avatarharuki zaemon

Crossing the road

By

When it comes to crossing the road, I’m just about as daredevil as the next person. I cross against the lights. I J-walk. You name it. Except when I see young children.

I approach an intersection looking both ways for oncoming traffic, about to cross against the lights when, out of the corner of my eye, I realise there are young children standing patiently for the lights to change.

It’s as if someone has erected an invisible wall. I stop dead in my tracks. I’m very thingy about it. I figure I’m old enough and ugly enough to know what I’m getting into but I don’t want to give these young kids a bad example. They’re not really old enough or experienced enough to know what’s safe or dangerous nor the neccessary motor skills even if they did. It’s almost like I feel a duty of care to towards them. I think as software developers we have similar resposibilities.

I consider myself to be a developer of average competence. I’ve been around the block a few times so I’ve made countless mistakes and, hopefully, learned from them. I’m pretty anal when it comes to certain things (as you’ve no doubt realised by now). But when I’m doing my own mini projects, sometimes I relax a little. Maybe I write some code that doesn’t have tests (howls from the crowd!). Maybe I hard-code a bunch of stuff I wouldn’t otherwise. The thing is I feel I’ve done enought to know when I can get away with it and when it’s just too darn important to overlook.

Sometimes mentoring and coaching can be like crossing the road. If I were by myself, I’d be quite happy to weave between the cars. But when there are kids around, maybe it’s better to sacrifice some speed for a little more safety?

As my good friend Damian Guy pointed out to me “that is why I like pairing. It is too easy to slacken off otherwise.”

When is a clock not a clock?

By

When it’s a mock.

It’s a common problem. You need to test behaviour of an application based on the system clock. Say some event that is triggered every n minutes, or at a certain time, etc.

Allow me to go nuts with some code examples for a change :-)

The most common solution to the problem is to do something like this:

public class Task implements Runnable {
    private final long _alarmTimeMillis;

    public Task(long alarmTimeMillis) {
        _alarmTimeMillis = alarmTimeMillis;
    }

    public void run() {
        waitForAlarmTime();
        doSomething();
    }

    private void waitForAlarmTime() {
        while (System.currentTimeMillis() < _alarmTimeMillis) {
            try {
                Thread.sleep(_alarmTimeMillis - System.currentTimeMillis());
            } catch (InterruptedException e) {
                // Ignore it
            }
        }
    }

    private void doSomething() {...}
}

This raises two questions in my mind: Firstly, should the action taken by the task really depend this closely on the scheduling? And secondly, how are we going to test it?

Let’s deal with the easy problem first. Let’s create a class called say, Alarm that looks something like this:

public class Alarm implements Runnable {
    private final Runnable _task;
    private final long _alarmTimeMillis;

    public Alarm(Runnable task, long alarmTimeMillis) {
        if (task == null) {
            throw new IllegalArgumentException("task can't be null");
        }
        _task = task;
        _alarmTimeMillis = alarmTimeMillis;
    }

    public void run() {
        waitForAlarmTime();
        _task.run();
    }

    private void waitForAlarmTime() {
        while (System.currentTimeMillis() < _alarmTimeMillis) {
            try {
                Thread.sleep(_alarmTimeMillis - System.currentTimeMillis());
            } catch (InterruptedException e) {
                // Ignore it
            }
        }
    }
}

Now this doesn’t look much different really except for one very important thing: We’ve decouple the scheduling from the action. Alarm now depends on a Runnable (an interface) to actually get the work done. That means Alarm is responsible for one thing only. Namely, the scheduling of a task. This allows us to independently test the schduling component and the task itself.

Because Runnable is an interface, we can easily code up a test that creates an implementation of Runnable purely for testing. Ie. a mock object. And because the tasks are now independent, we can test their behaviour irrespective of when they are likely to be scheduled.

So far so good. But how does this all relate to the original title? Well when we go to code up our AlarmTest we encounter some grief due to the fact that Alarm is coupled to System.currentTimeMillis(), a static method over which we have no control and also to Thread.sleep() another static method over which we have no control!

The solution however is relatively simple: Let’s help save the object by introducing our own abstraction of the system clock.

We’ll make an interface and call it, strangely enough, Clock:

public interface Clock {
    public long getCurrentTimeMillis();

    public void sleep(long millis) throws InterruptedException;
}

Now we can re-write our Alarm like this:

public class Alarm implements Runnable {
    private final Runnable _task;
    private final long _alarmTimeMillis;
    private final Clock _clock;
    public Alarm(Runnable task, long alarmTimeMillis, Clock clock) {
        if (task == null) {
            throw new IllegalArgumentException("task can't be null");
        }
        _task = task;
        _alarmTimeMillis = alarmTimeMillis;
        if (clock == null) {
            throw new IllegalArgumentException("clock can't be null");
        }_clock = clock;
    }

    public void run() {
        waitForAlarmTime();
        _task.run();
    }

    private void waitForAlarmTime() {
        while (_clock.getCurrentTimeMillis() < _alarmTimeMillis) {
            try {
                _clock.sleep(_alarmTimeMillis - _clock.getCurrentTimeMillis());
            } catch (InterruptedException e) {
                // Ignore it
            }
        }
    }
}

This allows us to implement a SystemClock that simply delegates to the original methods we called. But importantly, we can also implement our own MockClock that returns whatever values we like for getCurrentTimeMillis() and does whatever we like (most likely nothing) for sleep().

Not only does this make it easier to test, but it also means we don’t actually have to have our test wait for 3 hours just to see what happens. We can simply have our MockClock pretend to sleep for 3 hours.

We haven’t tried to make our own Thread or subvert the System class or clock in anyway. We’ve taken control and not allowed ourselves to be dictated to by someone elses API.

There’s still obviously some room for improvement here. We could make the Clock API possibly a little higher level, maybe? We could provide a ClockFactory if you don’t like the idea of passing it into the constructor (ala IoC). But you hopefully get the gist.

Of course had we done this test-first, we would likely have arrived at this solution in the first place. But it does serve to illustrate the point that, IMHO, testability necessitates good OO design.

My stuff is too complex to test

By

Boulderdash! Dealing with complexity is the essence of software engineering. I particularly liked the idea that software is fractal in nature as it is something I’ve always tried to communicate to others. The interactions between components in any layer (should) look the same as the interactions between layers. No matter what the level of abstraction. But I digress.

If you’ve managed to build a system that does the job but is too hard to test, then your system works by coincidence. Sure you may think you’ve deliberately implemented a solution in a particular way but the fact that it works is more dependent on probability than pure determinism.

