/dev/null
Cameron Purdy
archives
links

recent
20060210 Friday February 10, 2006

Thoughts late at night ...

Definitely getting too old for this ....

(2006-02-10 01:01:54.0) Permalink Comments [1]

20060206 Monday February 06, 2006

Java hosting

I get asked a lot where we host our publicly-available Java-based server applications. Until recently, my answer was that we hosted them ourselves, with the help of a T1, some rack servers and a bit of elbow grease. A few months ago, we started talking to a company called Contegix that came highly recommended from a number of people we knew in the industry. There are several reasons why it was important to us to find a hosting provider for these applications:

  • Despite popular myth, T1 lines are not necessarily any more reliable than other forms of IP connectivity. Sure, we have one throat to choke, but choking people doesn't seem to have any effect on an outage, and our local telco (a small company named Verizon) has had at least a half-dozen major outages over the past year. Unfortunately, there's no way to get a second physical provider here, as Verizon owns all the lines (including all of the "business-class" DSL lines offered by Covad, XO, etc.) When hosting applications, one needs high out-bound data rates, so any option (e.g. cable, satelite) with less bandwidth than SDSL are disqualified as an option. Plus, when it's a back-hoe that digs up the cable bundle, redundancy from the same company doesn't help.

  • All of our equipment is on battery-backup, but we do lose power a couple of times a year, and at least twice we've lost it for long enough that it exceeded the duration of the battery-backups. Having worked with Sparc/Solaris and other UNIX servers for a good long time, and having had to personally re-install various OS's starting from a clean format more than once, I can tell you that there is nothing scarier than having a UNIX server lose power. Fortunately, Linux has proven to be nowhere near as fragile (which is likely a reflection of the average wealth of its user base, i.e. they can't afford redundant power and their own generators). Nonetheless, the thought of having production data on a server without having 24/7 monitoring and a generator still scares me.

  • Despite having a relatively large collection of boxen, we're not actually a hardware company. The Intel P3-based 1U rack servers we've been running some of these apps on are antiques (to put it kindly). Sooner or later, we know that hard drives, fans, memory, NICs and CPUs will all fail, and so far we have just been lucky that none of them have! (BTW - kudos to Intel on those servers.) Also, keeping sufficient spares on hand takes good planning and careful attention to detail, which implies a full time role.

  • Despite having a company full of software geeks, it is impossible for us to keep up with all the patches for the various hardware, OS, JVM, library and application software. The security bulletins alone come at a high enough volume to warrant a full time role.

  • Configuration, administration and monitoring are all fairly in-depth tasks, and we host a large and growing number of applications and their various components. Really learning a system in order to do a great job of managing a grand total of one whole instance of that system is .. a huge waste.

We knew we were going to turn to a hosting provider. There are a number of very good Java hosting companies out there, so it could have been a hard decision, but we had recommendations to look at Contegix and they did a good job of addressing the issues that I listed above. We weren't looking for the least expensive option, since we already had far outgrown the various budget plans. Rather, we were looking for a very dependable option that would unload a huge amount of work from our staff. Another thing that worked in Contegix' favor was their experience with the very applications that we were planning to host (such as JIRA, Confluence, and Jive) as well as the application servers that we use.

I remember looking for a hosting company a few years ago, and there were not many options that I would have considered. The market for Java hosting has grown signficantly, and there are options from the very inexpensive to the very complete (refer to the above link to the JavaLobby discussion). We've been using Contegix only a short time now, but I've been very pleased with their ability to deliver on what they promised us, and I have already recommended them several times.

(2006-02-06 12:00:00.0) Permalink Comments [12]

20060204 Saturday February 04, 2006

Latest Jive

I recently blogged about Confluence, which we use internally for all sorts of stuff, and recently put up at wiki.tangosol.com to host online documentation. We haven't yet put JIRA up as a public site, but we do use it extensively internally and hope to eventually have our own public "bug parade" (a la Sun and the Java Developer Connection).

Well, a few days ago we finished an upgrade to the latest Jive Forums 4.2.4 release on our new support site, forums.tangosol.com (formerly tangosol.net), and it really rocks. (Coincidentally, it uses Coherence for clustering. ;-) One of the great features in this new version is that all new posts in a particular forum can be marked as "questions" by default, with simple work-flow to mark which ones are answered and which ones are not. For a support site, that is a super big help for making sure that things get resolved.

Another one I'd like to get into production is Ashkelon, the same open source project that is used to host JDocs.

I have been incredibly pleased at the maturity of many open source projects and commercial products out there for supporting the entire software process, from start to finish, and -- better yet! -- how many of them can be easily hosted for our customers to use. Kudos!

(2006-02-04 13:45:00.0) Permalink

20060203 Friday February 03, 2006

CNN or TheOnion?

If you saw a quote like this, would you guess it was from the Onion?

"Bin Laden our beloved, Denmark must be blown up," protesters in Ramallah chanted.

Follow this TinyURL to find out the real answer: http://tinyurl.com/d7u5g

(2006-02-03 22:00:00.0) Permalink Comments [3]

20060202 Thursday February 02, 2006

Java Continuations, Revisted

I re-read Rob Harwood's explanation of continuations, and it reminded me that I was going to suggest a way to achieve continuations in Java. Basically, it's extremely easy to generate a continuation point:

public Object anyfoo()
    {
    Object oReturn = null;
    // some stuff is done here
    // ...
    // for whatever reason, stop executing but allow
    // execution to start from this point later
    throw new ContinuationPoint();
    // some more stuff is done here
    // ...
    return oReturn;
    }

Detecting the condition and saving it off for later continuation is also simple:

try
    {
    // do lots of work
    }
catch (ContinuationPoint e)
    {
    session.setAttribute("most-recent-progress", e);
    }

It's even easy to later resume from where the code left off:

ContinuationPoint cp = (ContinuationPoint) session.getAttribute("most-recent-progress");
if (cp != null)
    {
    session.removeAttribute("most-recent-progress");
    cp.continue();
    }

Of course, the secret is hidden in the latest pre-alpha of JDK 1.7:

package java.lang;
public class ContinuationPoint
        extends Exception
    {
    public native void continue()
            throws AllSortsOfFunkyErrors;
    }

It could be true .. ;-)

