Guest Appearance on Byte Into It

Last week on Melbourne\’s 3RRR, Georgia Webster, Andrew Fish, Keren Flavell and myself discussed Windows 7, TinyXP, iPhone security, defamation, video games, the seeming success of the Australian \’net filtering trial and my experiences at the Open Video Conference.

It was a really fun experience and reminded me how much I miss radio. There\’s something very liberating about such a simple live medium, and you don\’t have to worry about whether you\’ve got food on your shirt. Radio is kind of a nerd\’s dream, because it\’s closer to “pure thought” than all the live visual media.

You can download the MP3 here.

Brix Rules

Ok, so I\’ve been playing with Brix, a CMS toolkit from the creators of Apache Wicket that uses Apache Jackrabbit as its content repository.

And it rules.

Jackrabbit (a JCR implementation, i.e. a hierarchical repository for all sorts of content) is maturing fast, with 1.5.0 hot off the presses. It\’s performance is really quite good in my simple tests (most page loads creating just one highly optimised database hit).

Wicket is also about to take a great leap, thanks to 1.4\’s generics helping everyone keep their code type-safe.

Brix\’s own architecture is fascinating. It currently has a very simple GUI, but don\’t let that fool you – there\’s a lot of hidden power underneath, and the code is very clean and well-factored (if under-commented in parts…)

The combo is a winner, so I\’m actually switching the bulk of my own CMS over to Brix as of today. Lots of work, but I get the feeling it\’ll all be worthwhile.

Sending HTML Email with Wicket part II: Converting links

In my previous post, I showed how you can use Wicket\’s HTML rendering engine to render HTML emails by faking a request/response cycle.

In this post, I\’ll show you how to use an IVisitor to change image and anchor URLs to be absolute instead of relative. This is absolutely essential in order to make your HTML email work – otherwise all your images can\’t be found, and your links point to your own mail server.

The trick is to use Wicket\’s IVisitor to add a TransformerBehaviour to all the Images and Links that uses a regex to transform the URL after render but before the page is returned.

The code for the IVisitor is below:

private final class RelativeToAbsoluteUrlVisitor implements IVisitor {
        private final String requestPath;
        private Pattern urlPattern;
        private Class<? extends Component> componentClass;

        private RelativeToAbsoluteUrlVisitor(String requestPath, Class<? extends Component> componentClass, String attributeName) {
            this.requestPath = requestPath;
            this.componentClass = componentClass;
            urlPattern = Pattern.compile(attributeName+"="(.*?)"");
        }

        public Object component(Component component) {
            //if this component is of the specified class, update the URL attribute to be absolute instead of relative
            if(componentClass.isInstance(component)) {
                component.add(new AbstractTransformerBehavior() {

                    @Override
                    public CharSequence transform(Component component,
                            CharSequence output) throws Exception {
                        log.warn("Transforming component output: "+output);

                        Matcher m = urlPattern.matcher(output);

                        if(m.find()){
                            String attributeValue = m.group(1);
                            int start = m.start(1);
                            int end = m.end(1);

                            //convert relative to absolute URL
                            String absolutePath = RequestUtils.toAbsolutePath(requestPath, attributeValue);

                            log.warn("Got absolute path \'"+absolutePath+"\' from relative path \'"+attributeValue+"\'");

                            //construct a new string with the absolute URL
                            String strOutput = String.valueOf(output);
                            String finalOutput = strOutput.substring(0, start)+absolutePath+strOutput.substring(end);

                            log.warn("Returning updated component: \'"+finalOutput+"\'");

                            return finalOutput;
                        }

                        return output;
                    }});
            }
            return IVisitor.CONTINUE_TRAVERSAL;
        }
    }