If it seems too complex, first break the problem down into smaller, more manageable, chunks. Start solving the smaller problems. Then start re-factoring and abstracting based on common code, common solutions, etc. In this way, large systems become a complex arrangement of simple things.

Software that is designed well is testable. The corollary to this is that software that is not testable is designed poorly. A focus on testability results in software that is well designed.

Can you guess what the next three letter acronym is likely to be?

If you guessed TDD, you guessed right!

Help save the object

By

I have an innate dislike of the so called static helper class.

If you’ve not seen one of these, you are very lucky indeed! It’s a class that contains entirely static methods that usually perform convenience functions such as data conversions, formatting, resource management, you name it.

If you’ve come from a C or VB background, it’s probably a natural way to bring back that warm fuzzy feeling to the otherwise scary world of OO.

The problem is, static classes are difficult to decorate (yes, again, I know we now have AOP frameworks) and importantly, they’re not pluggable (as in Strategy) which means you can’t mock them out for testing!

It’s not so much of a problem if the behaviour is trivial such as performing a simple calculation. But when it involves reading from a database, putting messages on queues, etc. the situation becomes much worse. You end up setting up, configuraing and ultimately testing a lot more than you had anticipated.

Quite often, the behaviour would be more appropriately placed in the class on which the processing is being performed instead of exposing the internals of the class just to perform a caclulation. If you do need to expose some of the internals, trun it around and use a Visitor. At the very least, turn the class into a Singleton and possibly even use a factory method of some kind. The java libraries have very few static classes. When they do (such as java.util.Collections) they’re almost always some kind of factory.

So next time you’re building a system, spare a few lines of code and help save the object. Better still, start practicing test-driven development and see how few of these things you end up with :-)

P.S. I’ve been accused of using too many acronyms before. This time I’m sure to be accused of using too many patterns. That’s what patterns are for: communication. It saves me having to write a whole lot of code to explain what I mean. So get over it! :-)

Definition of insanity

By

Ever noticed how some organisations persist with processes/people/tools that, on reflection, fail every time yet continue to justify their use with the promise of success “the next time”?

Is it too much to ask for competent people? If they aren’t, we need to find new ones. If they lack experience, we need to educate them. Process+Monkeys is NOT the answer. Sure process is important but allow process improvement to flow upwards from the experience of developers, not downwards from the ideals of people far removed from the actual day-to-day work.

Capability is developed through real projects. Abstract frameworks and overpriced development tools are not the solution. And I don’t know about you but it seems to me that more often than not, simply sending developers on courses can often be like paying someone to take time off to read a manual!

Maybe they believe that people don’t matter? That process and tools will ultimately replace people and, therefore, we should simply employ substandard people and continue to improve the process and tools until they achieve this goal?

Better the devil you know?

Certainly it’s been my experience that large consulting firms and software vendors draw a (very) fine line between doing what’s best for the customer, and commercial imperatives such as expanding the account and selling more ‘wares. Thankfully, I’ve never worked for a company that acted in this way but I am unfortunate enough to have been forced to work with them.

I’ve wondered recently if, in some perverse way, large organisations are ethically and morally obliged to employ substandard people. That way they can continue to make outrageous profits. I mean, imagine if they were actually efficient at doing business. Imagine how much money they’d make then. Hehehe.

All companies I’ve been doing work for recently definitely want to change. I just wonder how much inertia there is to overcome, or if it’s even possible.

Hopefully, they’ll realise they can’t continue to repeat the same behaviour and somehow expect different results.

It's not a democracy

By

“Smart people don’t always make smart decisions.” – anonymous

Ahh yes. The code review…

Why do some developers feel that adhereing to (largely) well accepted design practices and coding standards (anyones just have them!) somehow takes away their freedom of expression when they’re quite happy to unwittingly limit themselves by way of poor design choices and down right rotten coding practices?

Do they feel they work in a commune or democracy? It’s not. Shared ownership I have no problem with. But shared responsibility? Yeah right.

Set your standards, enforce them and be done with it I say. You wanna debate about it, sure, now lets stay back and re-factor the code so that it now adheres to the new standards! I’ve done it before. It’s a lot of work. But you gotta admit the code was easy to extend and maintain.

Use Checkstyle, PMD, or similar tool that is extensible enough to add your own. These tools dont just check line lenghts anymore. They can be used for enforcing design patterns, looking for poor ot at least suspect coding practices. Almost anything really.

And make sure you have a code cop with Balls!

Aren't classes supposed to have both data and behaviour?

By

A bit late off the mark but I only just found an old article on why getters and setters are evil. Combine this with my preference for immutable objects, and JavaBeans often become the devils work. It also fits in nicely with another article I read recently, Martin Fowlers AnemicDomainModel, in that both talk about the value of spending the time to put behaviour back on classes rather than having dumb data objects with essentially procedural functions (in the way I usually see entity and session beans used).

It’s a strikingly difficult concept to grasp. I know I struggled with it for years. And I’ve found it even harder to convince others of since I did get it.

Here’s a typical example I see quite often. Now this is only my opinion of course but instead of:

account.setStatus(AccountStatus.CLOSED);

I’d prefer to see:

account.close();

Internally, the Account might simply look like:

public void close() {
    setStatus(AccountStatus.CLOSED);
}

But it might also do something more. In this trivial example, the benefits are not so obvious but, when the logic becomes more complex, the setStatus() method can become quite large and usually ends up with a switch statement that (hopefully) delegates to separate methods anyway. So why not just call the methods explicitly. After all, the AccountStatus class is really an implementation detail, albeit a common one.

Having said this, deciding where behaviour belongs is often quite difficult. I often find myself using Strategies and Composites instead of inheritence but I’ve also found this a difficult concept to communicate, with doubters usually exclaiming “But aren’t classes supposed to have both data and behaviour?” :-)

Ahh crufty code

By

Inspired by this blog entry I thought it was time to start blogging some of the crufty code that I come across from time to time and find highly amusing. Now having said that, let me say this: I’ve written a bunch of crufty code in my time (if we lived by my good friend Jon Eaves rules of life, I’d have been removed from the gene pool long ago). I’d say probably every 12 months or so I look back on stuff I thought was great at the time and whince. But I have to admit that some of these are just way too funny not to air. Feel free to email me or comment with your own. So here goes…

These are just plain broken and wrong:

String s = new String();
String s = new String("");
String s = new String().valueOf(someInt);
try {...} catch (RuntimeException e) {
  e.printStackTrace();
}
try {...} catch (Exception e) {
  if (e instanceof SomeException) {
    ...
  } else if (e instanceof AnotherException) {
    ...
  } else {
    throw e;
  }
}
if (aBoolean == true) {
  return true;
} else {
  return false;
}