(The implementation is left up to the reader.)

(2006-02-02 14:00:00.0) Permalink Comments [8]

Apple MacCluster

Our new Apple cluster showed up today, including a rack-full of beautiful G5s and (for comparison) the new Intel-based Macs.

Sweet!

(2006-02-02 12:00:00.0) Permalink Comments [3]

Dogma, Josh Bloch and Java Constructors

A few days ago, I made a post using an example bean implementation from Sam Pullara. The bean calls property mutators from the constructor. The property mutators are public. I thought nothing of it.

Benoit commented:

On a sideline unrelated to the discussion..., isn't it bad practice for a constructor of a non-final class to call non-final methods? Risk is that you could create a class that extends it and gets called before its parent is fully built...

Best,
Benoit.

I responded that no, it was not bad practice, and it's quite often the intent. Joop then commented:

That maybe your intent, Mr. Purdy, but Benoit is right. Maybe your requirements outweigh the dangers - but it is definitely bad practice, in general. You are passing a partially-constructed object ("this") to the overriding method and that's a recipe for disaster.

... and subsequently:

It's not dogma, it's common sense. Are there "very well thought out and very good patterns" that use or rely on virtual invocation from a constructor? Probably, though maybe not *so* well thought out. That's why I said that "maybe your requirements outweigh the dangers". But my point was just that *Benoit* was right and you dismissed him summarily. It *is* bad practice. Look no further than Effective Java itself, the defacto arbiter of bad practice in Java.

(I hope I do not dismiss the points of others summarily. My response may have sounded flippant, but I have great respect for the considered opinions of others. In fact, all of my opinions were at some point in the past the opinions of others that I have simply appropriated for my own use. I greatly enjoy technical arguments, because of how much I learn from them, so Benoit and Joop, I mean no disrepect, and thank you for disagreeing. Now, back to my regularly scheduled bile. ;-)

Well, since Dr. Josh Bloch said it, and since it's in a book called Effective Java, and since that book is sold on Amazon, and since Amazon uses lots of Java to run their web site, and since Java comes from Sun, and since the sun was a Mayan god, then I guess I have to be wrong, because God can't be wrong.

On the other hand, maybe the idea that "calling a virtual method from a constructor is a bad thing" is itself wrong. After all, virtual invocation from a constructor is how we implemented inheritable state and inheritable parent/child composition in our Java component modeling tool, and it has no problem supporting inheritance a hundred levels deep. (The component modeling tool is built using itself, including its entire GUI, and inheritance a hundred levels deep is common.)

And if a "property" is itself a virtual concept, then there is no way to initialize any non-private properties without calling virtual methods from a constructor. (I refer to properties, as opposed to "fields", which arguably should not even exist in Java. A field is just a accessor/mutator combination whose implementation happens to be native.)