Then we override the onBeforeRender() routine to traverse the component hierarchy and add this behaviour to the appropriate elements. Note that I haven\’t shown how you get the current absolute request URL, as in my system this is proprietary. There\’s plenty of example code floating around on how to do that, anyway.

    protected void onBeforeRender() {
        super.onBeforeRender();

        final String requestPath = MyCustomWebRequestCycle.get().getCurrentUrlAsString();

        IVisitor imageVisitor = new RelativeToAbsoluteUrlVisitor(requestPath, Image.class, "src");
        IVisitor anchorVisitor = new RelativeToAbsoluteUrlVisitor(requestPath, Link.class, "href");

        visitChildren(Image.class, imageVisitor);
        visitChildren(Link.class, anchorVisitor);
    }

So there you have it! All the bits and pieces to create HTML email with Wicket. There\’s one more catch though: You have to generate these emails in the same process as the Wicket Application. Calling Application.get() outside of the main process results in an error. In my system, I get around this by generating the HTML email source every time the user saves my Newsletter bean, which means that when it\’s finally sent (in the background), it just sends the pre-generated HTML. Easy!


	

Render a Wicket page to a string for HTML email

Something that\’s very desirable to do in Apache Wicket is create HTML emails using Wicket\’s brilliant component-oriented markup.

I\’ve been working on this problem on and off for ages — it\’s tricky because of teh way that markup rendering is so deeply tied to the requestcycle, which in turn is deeply dependent on the httpservletrequest — with good reason, too. That\’s where Wicket gets its autoconfiguring magic from!

So in order to use Wicket to create HTML emails, we need to fake the request/response cycle. I wrote this convenient method that renders a bookmarkable page (pageclass + pageparameters) to a string:

protected String renderPage(Class<? extends Page> pageClass, PageParameters pageParameters) {

        //get the servlet context
        WebApplication application = (WebApplication) WebApplication.get();

        ServletContext context = application.getServletContext();

        //fake a request/response cycle
        MockHttpSession servletSession = new MockHttpSession(context);
        servletSession.setTemporary(true);

        MockHttpServletRequest servletRequest = new MockHttpServletRequest(
                application, servletSession, context);
        MockHttpServletResponse servletResponse = new MockHttpServletResponse(
                servletRequest);

        //initialize request and response
        servletRequest.initialize();
        servletResponse.initialize();

        WebRequest webRequest = new WebRequest(servletRequest);

        BufferedWebResponse webResponse = new BufferedWebResponse(servletResponse);
        webResponse.setAjax(true);

        WebRequestCycle requestCycle = new WebRequestCycle(
                application, webRequest, webResponse);

        requestCycle.setRequestTarget(new BookmarkablePageRequestTarget(pageClass, pageParameters));

        try {
            requestCycle.request();

            log.warn("Response after request: "+webResponse.toString());

            if (requestCycle.wasHandled() == false) {
                requestCycle.setRequestTarget(new WebErrorCodeResponseTarget(
                        HttpServletResponse.SC_NOT_FOUND));
            }
            requestCycle.detach();

        } finally {
            requestCycle.getResponse().close();
        }

        return webResponse.toString();
    }

One other thing that\’s desirable to do is change all relative links in the email to absolute URLs — something that Wicket makes super-easy, if you know how. That will be the subject of my next post.

Free SMS service notifications using Google Calendar

Today I had a small revelation.

I was wracking my brains trying to figure out the SMS messaging provider to use to send myself service outage notifications for my clients\’ web sites. Given that I have just a handful of clients so far, it makes no sense to use a provider that requires a minimum monthly or yearly spend.

Ideally of course, I\’d like to spend nothing at all, and in exasperation I finally threw my hands in the air (they\’re detachable) and whined: “Google sends SMS\’s for free – why is it so hard for everyone else?”

(answer: not everyone has billions of dollars)

And then came the revelation: Why not create a command-line tool that uses Google\’s Calendar API to create events 6 minutes in the future that have an SMS notification set for 5 minutes prior to launch? That way, within a minute you get a notification sent to your phone for free within 1 minute. Sweet!

So, here\’s the code (it\’s in Java… sorry)