Next, I can only assume the developers thought that finally was only run after a catch?

try {
  return someMethod();
} catch (...) {
  ...
} finally {
  return null;
}

Some lesser evils…

Instead of this:

Object[] objects = someCollection.toArray();
for (int i = 0; i < objects.length; i++) {
  ...
}

try:

for (Iterator iter = someCollection.iterator(); iter.hasNext(); ) {
  Object o = iter.next();
  ...
}

This is pretty anal but instead of this:

return new Boolean(aBoolean);

use either of these two:

return Boolean.valueOf(aBoolean);
return aBoolean ? Boolean.TRUE : Boolean.FALSE;

Adapter classes

By

Since I ranted about abstract classes a couple of days ago I’ve read quite a number of other blogs that discuss interfaces versus abstract classes. Most of the discussion seems to be around the fact that adding a method to an interface is a pain because you need to add that method to any class that implements the interface. The solution in some cases (but definitely not all) is to have an “adapter” class that provides empty implementations of all methods. You can then extend this and override only the methods you need. For example:

public interface Foo {
    public void somethingHappened();
}

public class FooAdapter implements Foo {
    public void somethingHappened() { ... }
}

This can make a lot of sense if you’re implementing some kind of listener/observer. But if the interface is required to return something as in:

public interface Foo {
    public int getSomething();
}

then what do we do? Return 0 for integers, null or "" for strings or throw UnsupportedOperationException?

Interestingly, if you don’t recompile the implementing class and something attempts to call a method on the interface that you haven’t implemented, the JVM will throw a NoSuchMethodError.

The one place where it might bite significantly is if/when you change a public interface in a public API. Once that API is published and used by third-parties, it becomes much harder to change for fear of breaking backwards-compatibility.

At any rate, these “adapter” classes aren’t really abstract classes nor are they types. They are once again a convenience of the programming language. Ruby and smalltalk for example will, like the JVM, throw an exception if you attempt to call an unimplemented method on a class. The difference being that Java’s strong typing forces you to deal with it up front.

So, putting pandora firmly back in her box, I don’t see that any of this really goes against my original proposition that abstract classes are not in fact types.

Don't panic, just cluster

By

I encountered a rather (un)amusing situation just recently. A project I was casually checking in on (just because I’m a nosey parker) had a unit test suite take 70 mins to run? “How many tests are there?” I ask. A couple of thousand I thought to myself? What if I told you 200!

Ok, so now that you’ve picked yourself up off the floor. You’re first thought would most likely be similar to mine: Aha! They’re hitting the database in their unit tests. Well how about this, they were hitting the database not just in every test, but setUp() and tearDown() are rebuilding the database by loading a schema and data from XML and re-populating it.

But not to fear, they’re on to it! They’re doing some work to speed up the tests. They’ve figured that if they create some extra database users they can run the tests in parallel!

Reminded me of another project I saw some time ago. The (web) application services around 2000 users concurrently. The application craps out so often that they decided to have a dozen or more servers all regularly saving session state to a database (read SLOOOOOW) so that the users would experience no perceptable loss of service.

So what do these have in common? In both cases, instead of looking at the design of the application. Instead of cleaning up the code and making it more reliable. Instead of reducing dependencies between layers. The answer was to cluster. I mean, why build better software when you can just throw more hardware and software licenses at the problem….KACHING$$$$$.

In the case of the testing, had it not ocurred to anyone not to hit the database at all? How can a unit test hit a database? Wouldn’t that classify, by definition, as an integration test?

As for the web app, it seems to me that had the code base been even remotely stable, the claims made by the application server even remotely accurate, add in a UPS and some RAID and the application would have been more stable than the average users internet connection.

Now don’t get me wrong, like all things there are times when you want to service 1000s of customer concurrently and you may well require lots of hardware. But the overriding factor must be to get the application right first. Then think about clustering. The old saying goes “A chain is only as stong as its weakest link.” If the application is broken in the first place, you will get ever diminishing returns my simply adding more and more servers.

It seems to me that in most cases, it’s rare to see a business application that warrants true (real-time session failover) clustering rather than just a simple server farm. I agree that a cluster and/or a server farm gives you the ability to to take a machine out for servicing, handle hardware failures, and to scale an application to handle more and more concurrent users. But it should not be used as a quick fix for poor software development.

Endulge me as I make a tenuous analogy with my martial arts training. Most martial arts teach that it’s not about strength. It’s about technique. Get your technique right first without resorting to brute force. Then when you can make that work, add in your natural strength and you will seriously kick butt.

Servlets are for integration

By

It seems to me that most people I work with are fixated on the idea that servlets and JSPs are for presentation, session beans for business logic, message driven beans for integration, JNDI for service lookup, etc. I tend to have a different perspective on this.

JSPs are a templating language where one possible use is the creation of HTML which is likely (though not guranteed) to be used for presentation. But JSPs may also be used for generating XML again possibly for presentation via XSLT or maybe for SOAP or even CSV, etc.

JNDI may well be used for service lookup in most J2EE applications but JNDI is really an interface to heirarchical databases allow us to query LDAP, NIS, DNS, File Systems, etc. Nothing new here for some people but I bet if you did a poll at work you’d find that most J2EE developers didn’t know that!

Message driven beans, well theres no doubting that these are for integration. Just make sure you don’t code any business logic directly into them please! Aha you say, of course not that’s what session beans are for right? Not in my opinion, no.

Session beans and Servlets are also for integration! Now I know thats not what Application Server vendors such as IBM, Sun, BEA, etc. would like you to believe but IMHO that’s all they are.

Session beans provide you with a mechanism for remoting services via RMI, IIOP, Corba, etc. Servlets provide you with a mechanism for remoting services via HTTP (think SOAPServlet) and Message Driven Beans provide you with a mecahnsim for remoting services via JMS albeit an asynchronous-only service.

Again, IMHO, it’s all about abstraction. In my mind, these technologies are all different solutions to the problem of distributing services.

If you code your business logic into POJOs then you have the flexibility to distribe your services to RMI clients via Session Beans, web browsers via servlets, SOAP clients via your choice of session beans, servlets and message driven beans. You can even use JavaMail (POP and SMTP) as transports for SOAP messages. For that matter you can serialize java objects and send them via HTTP if you wanted a simple binary protocol for java clients to communicate with an application server through firewalls.

Now try telling that to your Sun-certified J2EE architect :-)

Best of all you can now fully unit test your code, out of container, which means there’s no excuse for not doing TDD.

HTTPUnit is NOT!

