Atom Feed SITE FEED   ADD TO GOOGLE READER

Extensible Properties for the JVM, refined

Remi and Richard are talking about properties again, refining their ideas. I figured I should organize my thoughts as well.

FIrst off, how about some aggressive requirements:
  1. Properties should support arbitrary implementations! It should be possible to have properties backed by fields, HashMaps, databases, calculations, devices etcetera. Using such a property should require minimal amounts of code.
  2. Properties should support pluggable behaviours. Properties should be able to be made Observable, non-nullable, validated, copied-on-write, etcetera. Using such a behaviour should require minimal amounts of code.
  3. Properties should not increase per-instance memory usage.
  4. Properties should have a minimal impact on performance
  5. Properties should be toolable.
  6. Properties should interact cleanly with legacy getters and setters.
Just as the enhanced for loop works using the Iterable interface, I think the properties for the JVM should have a Property interface like this:
public interface java.lang.Property<B,V> {
void setName(String name);
void setDelegate(Property other);
void set(B bean, V value);
V get(B bean);
}
Note that this interface provides a delegate to allow Properties to be chained. This allows behaviours like observable to be stacked.

When defining a property, the coder provides a list of properties objects to be chained in varargs style:
class Foo {
private property(new ObservableProperty(), new NonNullProperty()) String bar = "phils";
}
...is syntactic sugar for:
class Foo {
private String bar = "phils";
private static Property<Foo,String> $PROPERTY_bar = new ObservableProperty();
static {
Property<Foo,String> p1 = new NonNullProperty();
Property<Foo,String> p2 = new DefaultProperty<Foo,String>() {
public String get(Foo foo) { return bar; }
public String set(Foo foo, String bar) { foo.bar = bar; }
};
p1.setDelegate(p2);
$PROPERTY_bar.setDelegate(p1);
}
}