/**
* Simple command-line notification command that uses Google Calendar ATOM API to create
* a single event 6 minutes in the future with a 5 minute SMS reminder
*
* @author Daniel Walmsley
*
*/

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.List;

import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.CalendarEventEntry;
import com.google.gdata.data.calendar.CalendarFeed;
import com.google.gdata.data.extensions.Reminder;
import com.google.gdata.data.extensions.When;
import com.google.gdata.data.extensions.Reminder.Method;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;

/**
* This is a test template
*/

public class GCalNotifier {

public static void main(String[] args) {

    /**
     * Command line args: 
     * 
     * username
     * password
     * calendar name (e.g. "Notifications")
     * TimeZone offset (in hours)
     * event start offset (in minutes)
     * event end offset (in minutes)
     * title
     * description
     */

    try {

        // Create a new Calendar service
        CalendarService myService = new CalendarService("GCal Event Notifier");
        myService.setUserCredentials(args[0], args[1]);

        String calendarName = args[2];
        Long tzOffset = new Double(Double.parseDouble(args[3])).longValue() * 60 * 60 * 1000;
        Long startOffset = new Integer(Integer.parseInt(args[4])).longValue() * 60 * 1000;
        Long endOffset = new Integer(Integer.parseInt(args[5])).longValue() * 60 * 1000;
        String title = args[6];
        String description = args[7];

        // Get a list of all entries
        URL metafeedUrl = new URL(
                "http://www.google.com/calendar/feeds/default/allcalendars/full");
        System.out.println("Getting Calendar entries...n");
        CalendarFeed resultFeed = myService.getFeed(metafeedUrl,
                CalendarFeed.class);
        List<calendarentry> entries = resultFeed.getEntries();
        for (int i = 0; i < entries.size(); i++) {
            CalendarEntry entry = entries.get(i);
            String currCalendarName = entry.getTitle().getPlainText();
            System.out.println("t" + currCalendarName);

            if (currCalendarName.equals(calendarName)) {
                sendDowntimeAlert(myService, entry,
                        title, description, startOffset, endOffset, tzOffset);
            }
        }
        System.out.println("nTotal Entries: " + entries.size());

    } catch (AuthenticationException e) {
        e.printStackTrace();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (ServiceException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private static void sendDowntimeAlert(CalendarService myService,
        CalendarEntry entry, String title, String description, Long startOffset, Long endOffset, Long tzOffset) throws IOException,
        ServiceException {

    String postUrlString = entry.getLink("alternate", "application/atom+xml").getHref();

    URL postUrl = new URL(postUrlString);//was: "http://www.google.com/calendar/feeds/jo@gmail.com/private/full"

    CalendarEventEntry myEntry = new CalendarEventEntry();

    myEntry.setTitle(new PlainTextConstruct(title));
    myEntry.setContent(new PlainTextConstruct(description));

    Date now = new Date();

    Date startDate = new Date(now.getTime()+startOffset);
    Date endDate = new Date(now.getTime()+endOffset);

    DateTime startTime = new DateTime(startDate.getTime()+tzOffset);

    DateTime endTime = new DateTime(endDate.getTime()+tzOffset);

    When eventTimes = new When();
    eventTimes.setStartTime(startTime);
    eventTimes.setEndTime(endTime);
    myEntry.addTime(eventTimes);

    // Send the request and receive the response:
    CalendarEventEntry insertedEntry = myService.insert(postUrl, myEntry);
    System.err.println("Got response for: "+insertedEntry.getTitle().getPlainText());
    for(When when : insertedEntry.getTimes()) {
        System.err.println("When: "+when.getStartTime()+" to "+when.getEndTime());
    }

    //5 minute reminder
    Reminder reminder = new Reminder();
    reminder.setMinutes(5);
    reminder.setMethod(Method.SMS);
    insertedEntry.getReminder().add(reminder);
    insertedEntry.update();
}

}

Don\’t forget, you\’ll need to download the Google Data APIs and put their JARs in your classpath before this will work!

Personally I use this with Nagios. I always use the same args for the calendar offsets, so I\’ve encapsulated most of my settings (except title and body) in a script.

!/bin/sh

export SCRIPTDIR=/opt/calAlert
export USERNAME=username@gmail.com
export PW=mySecurePassword
export CAL=Notifications
export TZOFFSET=10
export STARTOFFSET=7
export ENDOFFSET=12
export TITLE=$1
export BODY=$2

export CURRDIR=pwd

export CLASSPATH=”${SCRIPTDIR}/calAlert.jar”

assumes GData libs are in “libs” subdirectory of SCRIPTDIR

for jarfile in $(ls “${SCRIPTDIR}/lib”)
do
CLASSPATH=”${CLASSPATH}:${SCRIPTDIR}/lib/${jarfile}”
echo lib/${jarfile}
done

echo “CLASSPATH=${CLASSPATH}”

export CLASSPATH

java GCalNotifier ${USERNAME} ${PW} ${CAL} ${TZOFFSET} ${STARTOFFSET} ${ENDOFFSET} “${TITLE}” “${BODY}”

Confused Narcissus

Ok, so a while ago I set up a Google Alert to inform me of happenings related to myself, and periodically it tells me someone on the web called “Dan Walmsley” (typically an Irish F1 Engineer, but occasionally me) has been written about.

Today, however, the results were far more disturbing.

Dan twinned the Steward way in back circuitously 2002 for example a musician in re the non-philharmonic restlessness. “Ace did exclamation in order to the administration ”˜oh, ourselves sense, Themselves do up have it gently too’,” me recalled as to his untimely days. “Nonetheless top brass didn’t factually accredit oneself.”

First of all, I think I\’ve been misquoted.

Second of all, who does this article benefit?

Thirdly, the post categories are: “african american, audism, feel good tips, republicans, yasin”. Why?

Fourthly, it\’s clearly a translation of this article. But a translation into what? Half-English gibberish?

Welcome, ladies and gentlemen, to hard evidence that synonym-generation does not lead to 100% readability during summary generation. This reminds me of my lost year at NICTA.

Wicket, Tomcat, Debian: Sometimes a little security goes way too far

Was up until 3am last night banging my head against another frustrating go-nowhere issue deploying Wicket on Debian Etch\’s default Tomcat5.5.

Apparently the latest version (5.5.20-2etch1) has additional security headaches features which prevent wicket from functioning properly out-of-the-box:

  • First of all, there\’s still an (as-yet-unsolved) mystery around why I couldn\’t get Wicket to start up as a filter. Just the mysterious “ERROR: filterStart” which makes me want to feed Tomcat to angry lions. Worked around it by using Wicket in Servlet mode instead.
  • Tomcat\’s juli.jar can\’t access WEB-INF/classes/logging.properties. Fixed (in sledgehammer-like way) by adding “permission java.security.AllPermission;” to /etc/tomcat5.5/policy.d/03catalina.policy, in the Juli section.
  • Tomcat security prevents webapps from accessing all sorts of features and methods by default, including wicket.properties, methods inside shipped jars, etc. Not being a Tomcat expert, and trusting the innate security of the server and millions of lines of third party code (i.e. I\’m an idiot) I again just popped a java.security.AllPermission; in appropriate spots in /etc/tomcat5.5/policy.d/04webapps.policy. Let the flames commence!

If Tomcat was a little more helpful in its error messages, this would never have been so painful. Jetty has always run my Wicket apps without complaint (though I\’ve never tried the official Debian Jetty packages – maybe they\’re crippleware secure too?).

The only reason I use Tomcat at all is the remote management and deployment features, which are well-supported by Cargo. Now that these issues are out of the way (mostly) I can take another few steps towards my dream of a seamless, fire-and-forget, auto-deploying, smoke-tested, pluggable and modular web app deployment system.

Oh, and have I mentioned recently how much I LOVE IInitializer?

Bless you, Wicket. Bless you.