By

I hope that I’m not the only one that sees HTTPUnit as a misnomer. Yes it may well easily plug into the JUnit framework but by definition, any tests involving HTTP and Servlets/JSPs are not unit tests. They are more likely functional tests, at best integration tests.

As my good friend James Ross points out, HTTPUnit is really a rich http client library. Infact I did some work for a client of mine some months ago to screen scrape web pages and after much searching, I decided that JWebUnit which is built on HTTPUnit was the best choice. The only draw back is it has some very trivial dependencies on junit classes.

I would have been happier if HTTPUnit had been called HTTPTest. For that matter HTTPClient as it doesn’t have any real dependencies on JUnit save a few convenience classes for in-servlet testing

But I digress. Why does this all bug me so much? Because HTTPUnit is named so, it’s easy for developers to convince their managers that they are writing unit tests when they’re not.

Don't mock infrastructure

By

While there are a few cases where I feel you may need to, for the most part I feel very strongly that mocking infrastructure is a fundamentally flawed approach to testing.

I have been down the path of mocking JNDI, JDBC, JMS, UserTransaction, etc. and whilst at the time I thought this was all very cool, I did wonder what the hell I was doing writing all this stuff.

Eventually I realised that the problem I was having was one of abstraction. Or more properly a lack thereof. I was tackling the wrong problem. Instead of thinking my class needs to lookup JNDI therefore I need to mock out JNDI, I started to think about the problem in general. That is, a mechanism for looking up services.

A perfect example is Using mock naming contexts for testing. While I think the intention is great, I don’t believe it goes far enough.

JNDIs Context has a multitude of methods I’ll never use, not to mention the fact that they all throw NamingException which quite frankly my code has never known how to deal with and usually throws a ImGivingUpException.

What I really need is a ServiceLookup interface with a few very simple methods like, not surprisingly, lookup(Class). Then I can have two implementations, one a MockServiceLookup that I can create inside my JUnit test and return whatever I like and a JndiServiceLookup that actually understands how to go about performing all the nasty JNDI stuff.

Not convinced? Ok well now imagine I have more than one way to obtain a service. Sometimes it’s through JNDI and sometimes I instantiate a local object. If all my code is tied to a JndiLookup what will I do? Instead, now that my code is tied to an interface, I can create an implementation that say first looks in a local configuration and then if that fails, delegates to the JNDI version. Or maybe a SuperServiceLookup that holds a list of other ServiceLookup implementations and just delegates to them as appropriate. Again referencing only the interface and not any concrete implementation.

Now think JMS. Instead of writing to/reading from queues, why not have MessageQueue interface with two simple methods: read(Message) write(Message). then I can have a MockMessageQueue that I instantiate in my test and a JmsMessageQueue that talks to the real thing.

Again, we’ve abstracted the problem. That is, sending/receving messages. Not talking to JMS.

So now, back to the original example, I can see that a MockInitialJndiContext may well be useful but probably only for one thing: testing my JndiServiceLookup. And then I can probably just have a constructor in JndiServiceLookup that accepts an Context and a default constructor that creates an InitialContext. Then use something like Easy Mock to fill in the rest for me.

When it comes down to it, InitialContext is really a convenience for calling InitialContextFactory anyway which in turn creates a Context which is after all an interface. So why be constrained by someone elses API?

My rule of thumb is that in general, I want to be mocking out my own interfaces not someone elses. I’ll usually have many places where I need to mock out a given interface of my own and at most one place where I need to mock out someone elses.

Using Factory and AbstractFactory, etc. for creation will then allow you to use Strategy, Decorator, etc. for different behaviour ie. Mock vs JNDI, JMS, etc. Martin Fowlers Patterns of Enterprise Application Architecture also has some great ways for doing this for enterprise applications as well.

Using testability to drive my thinking forces me to abstract problems in a way I hadn’t previously and good design emerges. This is why I’m such a huge TDD bigot. Because as Dan North point out, TDD is not about testing.

Abstract classes are not types

By

I have a rule of thumb that says, in most cases, any class X that is abstract MUST be named AbstractX. Abstract static factories are one instance I can think of where I intentionally break this rule. But the checkstyle check I wrote to enforce this understands that abstract classes are allowed to end in the word Factory.

The next part of the rule says that no variables, fields, parameters, etc. may be of a type who’s name begins Abstract.

Now that I’ve adhered to this rule, I find I’m passing AbstractShapes and AbstractVehicles around which doesn’t really seem to make sense to me.

Why? Because IMHO abstract classes are not types. Abstract classes in Java are a convenience of the language that, for one thing, facilitate code sharing through inheritence. But inheritence is by no means the only, nor the best, way to share common code. We can use delegation (eg composition), decoration (see me in my new AOP colours), etc.

So what do I do instead? In C++ I would have used a pure-virtual class. In Java I use interfaces.

Have you ever seen code that declared a variable of type java.util.AbstractList? No? Why not? It’s there, along with HashMap and TreeSet, etc. Because AbstractList is not a type. List is. AbstractList provides a convenient base from which to implement custom Lists. But I still have the option of implementing my own from scratch if I so choose. Maybe because I need some behaviour for my list such as lazy loading or dynamic expansion, etc. that wouldn’t be satisfied by the default implementation.

Classes are a combination of data and behaviour. Common practice is to hide data in such a way that access to the data is encapsulated within methods. This makes good sense for a number of reasons including the fact that maybe the particular piece of data is actually a calculated field and not a stored value. Just because I choose to implement the age of a Person as a stored value, shouldn’t mean that every Mammal must be implement in this way. What if I wanted to calculate the age of a Dog based on date of birth and the notion that 1 dog year is approximately 7 human years. That’s easy you say, make Mammal an abstract class and the getAge() method abstract. Well yes that would work but now lets extend that reasoning out to other attributes and behaviour of Mammals and soon we end up with a pure-abstract class. In other words an interface.

Attemping to use abstract classes as types also makes it difficult to implement decorators (yes I know we have AOP frameworks), especially if we accept the idea that methods not intended to be overidden should be declared final!

All of this leads inexorably (gotta love the wakowski brothers for bringing this word back into the english language) to the conclusion that abstract classes are not types. They are someones idea of the way in which a convenient base class implementation might behave.

IMHO interfaces are good. TDD almost always leads to the definition of more interfaces. Use them and leave abstract classes to reduce duplication of common code among related imnplementations of a given type.

Novel use of Hibernate?

By

I’ve been working on and off on a project that uses Hibernate persitent classes in Rhino Javascripts.

For various reasons, the database tables (and therefore the persistent classes) aren’t known until runtime at which point the meta-data is read from a database.