So the real question isn't whether it's right or wrong to call a virtual (i.e. not static, not private, not final) method from a constructor, because I could publish a book that says it's right. In fact, if I were to do so, I would certainly claim that in modern software, when composing sufficiently complex systems, virtual self invocation during construction is unavoidable without creating more entropy than the entropy that the rule is attempting to avoid.

At the same time, to Benoit's and Joop's point, calling one's own virtual methods during construction without a clear pattern for construction is a recipe for disaster. It is self-evident that some processing must only occur during construction, and that some processing must never occur during construction. With parent/child relationships, construction further envelops an entire tree of object instances, all of which must co-relate in a coordinated manner, thus adding an order of magnitude more complexity to the construction (since "new" is not virtual, for example). The choreography of construction is not something that can be flippantly addressed by dogmatic statements; we probably spent a man-year carefully designing, prototyping, testing and refining our approach to it. In the eight years since then, it has proven to be a zero-defect design, with some millions of lines of code built using it (including the GUI tool in which the component model is designed and the model compiler that produces the constructors from the component model).

Perhaps the real question we should be asking is this: Why are programmers still having to write constructors at all?

(2006-02-02 09:15:00.0) Permalink Comments [10]

20060130 Monday January 30, 2006

Java tops again in language rankings!

Java had another dramatic jump forward in the Tiobe Programming Community Index (often called the Tiobe language rankings). From the site:

The TIOBE Programming Community index gives an indication of the popularity of programming languages. The index is updated once a month. The ratings are based on the world-wide availability of skilled engineers, courses and third party vendors. The popular search engines Google, MSN, and Yahoo! are used to calculate the ratings.

Some interesting rankings: Java at 22.25%, C++ at 11.46%, Perl at 7.05%, C# at 3.55%, VB.NET at 0.51%, and Ruby at 0.37%.

I looked very closely at the data they used to assemble it, and one very interesting statistic came to the top: An astonishing 42% of the references to Ruby on the entire web come from a blog by Obie Fernandez, who averages between five and seven "blogarific" posts a day on the "awesome" benefits of Ruby.

Oh, those crazy ThoughtWorkers ;-)

(2006-01-30 22:45:00.0) Permalink Comments [8]

Making a Person XmlBean

Taking the example class from yesterday as a starting point, I've converted it to be a Tangosol XmlBean:

public class Person
        extends XmlBean
    {
    /**
    * Default constructor for Externalizable.
    */
    public Person() {}
    public Person(String firstName, String lastName, int age)
        {
        this.age = age;
        this.setFirstName(firstName);
        this.setLastName(lastName);
        }
    public String getFirstName()
        {
        return firstName;
        }
    public void setFirstName(String firstName)
        {
        this.firstName = firstName;
        }
    public String getLastName()
        {
        return lastName;
        }
    public void setLastName(String lastName)
        {
        this.lastName = lastName;
        }
    public int getAge()
        {
        return age;
        }
    public void setAge(int age)
        {
        this.age = age;
        }
    private String firstName;
    private String lastName;
    private int age;
    }

(Yes, I know BEA donated something called XMLBean to Apache. Different XMLBean. Why we didn't trademark the name I'll never know. ;-)

So the only differences are that there's a new constructor (for Java Externalizable). Additionally, there is an XML mapping file by the same name as the class (e.g. if it compiles to Person.class, then there will be a Person.xml in the same folder):

<xml-bean>
  <name>person</name>
  <property>
    <name>LastName</name>
    <xml-name>last-name</xml-name>
  </property>
  <property>
    <name>FirstName</name>
    <xml-name>first-name</xml-name>
  </property>
  <property>
    <name>Age</name>
    <xml-name>age</xml-name>
  </property>
</xml-bean>

As an alternative to the mapping file, the declaration can be done inline:

public class Person
        extends XmlBean
    {
    static
        {
        // configure the XmlBean
        init(Person.class, "person", new String[] {"FirstName", "LastName", "Age"});
        }
    ...

The nice thing is that the annoyance methods (equals(), hashCode(), toString(), readObject(), writeObject(), and to/from XML support) is automatically provided. For example, the toString() generates an XML document:

<person>
  <first-name>Tina</first-name>
  <last-name>Neward</last-name>
  <age>80</age>
</person>

I also updated the test so I could create a few zillion semi-random people easily:

public class Test
        extends Base
    {
    public static void main(String[] asArg)
        {
        NamedCache cache = CacheFactory.getCache("test");
        cache.addIndex(new ReflectionExtractor("getLastName"), true, null);
        // parse the command line parameters
        int cPeople = 1000;
        try
            {
            cPeople = Integer.parseInt(asArg[0]);
            }
        catch (Exception e) {}
        // generate lots of test data
        for (int i = 0; i < cPeople; ++i)
            {
            Person person = new Person(FIRST_NAME[rnd.nextInt(FIRST_NAME.length)],
                                       LAST_NAME [rnd.nextInt(LAST_NAME .length)],
                                       rnd.nextInt(80) + 1);
            cache.put(new Integer(i), person);
            }
        // find all the newards
        Set setNewards = cache.entrySet(new EqualsFilter("getLastName", "Neward"));
        for (Iterator iter = setNewards.iterator(); iter.hasNext(); )
            {
            out(iter.next());
            }
        }
    static final String[] FIRST_NAME = parseDelimitedString("Bob,Dave,Sam,Sue"
        + ",Carla,Sally,Biff,Ted,Mary,Bill,Fred,Fanny,Lilly,Arjun,Betty"
        + ",Yakim,Valerie,Tina,Steve,Pam,Octavius", ',');
    static final String[] LAST_NAME = parseDelimitedString("Adams,Anderson"
        + ",Bates,Calverson,Davies,Everton,Fitts,Geiger,Hingham,Ingerson"
        + ",Jones,Kelly,Lindhurst,Madison,Neward,Oppenheimer,Pearl,Ringwald"
        + ",Smith,Tyson,Unger,Valentine,Wyles,Zetikov", ',');
    static Random rnd = new Random();
    }

Next up: Making it more than a read-only example.

(2006-01-30 10:00:00.0) Permalink Comments [5]

20060129 Sunday January 29, 2006

Response to Sam Pullara in response to Ted Neward

I was browsing Sam Pullara's blog when I saw an older entry in Response to Ted Neward's - Anonymous Generic Methods making things "just work". Basically, Sam starts with a JavaBean (or you can call it a POJO if you want to sound cool):

public class Person {
    private String firstName;
    private String lastName;
    private int age;
    public Person(String firstName, String lastName, int age) {
        this.age = age;
        this.setFirstName(firstName);
        this.setLastName(lastName);
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String toString() {
        return "Person{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", age=" + age +
                '}';
    }
}

The idea is to be able to search through a collection of these for a matching name, for example. The scaffolding that Sam whipped up in 10 minutes uses generics and a Java 5 iterator:

public class SetUtils {
    public List project(List list, Predicate pred) {
        List results = new ArrayList();
        for (T t : list) {
            if (pred.check(t)) results.add(t);
        }
        return results;
    }
}
public interface Predicate {
    public boolean check(T t);
}

The test program stuffs some test data in and iterates through it:

public class Program {
    public static void main(String[] args) {
        Person cg = new Person("Cathi", "Gero", 35);
        Person tn = new Person("Ted", "Neward", 35);
        Person sg = new Person("Stephanie", "Gero", 12);
        Person mn = new Person("Michael", "Neward", 12);
        List list = new ArrayList();
        list.add(cg);
        list.add(tn);
        list.add(sg);
        list.add(mn);
        List newards =
            new SetUtils().project(list, new Predicate() {
                public boolean check(Person p) { if (p.getLastName().equals("Neward")) return true; else return false; }
            });
        for (Person p : newards)
            System.out.println(p);
    }
 }
Output:
Person{firstName='Ted', lastName='Neward', age=35}
Person{firstName='Michael', lastName='Neward', age=12}

I recognized the concept. We support the same functionality for searching Coherence caches and data grids. Here's the equivalent code to search a Coherence cache:

Set setNewards = cache.entrySet(new EqualsFilter("getLastName", "Neward"));

One line of code -- done!

Custom predicates like Sam's are supported too:

Set setNewards = cache.entrySet(new Filter() {
    public boolean evaluate(Object o)
        {
        return ((Person) o).getLastName().equals("Neward");
        }
    });

Optionally you can sort the results, obtain just the key set, have Coherence page through the results one page at a time, etc.

These searches return the same results whether the data was all on one server or whether it was spread out across a data grid of one hundred servers. In fact, with 100 servers, Coherence load-balances the data across all 100 servers. When a query is run against that data, Coherence uses a Parallel Query. That means with 100 servers, you can handle 100x the number of queries, or query 100x as much data, or get the result back 100x quicker. To speed things up even more, Coherence supports indexes; for example, here's an ordered index on the person's last name:

cache.addIndex(new ReflectionExtractor("getLastName"), true, null);

Indexes can make queries absolutely fly, but while being able to find the data is pretty cool in and of itself, being able to process it is even more interesting ....

(2006-01-29 22:45:00.0) Permalink Comments [8]

Content copyright 2002, 2003, 2004, 2005 by Cameron Purdy. All rights reserved.