Let's disect the generated code:
  • private static Property<Foo,String> $PROPERTY_bar
    The property literal is a static member of the class. The property is static so we don't increase memory usage per-instance. The name starts with a $ dollar sign to denote the field is compiler-generated and cannot be referenced normally.
  • = new ObservableProperty()
    The property literal instance is the outermost decorated value. This code is the first argument after the property token in the original source.
  • static { Property<Foo,String> p1 = new NonNullProperty(); p2 = ...
    Properties are constructed in order, from outermost to innermost. They're created in a static initializer because the property literals are shared between all instances.
  • = new DefaultProperty{Foo,String}() { ... }
    Default code to retrieve the field is generated. Because actual bytecode is created, field access is significantly faster than reflection.
  • p1.setDelegate(p2);
    The chain of property literals is linked together, innermost first
It's not as interesting to me, but it's also straightforward to create syntactic sugar for accessing properties:
    Foo foo = new Foo();
foo#bar = "bomber";
System.out.println(foo#bar);
... and property literals
    Property<Foo,String> barProperty = Foo##bar;
barProperty.set(new Foo(), "revolution");
System.out.println(barProperty.get(new Foo()))
Noting of course that the symbols # and ## are totally arbitrary and could be exchanged with any symbols, such as :, $, []. I think that multiple symbols are necessary to differentiate between a particular instance's property value and a property literal, otherwise the symbol #bar is ambiguous inside the Foo.java source file.

Terminal emulators reviewed

I spend a lot of time at the command line, so a good terminal emulator is important to me. Unfortunately, the ones that get packaged with Linux aren't that great:
Konsole
  • Terrible text wrapping support, no-reflow and newlines get inserted when you cut & paste
  • Slow
    Gnome Terminal
  • Bad text wrapping support, no re-flow
  • Impossible to scroll back while output is being generated
  • Slow

    On the Mac, the built-in terminal works great. But it lacks tabs, so I end up wasting a lot of time in Expose looking for the right window.
    Mac OS X Terminal
  • Really nice split-pane for scrollback
  • Perfect text wrapping
  • True transprency
  • But no tabs!
  • Mac OS X only!

    Fortunately, I've found the perfect terminal emulator. It's fast, it's simple, and it's very easy to install:
    Terminator
  • Unique horizontal scrolling (ideal for SQL)
  • Fast
  • Great keyboard shortcuts
  • Unlimited scrollback is on by default
  • Fixing IntelliJ crashes in StartupActionScriptManager

    On my Ubuntu box IntelliJ began crashing on startup with an unhelpful stacktrace:
    chixdiglinux:intellij-idea-6$ /usr/lib/intellij-idea-5/bin/idea.sh
    java.io.EOFException
    at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2237)
    at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2703)
    at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:760)
    at java.io.ObjectInputStream.(ObjectInputStream.java:278)
    at com.intellij.ide.startup.StartupActionScriptManager.b(StartupActionScriptManager.java:34)
    at com.intellij.ide.startup.StartupActionScriptManager.executeActionScript(StartupActionScriptManager.java:2)
    at com.intellij.ide.plugins.PluginManager.initClassloader(PluginManager.java:400)
    at com.intellij.ide.plugins.PluginManager.bootstrap(PluginManager.java:424)
    at com.intellij.ide.plugins.PluginManager.main(PluginManager.java:189)
    at com.intellij.ide.plugins.PluginManager.main(PluginManager.java:396)
    at com.intellij.idea.Main.main(Main.java:4)
    Exception in thread "main" java.lang.AssertionError:
    at com.intellij.openapi.diagnostic.DefaultLogger.error(DefaultLogger.java:49)
    at com.intellij.openapi.diagnostic.Logger.error(Logger.java:60)
    at com.intellij.ide.plugins.PluginManager.initClassloader(PluginManager.java:348)
    at com.intellij.ide.plugins.PluginManager.bootstrap(PluginManager.java:424)
    at com.intellij.ide.plugins.PluginManager.main(PluginManager.java:189)
    at com.intellij.ide.plugins.PluginManager.main(PluginManager.java:396)
    at com.intellij.idea.Main.main(Main.java:4)


    I tried asking Google, which led me to a bug that was similar, but not the same. I didn't have the file they instructed for me to delete, system/plugins/action.script.

    I tried the helpful but awkward tool lsof, which tells you which files are opened on your Unix machine. I wanted to get a hint about which file IntelliJ IDEA was crashing on! It turns out that it had a cache in /var/cache/intellij-idea-5/jessewilson/, and it was breaking on the action.script file in that folder.

    Is your IntelliJ crashing on startup? Consider removing the /var/cache/intellij-idea-5 folder and try again!

    EasyMock and OutOfMemoryError: PermGen space

    I wrote a test using the easy-to-use EasyMock Unit-testing API:
    package com.publicobject.pizzadelivery;

    import junit.framework.TestCase;
    import org.easymock.classextension.EasyMock;

    public class PizzaDeliveryPersonTest extends TestCase {
    private PizzaStore pizzaStore;
    private Customer customer;
    private Order order;
    private Address address;
    private DeliveryPerson deliveryPerson;

    @Override
    public void setUp() {
    pizzaStore = EasyMock.createMock(PizzaStore.class);
    order = EasyMock.createMock(Order.class);
    address = EasyMock.createMock(Address.class);
    customer = EasyMock.createMock(Customer.class);

    EasyMock.expect(pizzaStore.getNextOrder())
    .andReturn(order).anyTimes();
    EasyMock.expect(order.getAddress())
    .andReturn(address).anyTimes();
    EasyMock.expect(order.getPrice())
    .andReturn(new Money(17)).anyTimes();
    EasyMock.expect(address.getCoordinates())
    .andReturn(new GpsCoordinates(30, 40)).anyTimes();
    EasyMock.expect(customer.acceptOrder(order))
    .andReturn(new Money(20).anyTimes();

    EasyMock.replay(pizzaStore);
    EasyMock.replay(order);
    EasyMock.replay(address);
    EasyMock.replay(customer);
    }

    public void testDeliveryPerson() {
    doTestDeliveryPerson(new BasicPizzaDeliveryPerson());
    }

    public void testDeliveryPersonWithNoGasInCar() {
    BasicPizzaDeliveryPerson deliveryPerson = new BasicPizzaDeliveryPerson()
    deliveryPerson.getCar().setFuel(0);
    doTestDeliveryPerson(deliveryPerson);
    }

    public void testDeliveryPersonWhoSpeaksFrench() {
    BasicPizzaDeliveryPerson deliveryPerson = new BasicPizzaDeliveryPerson()
    deliveryPerson.setNativeLanguage(Language.FRENCH);
    doTestDeliveryPerson(deliveryPerson);
    }

    public void testRealTimeDeliveryPerson() {
    PizzaDeliveryPerson deliveryPerson = new RealTimePizzaDeliveryPerson()
    doTestDeliveryPerson(deliveryPerson);
    }

    public void testMinimumWageDeliveryPerson() {
    BasicPizzaDeliveryPerson deliveryPerson = new BasicPizzaDeliveryPerson()
    deliveryPerson.setHourlyWage(new Money(7));
    doTestDeliveryPerson(deliveryPerson);
    }

    public void doTestDeliveryPerson(PizzaDeliveryPerson deliveryPerson) {
    assertTrue(deliveryPerson.getTotalMoney().toDollars() == 0);
    deliveryPerson.setStore(pizzaStore);
    deliveryPerson.runDelivery();
    assertTrue(deliveryPerson.getTotalMoney().toDollars() == 20);
    assertTrue(deliveryPerson.getTotalTips().toDollars() == 3);
    }
    }

    ...but when I ran it in my continuous build, it failed due to a memory leak:
    java.lang.OutOfMemoryError: PermGen space

    It turns out that every time I call EasyMock.createMock()*, a piece of memory is allocated that can never be reclaimed!

    My hacky fix was to make all the mocked objects static. Now they only get created once, not once for each of the 5 test methods:
    public class PizzaDeliveryPersonTest extends TestCase {
    private static PizzaStore pizzaStore;
    private static Customer customer;
    private static Order order;
    private static Address address;
    private static DeliveryPerson deliveryPerson;

    static {
    pizzaStore = EasyMock.createMock(PizzaStore.class);
    order = EasyMock.createMock(Order.class);
    address = EasyMock.createMock(Address.class);
    customer = EasyMock.createMock(Customer.class);

    EasyMock.expect(pizzaStore.getNextOrder())
    .andReturn(order).anyTimes();
    EasyMock.expect(order.getAddress())
    .andReturn(address).anyTimes();
    EasyMock.expect(order.getPrice())
    .andReturn(new Money(17)).anyTimes();
    EasyMock.expect(address.getCoordinates())
    .andReturn(new GpsCoordinates(30, 40)).anyTimes();
    EasyMock.expect(customer.acceptOrder(order))
    .andReturn(new Money(20).anyTimes();

    EasyMock.replay(pizzaStore);
    EasyMock.replay(order);
    EasyMock.replay(address);
    EasyMock.replay(customer);
    }

    ...
    }

    It gets the job done, but not well. The disadvantages of this approach:
  • I need to use anyTimes() on my objects so their methods can be called repeatedly
  • The code is ugly
  • I still have a memory leak, it's just a bit smaller

    It would be a great fix for EasyMock if multiple calls to EasyMock.createMock(PizzaStore.class) could share the same generated class object.
  • Extensible Properties for the Java Language

    Remi Forax is leading some research on hacking in properties into the JVM. Richard Bair's latest post has inspired me to write about how I think observable properties could be implemented.

    I would like properties to be extensible, so that all the generic boilerplate (not just get/set) can be applied:
    public class Person {
    private @Property(ObservableProperty.class) String surname;
    }

    ...where ObservableProperty implements a chain-of-command interface for setting properties:
    interface Property<B,T> {
    /** the delegate holds the 'real' property that we're just decorating */
    public void setDelegate(Property<B,T> delegate);
    /** @param name the bean property name, such as surname */
    public void setName(String name);
    public T get(B bean);
    public void set(B bean, T value);
    }

    ... and ObservableProperty is implemented something like this:
    public class ObservableProperty<B extends Observable,T> implements Property<B,T> {
    private Property<B,T> delegate;
    private String name;
    public void setDelegate(Property<B,T> delegate) {
    this.delegate = delegate;
    }
    public void setName(String name) {
    this.name = name;
    }
    public T get(B bean) {
    return delegate.get(bean);
    }
    public void set(B bean, T value) {
    T old = delegate.get(bean);
    delegate.set(bean, value);
    firePropertyChange(bean.getListeners(), name, old, get(bean));
    }
    }


    The real power of this approach is chaining decorators:
       private @Property(ObservableProperty.class, NotNullProperty.class) String surname;
    private @Property(ObservableProperty.class, EmailAddressProperty.class) String emailAddress;
    private @Property(TransactionalProperty.class) long id;


    This approach makes code better because it is declarative. We are telling the API what to do, not how to do it.

    Javac bug: the order of imports is significant

    The following Java code does something slightly weird - it statically imports a symbol that is defined in the same file. Although it's a little unconventional, it should be perfectly legal:

    package com.publicobject.dinosaurs;

    import static com.publicobject.dinosaurs.Dinosaur.GeologicPeriod.CRETACEOUS;
    import static com.publicobject.dinosaurs.Dinosaur.GeologicPeriod.TRIASSIC;
    import static com.publicobject.dinosaurs.Dinosaur.GeologicPeriod.JURASSIC;
    import com.publicobject.time.Period;

    public class Dinosaur {

    private Period period;
    private String name;

    public static class GeologicPeriod extends Period {
    public static final GeologicPeriod TRIASSIC = new GeologicPeriod(-251, -199.6, "Triassic");
    public static final GeologicPeriod JURASSIC = new GeologicPeriod(-199.6, -145.5, "Jurassic");
    public static final GeologicPeriod CRETACEOUS = new GeologicPeriod(-145.5, -65.5, "Cretaceous");

    private String name;

    public GeologicPeriod(double startAsMillionYears, double endAsMillionYears, String name) {
    super(startAsMillionYears, endAsMillionYears);
    this.name = name;
    }
    }

    public Dinosaur(Period period, String name) {
    this.period = period;
    this.name = name;
    }

    public boolean isJurassic() {
    return period.overlaps(JURASSIC);
    }

    public boolean isTriassic() {
    return period.overlaps(TRIASSIC);
    }

    public boolean isCretaceous() {
    return period.overlaps(CRETACEOUS);
    }
    }


    ...except that this Java code does not compile! It fails with a bizarre error, Javac is not able to find the Period class:
    /Users/jessewilson/Dinosaurs/IntellijProject/source/com/publicobject/dinosaurs/Dinosaur.java:13: cannot find symbol
    symbol : class Period
    location: class com.publicobject.Dinosaur
    public static class GeologicPeriod extends Period {
    ^


    If I rearrange the imports, moving the import of the Period class ot the top, the code compiles.