I used the ASM byte-code library to dynamically generate classes at runtime, map them using Hibernate and expose them with some "fancy" wrappers in Rhino. So anytime they update the database schema, all they need to do is either restart the server or send it a "re-load signal".

All in all a very simple solution that was very easy to get up and running.

Interestingly, the fact that hibernate doesn’t require code generation was my saving grace. I’m not suggesting thate code generation is bad but in this particular case it ouldn’t have worked as well, IMHO.

I’m now working on doing some file-import using the same “framework”. Basically a legacy system spits out EDI-ish data with record types, etc. that needs to be imported. The database meta-data contains a mapping between record types and layout to tables and columns espectively.Again, this will no doubt involve some dynamic class generation to perform the import? But maybe not. I’ll start with some tests and see what happens ;-) Gotta love TDD.

FIRST POST!

By

Ok…So this was automatically generated! I didn’t have anything to say then for my first post and I still don’t :-)

Site44.com deploy task for Octopress

By

Further to my previous post, I also added a rake task to my Rakefile that uses rsync to deploy:

rsync_delete   = false
deploy_default = "local"

# snip

desc "Deploy website via local rsync"
task :local do
  exclude = ""
  if File.exists?('./rsync-exclude')
    exclude = "--exclude-from '#{File.expand_path('./rsync-exclude')}'"
  end
  puts "## Deploying website via local rsync"
  ok_failed system("rsync -avz #{exclude} #{"--delete" unless rsync_delete == false} #{public_dir}/ #{deploy_dir}")
end

I initally thought it would be pretty neat to work out the local website location in Dropbox but in the end I decided it was simpler to just leave the deploy_dir variable set to the default "_deploy" and have that directory symlinked to the approproprate Dropbox folder.

Simon Harris - Resume

By

Shelly Beach, Ballina, New South Wales, Australia.
Shelly Beach, Ballina, New South Wales, Australia.

haruki_zaemon@mac.com +61 417 505 611 harukizaemon.com

Overview

A product and people focused leader with over 30 years experience developing commercial and product-aligned technology strategy; managing and growing empowered product-mode teams; and architecting, building, and running systems.

I have hands-on experience from low-level programming to board membership, and the myriad things in between, and I’m not afraid to get my hands on some code when the moment calls.

I have a proven track record working as part of a team, scaling organisations to deliver effectively in competitive markets. I have studied Psychological Safety, and Clinical Psychology, as well as undertaken leadership coaching in order to improve my effectiveness as a leader.

Based on my experience and diverse skill set, I believe I am well-suited for senior leadership positions in technology or engineering departments, including:

  • Chief Technology Officer: Given my track record in technology strategy development, team management, and project delivery, I can provide strategic direction, oversee technology operations, and drive innovation within an organisation.
  • Head of Engineering: With my experience in managing large engineering teams, leading technical initiatives, and driving organisational change, I believe I would thrive being responsible for setting the engineering vision, driving technical excellence, and fostering a culture of innovation and collaboration.
  • Director of Product Development: Given my experience in product-aligned technology strategy and cross-functional collaboration, I am well suited to a role where I would oversee the end-to-end product development process. I would work closely with product managers, engineers, and other stakeholders to drive the successful delivery of products aligned with business goals.
  • VP of Technology: My experience in technology leadership, strategic planning, and team management makes me well-suited for a VP-level role in a technology-focused organisation. I could provide leadership in shaping the technology roadmap, driving technical excellence, and fostering a culture of innovation and continuous improvement.
  • Technology Consultant or Advisor: Given my broad range of technical expertise and experience across various industries, I would could also provide valuable insights and guidance to organisations seeking to optimise their technology strategy, improve team performance, or drive digital transformation initiatives.

About

I started my professional career as an apprentice assembler programmer in 1991, managed my first team in 1996, and have been involved with nearly every aspect of product development and delivery ever since.

I’m always looking for opportunities to grow, and have been fortunate to have worked in a variety of roles that challenged me in different ways:

  • As an agile coach and programming instructor I learnt how important it is to understand where people and systems are today in order to understand how to get them to where they need to be.
  • Doing technical pre-sales and on-site consulting taught me how to rapidly come up to speed in a business domain, articulate what good looks like, and manage stakeholder diversity.
  • Running product development and architecture from iPhone apps, bio-technology, real-time high-frequency trading, and US$100M ARR online marketplaces gave me an appreciation for the value of rapid feedback and the clear articulation of trade-offs in the decision-making process.
  • Hiring, managing, and leading teams showed me that the most effective way to scale is to trust, empathise with, and empower people by creating opportunities and supporting them to grow so they can apply the most effective skills, tools, context, and decision-making heuristics.

I speak Japanese and hold a 6th degree black belt in Aikido, having regularly travelled to Japan and trained as a live-in student since the age of 15.

Current Experience

2024-current: Head of Engineering, Clinical Solutions iCare World

2021-current: Non-Executive Board Member, Team Kids

Responsibilities
  • Participate in quarterly board meetings, and regular strategy and planning sessions.
  • Advise the Founder, CEO, and CFO on technology, commercial, and operations strategy.
  • Directly review, assess, and guide IT operations.
Achievements
  • Performed software, systems, architecture, and operations, as well as directly informing legal and regulatory compliance due diligence for the successful acquisition of Kids Unlimited.
  • Worked with the head of IT to develop a long-term technology strategy directly aligned with the long-term business strategy. This enabled the business to more effectively invest resources where they would generate the most value.

Publications and Appearances

2024: AU Evo Inspires #74

  • Appeared on the EVO AU podcast again, this time for a 5-min lightening Q&A.

2023: Evo AU #98 – Scaling A Development Team

  • Appeared on the EVO AU podcast along with 3 other guests to discuss challenges, approaches, mistakes, and lessons we’ve learnt scaling teams.

2005: Beginning Algorithms, Wrox Press

  • Co-authored with James Ross, Beginning Algorithms guides readers through the basics of algorithms and data structures, and examines specific algorithms that will help readers in programming tasks. In particular the examples in the book were almost entirely test-driven—an approach has drawn its fair share of criticism for being overly pedagogical.

Education and Training

2023: Participatory Leadership, Art of Hosting

  • Tools, techniques, and practices for scaling teams and organisations, enabling faster decision-making, and genuinely partnering to create systems change in our fast-paced, top-down world.

2022: Certified Practitioner, The Fearless Organization

  • The Fearless Organization Scan uses the body of work of Amy C. Edmondson to help organizations measure and improve psychological safety in their teams.

2022: Certificate, Motivational Interviewing - Foundational

  • Dr. William Miller, Dr. Stephen Rollnick & Dr. Theresa Moyers
  • Grade: 97/100
  • Motivational Interviewing (“MI”) is a way of conducting conversations about change to strengthen clients’ motivation and commitment. MI has been found to be useful in helping people change across a broad array of problems in counselling and psychotherapy, health care, coaching, social work, and education.

Affiliations and Membership

Prior Experience

2022-2024: Engineering Director, Envato

Responsibilities
  • Reporting to the CTO
  • Responsible for ~130 staff Asian Pacific Engineering team.
  • 9 direct reports: 3x Senior Engineering Managers, 2x Engineering Managers, 1x Senior Principal Engineer, 1x Senior Principal Engineer, 2x Lead Engineers.
  • Domains: supply (content and authors); demand (customer); satellites (traffic generation); data, data-science, and BI-analytics; infrastructure, and practice.
  • Work closely with the heads of analytics, design, marketing, and product to prioritise, plan, and deliver product and platform improvements.
  • Member of the cross-functional senior leadership, cross-functional product development leadership, and technology leadership teams.
  • People and team management including workforce planning, team restructuring, hiring, career development and growth, and performance management.
  • Systems operations, budgeting and cost management, architecture, risk, and security.
  • Roadmaps, planning, and delivery of product-focus projects, and keeping the lights on (“KTLO”) activities (e.g. BAU, systems upkeep, etc.)
  • Technical direction and strategy.
Achievements
  • Worked as part of the senior leadership team to design and implemented a major restructure of the business and ways of working towards product-led initiatives.
  • Initiated and led the development of a lightweight governance to drive the adoption of Machine Learning (“ML”) and Artificial Intelligence (“AI”) for both operations and product improvement.
  • Initiated and led the development of our Analytics and Data vision and strategy.
  • Worked with heads of product to develop a long-term product vision and strategy.
  • Worked as part of the technology leadership team to review and refresh our overall technology team strategy.
  • Initiated and drove a pilot near-shoring team, led by one of the Senior Engineering Managers as part of a strategy to expand our global workforce.
  • Introduced a lightweight approach to decision-making across senior leadership and engineering to improve speed and quality.
  • Envisioned, created, and staffed an Engineering Effectiveness (“EE”) team to improve the way Engineering operates as a function to the benefit of the business as a whole.
  • Initiated and led an “Engineering Uplift” initiative to create a culture of continuous improvement by bringing together the leaders of previously disparate engineering teams, growing their skills as leaders, and creating an engineering-wide program of work to uplift performance.
  • Initiated and led a review of and approach to evolve infrastructure and operations.
  • Initiated and led the re-platforming of our digital asset management platform to improve operational efficiency and effectiveness, and address the opportunity cost of the in-house solution. This included a business case, board approval, vendor due diligence, legal review, commercial vendor negotiations, and project governance.
  • Worked with the people team to explore options for near-shoring as part of a strategy towards a global presence, and scaling the workforce while managing the cost of operations.
  • Worked as part of the senior leadership team to prepare the organisation for outside investment, including technology and data due diligence process.

2021: Engineering Director, Envato

Responsibilities
  • Reporting to the CTO.
  • Responsible for ~60 staff Asia Pacific Engineering team.
  • 7 direct reports: 1x Senior Engineering Manager, 3x Engineering Managers, 1x Senior Architect, 1x Senior Principal Engineer, 1x Apprentice Mentor.
  • Domains: supply (content and authors); data, data science, and BI-analytics, infrastructure; architecture; practice; and apprentice program.
  • Member of the cross-functional senior leadership, cross-functional product development leadership, and technology leadership teams.
  • People and team management including workforce planning, team restructuring, hiring, career development and growth, and performance management.
  • Systems operations, budgeting and cost management, architecture, risk, and security.
  • Planning and delivery of strategic projects and keeping the lights on activities (e.g. BAU, systems upkeep, etc.)
  • Technical direction and strategy.
Achievements
  • Initiated and led the development and execution of a coherent data strategy to address the business needs of self-service performance and insights, data literacy, data as a product, and data governance.
  • Led the expansion of our apprentice program from 1 to 2 teams, including business case, board approval, and hiring an additional mentor. The apprentice program is open to women entering the technology workforce for the first time.
  • Led the development and rollout of the remainder of our career development framework, working with the people team to rebuild our position and job descriptions, build out our learning and development materials, and formalise a holistic approach to workforce planning.
  • Initiated and led the development of a 3-5 year technical and product strategy for one of our recent commercial acquisitions.
  • Developed and promoted a Senior Engineering Manager to whom I delegated most of my supply-side responsibilities. This gave her greater opportunity to grow and thrive, and me more time to focus on a broader set of responsibilities.

2020: Senior Engineering Manager, Content Group, Envato

Responsibilities
  • Reporting to the CTO.
  • Respomnsible for ~30 staff across 5 Asia Pacific Engineering teams.
  • 6 direct reports: 1x Senior Engineering Manager, 3x Engineering Managers, 1x Senior Architect.
  • Domains: supply (content and authors), architecture, and infrastructure, operations innovation.
  • Member of the content leadership and technology leadership teams.
  • People and team management including workforce planning, team restructuring, hiring, career development and growth, and performance management.
  • Systems operations, budgeting and cost management, architecture, risk, and security.
  • Planning and delivery of strategic projects and keeping the lights on activities (e.g. BAU, systems upkeep, etc.)
  • Technical direction and strategy.
Achievements
  • Introduced a lightweight approach to consistently and coherently articulating actionable technology strategy.
  • Initiated and led the development of a content group strategy and roadmap for evolving and maintaining the platform architecture to meet the needs of the business.
  • Initiated and led a restructure of content group product delivery teams aligned to business capabilities.
  • Initiated and led the development of a company-wide technology team strategy to meet the needs of the business.
  • Established an infrastructure team within the content group to ensure sustainability of operations.
  • Led the development of the technology team career map which we made public in 2021.
  • Led the development of a technology team performance and progression framework, including clear expectations, remuneration and assessment framework.

2019: Delivery Team Lead & Product Owner, Content Warehouse, Envato

Responsibilities
  • Reporting to the Senior Engineering Manager.
  • Manage a team of 6 direct reports.
  • Set direction and drive execution by organising team work, setting goals, and holding the team accountable.
  • Collaborate with stakeholders including customers and other product managers in the development and execution of the team’s roadmap.
  • Communicate strategy, rationale, and progress within the team and to external stakeholders.
Achievements
  • Performed the role whilst continuing to operate in my capacity as the group Lead Engineer role.
  • Worked with other the product managers and stakeholders from across the business to put together a roadmap.
  • Delivered a number of those initiatives to improve the customer experience by:
  • Automatically identified and removed duplicate and similar content from the shopfronts.
  • Enabled satellite and subsidiary businesses to access the centralised content library via an API.
  • Enabled satellite and subsidiary business to send their content to the centralised library via an API.
  • Reduced downtime by measuring and addressing system performance under load.
  • Reduced downtime and dependency between shopfronts by centralising content distribution.
  • Improved internal operations by centralising and simplifying the process of managing content publication and removal.
  • Reduced operating costs by reviewing and removing old, stale, and rejected content.
  • Improved search and discovery by providing shopfronts with curated content quality signals.
  • Established career development plans for 2x senior- and 3x mid-level engineers.

2018: Lead Engineer, Content Group, Envato

Responsibilities
  • Reporting to the Senior Engineering Manager.
  • Take ownership of and proactively improve the architecture of systems.
  • Work with teams and stakeholders to identify and solve tangible business problems and improve delivery capability.
  • Work with and drive teams to define what success looks like, set goals, define metrics, organise work, and track progress.
  • Monitor, assesses, and escalate issues to do with team, systems, and delivery.
  • Monitor and assist in adjusting team pace to instil a sense of urgency while remaining calm and ensuring the team acts sustainably at all times.
  • Understand and communicate strategy, rationale, solutions architecture, and progress to the team and to stakeholders.
  • Guide, co-write & review design documentation.
Achievements
  • Worked as part of an initiative to drive operational efficiency through the use of machine learning.
  • Worked with the Principal Software Architect to articulate a vision for the evolution of our systems in order to meet commercial objectives.
  • Worked with the Principal Software Architect to introduce a lightweight approach to improve the way product changes are communicated, reviewed, and delivered including Requests for Comments (“RFC”), Architecture Decision Records (“ADR”), and Non-Functional Requirements (“NFR”).
  • Performed technical due diligence for the acquisition of Placeit.

2017: Engineering Hiring Specialist, Envato

Responsibilities
  • Reporting to the CTO.
  • Working as part of the people team.
  • Improve Envato’s Engineering capability and capacity.
  • Increase diversity, equity, and inclusion (“DEI”).
Achievements
  • Hired the first participants in our apprentice program. The apprentice program is open to women entering the technology workforce for the first time.
  • Doubled the hiring rate while maintaining standards by applying Lean practices.
  • Doubled the number of female engineers by identifying and addressing biases within the process, proactively reaching out to candidates, and continuously measuring against targets.
  • Recruited and hired Envato’s first female Senior Engineering Manager.

2017: Engineering Lead, LAMP, World Mosquito Program

Responsibilities
  • Work with scientists from Monash University with funding from the Bill and Melinda Gates Foundation to rollout and monitor measures to control the spread of malaria throughout South-East Asia, the Pacific, South America, and Northern Australia.
Achievements
  • Built custom machine learning (“ML”) computer-vision (“CV”) software to analyse smartphone photographs of trays containing mosquito DNA experiments.
  • Enabled local laboratories to run automated analysis of experiments at a fraction of the cost and with error rates an order of magnitude lower than the previous manual process.

2016-2024: Co-founder and Engineering Lead, Startup - Automated Analytics

Responsibilities
  • Hands-on engineering, developing and delivering high-performance image processing, visualisation, and digital asset management platform targeting bio-medical, materials science, and geo-physics industries.
  • Currently working with a scale-up looking for market fit with the high-performance-computing (“HPC”) sector for biomedical and health research.
Achievements
  • Developed a range of proprietary CPU and GPU-based statistical and machine learning (“ML”) software tools to process computed tomography (“CT”), scanning electron microscope (“SEM”), and light microscope images.
  • Developed a custom web-based visualisation and catalogue management tool.
  • Scaled the denoising, segmentation, and analysis to support images in the hundreds of gigabytes size range.
  • Scaled the processing of images to be orders of magnitude faster than the current leading industry competitors.
  • Deployed Aizen at the University of Houston, Texas to analyse rock samples.

2014: Engineering and Delivery Lead, Interbank eFX High-Frequency Trading, ANZ

Responsibilities
  • Manage the development and delivery, and perform hands-on development of ANZ’s global electronic foreign exchange (eFX) high-frequency marketplace, providing liquidity to over 30 international banks, hedge funds, and foreign exchange brokers such as Reuters.
Achievements
  • Improved tick-to-trade latency from seconds to single-digit milliseconds.
  • Increased by two orders of magnitude the number of trading clients.
  • Doubled the number of instruments offered.
  • Tripled the number of securities offered.
  • All but eliminated non-business trade rejections.
  • Split a monolithic codebase into more focused components creating greater autonomy for sub-teams.
  • Simplified the build and release process and moved from quarterly to weekly releases.
  • Introduced continuous integration practices.
  • Replaced three legacy integration platforms.
  • Significantly improved overall supportability and reliability.
  • Built out co-location in London and Tokyo.

2011: Consultant and Developer, Cogent Consulting

  • Led the development and delivery of an online learning environment and accompanying iPad app for Melbourne Business School.
  • Worked with another developer and a designer to build Readtime, an iPhone app for getting through your read-it-later list. Readtime was featured in New & Noteworthy in 50+ countries as well as reviewed on sites such as Beautiful Pixels, Lifehacker, One Thing Well, and Cult of Mac.

2009: Practices Lead, Civica

  • Led the introduction of automated testing, continuous integration, and mapping out the architecture for the next generation of their flagship product used by Local Government Authorities around Australia, New Zealand, and Malaysia.

2008: Consultant and Developer, Cogent Consulting

  • Managed the development and delivery, and performed hands-on development of Runway, a web-based Getting Things Done (“GTD”) application. Runway has features that at the time were unique, including natural language parsing for todo items including dates, times, contacts, reminders, tags, contexts, etc.
  • Managed the development and delivery, and performed hands-on development of a business rules system used to classify book content for repurposing as website content at Lonely Planet.

2003: Independent Consultant

  • Worked with numerous multinational and domestic organisations around Australia providing agile coaching, performing architecture reviews, leading development teams, and performing hands-on development.

2003: Simian Code Clone/Duplication Detection Tool

  • Built Simian, a tool for detecting copy&paste code in a multitude of programming languages.
  • Sold Simian to Quandary Peak Research in late 2022.
  • Simian is used by hundreds of commercial and open source projects world-wide including defence departments, space research organisations, and large consulting firms.
  • The mathematics institute of the Netherlands replaced their home grown-system with Simian.

2001: Java Practice Lead, ThoughtWorks Australia

  • Employee number 11 at Thoughtworks Australia
  • Responsible for hiring local developers, technical pre-sales, agile coaching, developing and delivering training material.
  • Managed teams and performed hands-on development at NAB, Westpac, Suncorp, and Macquarie Bank in the development and delivery of large distributed applications.

1999: Independent Consultant

  • Led teams, provided agile coaching, developed and ran Object-Oriented training courses, and worked as a hands-on developer for clients such as United Energy, VicRoads, and DMR (now Fujitsu).

1996: Developer, Addease

  • Managed a team of 6 people and performed hands-on development.
  • Developed a large-scale, client-server Human Resources application that was eventually bought out by Morgan Banks recruitment.
  • Introduced eXreme Programming (“XP”) practices.

1991: Apprentice Developer, Australian Systems Engineering

  • Developed mainframe applications for Australian and international clients.
  • Taught myself x86 and IBM System/370 Assembler, C, and C++.
  • Built CPU emulators, interpreters, and compilers for proprietary languages that are still in use today.
  • Assisted with the instruction of several System/370 programming courses conducted by ASE at Ferntree Computer Corporation.

Keywords

Agile, Algorithms and Data Structures, Analysis, Amazon Web Services (“AWS”), Architecture, Artificial Intelligence (“AI”), Art of Hosting and Harvesting, Assembly Language, Business Rules, C/C++, Career Development, Cascading Stylesheets (“CSS”), Change Management, Coaching, Communication, Computer Vision (“CV”), Continuous Improvement, Continuous Integration/Continuous Deployment (“CI/CD”), Culture Change, Data, Databases, Decision-Making, Delivery, Design, Development, Diversity/Equity/Inclusion (“DEI”), Due Diligence, Effectiveness, Empowerment, Engineering, eXtreme Programming (“XP”), Event Sourcing, Execution, HyperText Markup Language (“HTML”), Governance, High Performance Computing (“HPC”), Hiring, iOS, Java, Javascript, Kotlin, Leadership, Machine Learning (“ML”), Motivational Interviewing (“MI”), MySQL, Objective-C, Object-Oriented Programming (“OOP”), Operations, Participatory Leadership, People Management, Performance Management, PostgreSQL, Product Development, Psychological Safety, Recruitment, Risk Management, PowerBuilder, Roadmapping, Ruby, Ruby on Rails, Software, SQL, Strategy, Swift, Theory of Constraints (“TOC”), Typescript, Vision.

Simon Harris

By

In the backyard overlooking the chickens and "granny flat" c. 2023.
In the backyard overlooking the chickens and "granny flat" c. 2023.

I respectfully acknowledge the Wurundjeri Woi-wurrung people as the Traditional Owners of the Country on which I live, and the value and the significance of those people and their history.

My friends call me Sampy.

I’ve had the pseudonym “Haruki Zaemon” since I first lived in Japan.

Raising a family of three teenage-ish children and one grandparent, with Jess.

Student of Aikido since 1988, teaching Aikido in Eltham, Victoria, Australia

Head of Engineering / Engineering Director, working in Software and Technology since 1991.

Once made a joke about non-dairy crema.

Opinions are sometimes mine, sometimes others, rarely substantiated.

I post musings, share links I liked, and briefly review books I’ve read, on a range of topics including Psychological Safety, Software Engineering, Organisational Psychology, Motivational Interviewing, Leadership, and Diversity, Equity, and Inclusion.

Checkout my resume if you’d like to know more about my work history and skills.

Generate a site44.com redirects file for your Octopress blog

By

Over the weekend I converted my, albeit languishing blog from straight Jekyll on GitHub Pages to use Octopress served via Site44.

In the process I also took the opportunity to switch the URLs I was using for blog entries to the Octopress default. This in turn left me needing a bunch of redirects.

Site44 supports redirects via a well-known text file in the root of your website. The text file provides a mapping between source and destination paths. That’s all very well and good but I didn’t much feel like creating 300+ redirect mappings by hand. Besides the tedious nature of the task, the chances I was going to screw one or more of them up in the process were fairly high.

Thankfully, due to a previous blog move, I happened to have a bunch of permalink definitions in the front-matter of most of my blog entries. All I really needed then was a way to turn those into said text file.

A quick Google turned up the Alias Generator plugin for Octopress by Thomas Mango which was very close to, but not quite what I needed.

Hack hack hack on the plugin, a quick rename of all the permalink attributes in my posts to alias, and voila! a new plugin that generates a redirects.site44.txt with all my redirects:

# Site 44 Redirects Text Generator for Posts.
#
# Generates a www.site44.com compatible redirects file pages for posts with aliases set in the YAML Front Matter.
#
# Place the full path of the alias (place to redirect from) inside the
# destination post's YAML Front Matter. One or more aliases may be given.
#
# Example Post Configuration:
#
#   ---
#     #     title: "How I Keep Limited Pressing Running"
#     alias: /post/6301645915/how-i-keep-limited-pressing-running/index.html
#   ---
#
# Example Post Configuration:
#
#   ---
#     #     title: "How I Keep Limited Pressing Running"
#     alias: [/first-alias/index.html, /second-alias/index.html]
#   ---
#
# Author: Simon Harris
# Site: http://harukizaemon.com

module Jekyll
  REDIRECTS_SITE44_TXT_FILE_NAME = "redirects.site44.txt"

  class RedirectsSite44TxtFile < StaticFile
    def write(dest)
      begin
        super(dest)
      rescue
      end

      true
    end
  end

  class RedirectsSite44TxtGenerator < Generator
    def generate(site)
      unless File.exists?(site.dest)
        FileUtils.mkdir_p(site.dest)
      end

      File.open(File.join(site.dest, REDIRECTS_SITE44_TXT_FILE_NAME), "w") do |file|
        process_posts(site, file)
        process_pages(site, file)
      end

      site.static_files << Jekyll::RedirectsSite44TxtFile.new(site, site.dest, "/", REDIRECTS_SITE44_TXT_FILE_NAME)
    end

    def process_posts(site, file)
      site.posts.each do |post|
        generate_aliases(file, post.url, post.data['alias'])
      end
    end

    def process_pages(site, file)
      site.pages.each do |page|
        generate_aliases(file, page.destination('').gsub(/index\.(html|htm)$/, ''), page.data['alias'])
      end
    end

    def generate_aliases(file, destination_path, aliases)
      Array(aliases).compact.each do |alias_path|
        file.puts("#{alias_path} #{destination_path}")
      end
    end
  end
end