blog.smart-java.nl
Ordina J-Technologies – Java Blog

Archief ‘Algemeen’ categorie




Temporale data – deel 4: bitemporale collections

Door: gnoij, 7 December 2009

Het is alweer enige tijd geleden dat ik heb geschreven over temporale patterns. In dit deel wil ik het bitemporale pattern uitbreiden met bitemporale collections. Werd met de bitemporale property nog een enkele property historisch gemaakt, in dit deel maken we een lijst historisch. Dat wil zeggen dat er meerdere elementen van een historische lijst tegelijkertijd geldig kunnen zijn.

De klasse BiTemporalObject uit het derde deel blijft hetzelfde. En naast de BiTemporalProperty voor enkelvoudige historische relaties krijgen we nu ook een klasse BiTemporalCollection voor meervoudige historische relaties.

BiTemporalCollection

public class BiTemporalCollection {
 
    protected List alleHistorischeVersies = new ArrayList();
 
    public BiTemporalCollection() {
        super();
    }
 
    public List getActueleVersies() {
        return getVersieOp(new Date(), new Date());
    }
 
    public List getVersieOp(final Date registratieTijdstip, final Date peilDatum) {
        return (List) CollectionUtils.select(this.alleHistorischeVersies, new Predicate() {
            public boolean evaluate(Object object) {
                return ((BiTemporalObject) object).isGeldigOp(registratieTijdstip, peilDatum);
            }
        });
    }
 
    public void voegActueleVersieToe(BiTemporalObject actueleVersie) {
        if (actueleVersie == null) {
            throw new IllegalArgumentException("actueleVersie is null");
        }
        this.alleHistorischeVersies.add(actueleVersie);
    }
 
    public void wijzigActueleVersie(BiTemporalObject nieuweVersie, BiTemporalObject oudeVersie) {
        if (nieuweVersie == null) {
            throw new IllegalArgumentException("nieuweVersie is null");
        }
        if (oudeVersie == null) {
            throw new IllegalArgumentException("oudeVersie is null");
        }
        beeindigVorigeVersie(nieuweVersie, oudeVersie);
        this.alleHistorischeVersies.add(nieuweVersie);
    }
 
    private void beeindigVorigeVersie(final BiTemporalObject nieuweVersie, final BiTemporalObject oudeVersie) {
 
        BiTemporalObject afTeVoerenVersie = zoekActueleVersie(oudeVersie, nieuweVersie.getIngangsdatum(), nieuweVersie.getOpvoerTijdstip()); 
 
        if (afTeVoerenVersie == null) {
            throw new IllegalStateException("Geen oude actuele versie gevonden!");
        }
 
        beeindigVersie(afTeVoerenVersie, nieuweVersie.getIngangsdatum(), nieuweVersie.getOpvoerTijdstip());
    }
 
    public void beeindig(final BiTemporalObject actueleVersie, final Date einddatum) {
        BiTemporalObject afTeVoerenVersie = zoekActueleVersie(actueleVersie, einddatum, new Date());
        if (afTeVoerenVersie == null) {
            throw new IllegalStateException("Geen oude actuele versie gevonden!");
        }
        beeindigVersie(afTeVoerenVersie, einddatum, new Date());
    }
 
    public void beeindig(final Date einddatum) {
        List afTeVoerenVersies = (List) CollectionUtils.select(this.alleHistorischeVersies, new Predicate() {
            public boolean evaluate(Object object) {
                BiTemporalObject versie = (BiTemporalObject) object;
                return versie.isGeldigOp(new Date(), einddatum);
            }
        });
 
        CollectionUtils.forAllDo(afTeVoerenVersies, new Closure() {
            public void execute(Object input) {
                beeindigVersie((BiTemporalObject) input, einddatum, new Date());
            }
        });
    }
 
    private void beeindigVersie(BiTemporalObject versie, Date einddatum, Date registratieTijdstip) {
        BiTemporalObject kopieVersie = versie.kopieer(einddatum, registratieTijdstip);
        this.alleHistorischeVersies.add(kopieVersie);
    }
 
    private BiTemporalObject zoekActueleVersie(final BiTemporalObject actueleVersie, final Date peildatum, final Date registratieTijdstip) {
        return (BiTemporalObject) CollectionUtils.find(this.alleHistorischeVersies, new Predicate() {
            public boolean evaluate(Object object) {
                BiTemporalObject versie = (BiTemporalObject) object;
                return versie.equals(actueleVersie) && versie.isGeldigOp(registratieTijdstip, peildatum);
            }
        });
    }

De wijzigingen ten opzichte van de BiTemporalProperty uit het vorige deel zijn dat er nu niet één maar meer versies tegelijkertijd geldig kunnen zijn (getActueleVersies) en dat we versies kunnen toevoegen (voegActueleVersieToe) en kunnen wijzigen (wijzigActueleVersie). Bij deze laatste methode we de te wijzigen versie meegeven, zodat deze beëindigd kan worden en de nieuwe versie toegevoegd wordt. Ook kunnen we alle versies van de collection beëindigen (beeindig) of een enkele versie (die dan weer aan de methode meegegeven moet worden).

Een voorbeeld

In dit voorbeeld wordt het voorbeeld uit deel 3 uitgebreid met een manager (zelf ook een werknemer), die meerdere werknemers onder zich heeft.

Manager

public class Manager extends Werknemer {
 
    private static final long serialVersionUID = 1L;
 
    private BiTemporalCollection werknemers = new BiTemporalCollection();
 
    public Manager() {
        super();
    }
 
    public Manager(String naam) {
        super(naam);
    }
 
    public void voegWerknemerToe(Werknemer werknemer, Date ingangsdatum) {
        this.werknemers.voegActueleVersieToe(new WerknemerRelatie(werknemer, ingangsdatum));
    }
 
    public List getTeManagenWerknemers() {
        List relaties = getWerknemerRelaties();
        CollectionUtils.transform(relaties, new Transformer() {
            public Object transform(Object object) {
                return ((WerknemerRelatie)object).getWerknemer();
            }
        }) ;
        return relaties;
    }
 
    public List getTeManagenWerknemers(Date registratieTijdstip, Date peildatum) {
        List relaties = getWerknemerRelaties(registratieTijdstip, peildatum);
        CollectionUtils.transform(relaties, new Transformer() {
            public Object transform(Object object) {
                return ((WerknemerRelatie)object).getWerknemer();
            }
        }) ;
        return relaties;
    }
 
    public List getWerknemerRelaties() {
        return this.werknemers.getActueleVersies();
    }
 
    public List getWerknemerRelaties(Date registratieTijdstip, Date peildatum) {
        return this.werknemers.getVersieOp(registratieTijdstip, peildatum);
    }
 
    public void wijzigWerknemer(Werknemer nieuweWerknemer, Werknemer oudeWerknemer, Date wijzigingsdatum) {
        WerknemerRelatie nieuweRelatie = new WerknemerRelatie(nieuweWerknemer, wijzigingsdatum);
        WerknemerRelatie oudeRelatie = bepaalWerknemerRelatie(oudeWerknemer);
        this.werknemers.wijzigActueleVersie(nieuweRelatie, oudeRelatie);
    }
 
    private WerknemerRelatie bepaalWerknemerRelatie(final Werknemer werknemer) {
        return (WerknemerRelatie)CollectionUtils.find(this.werknemers.getActueleVersies(), new Predicate() {
            public boolean evaluate(Object obj) {
                return ((WerknemerRelatie)obj).getWerknemer().equals(werknemer);
            }
        });
    }
 
    public void stopManagenVanWerknemer(Werknemer werknemer, Date einddatum) {
        WerknemerRelatie relatie = bepaalWerknemerRelatie(werknemer);
        this.werknemers.beeindig(relatie, einddatum);
    }
 
    public void stopManagen(Date einddatum) {
        this.werknemers.beeindig(einddatum);
    }
}

Deze klasse representeert de manager. Als een manager een werknemer gaat managen, wordt de werknemer toegevoegd aan de lijst van te managen werknemers (voegWerknemerToe). Als een werknemer wordt vervangen door een andere werknemer wordt de methode wijzigWerknemer aangeroepen, waardoor de relatie met de oude werknemer wordt beëindigd en de nieuwe werknemer wordt begonnen. Als een werknemer uit dienst gaat of naar een andere afdeling, stopt de manager met het managen van deze werknemer (stopManagenVanWerknemer), waardoor de historische relatie van de manager met de werknemer wordt beëindigd. Als de manager stopt met managen (stopManagen), wordt de relatie met alle gemanagede werknemers beëindigd.

De historische relatie van de manager met de werknemer wordt gerepresenteerd door de klasse WerknemerRelatie.

WerknemerRelatie

public class WerknemerRelatie extends BiTemporalObject {
 
    private static final long serialVersionUID = 1L;
 
    private Werknemer werknemer;
 
    protected WerknemerRelatie() {
        super();
    }
 
    protected WerknemerRelatie(Werknemer werknemer, Date ingangsdatum) {
        super(ingangsdatum);
 
        this.werknemer= werknemer;
    }
 
    public Werknemer getWerknemer() {
        return this.werknemer ;
    }
}

De test ziet er als volgt uit:

public void testManager() {
 
	Manager jan = new Manager("Jan");
 
	// simuleer de opvoer tijd 1-1-2003
	Date aanvangsdatum = DateUtils.maakDate(2003, 1, 1);
	BiTemporalObject.TEST_OPVOER_TIJDSTIP = aanvangsdatum; 
 
	Afdeling inkoop = new Afdeling("Inkoop");
	jan.setAfdeling(inkoop, aanvangsdatum);
 
	Werknemer kees = new Werknemer("Kees");
	kees.setAfdeling(inkoop, aanvangsdatum);
	jan.voegWerknemerToe(kees, aanvangsdatum);
 
	Werknemer piet = new Werknemer("Piet");
	piet.setAfdeling(inkoop, aanvangsdatum);
	jan.voegWerknemerToe(piet, aanvangsdatum);
 
	assertEquals(2, jan.getTeManagenWerknemers().size());
	assertTrue(jan.getTeManagenWerknemers().contains(kees));
	assertTrue(jan.getTeManagenWerknemers().contains(piet));
 
	Date wijzigingsdatum = DateUtils.maakDate(2006, 1, 1);
	BiTemporalObject.TEST_OPVOER_TIJDSTIP = wijzigingsdatum; 
 
	Werknemer johan = new Werknemer("Johan");
	johan.setAfdeling(inkoop, wijzigingsdatum);
 
	// Johan vervangt Piet, die uit dienst gaat
	jan.wijzigWerknemer(johan, piet, wijzigingsdatum);
	piet.uitDienst(wijzigingsdatum);
 
	assertEquals(2, jan.getTeManagenWerknemers().size());
	assertTrue(jan.getTeManagenWerknemers().contains(johan));
	assertFalse(jan.getTeManagenWerknemers().contains(piet));
 
	// jan managete gisteren piet nog wel
	Date gisteren = DateUtils.maakDate(2005, 12, 31);
	assertTrue(jan.getTeManagenWerknemers(new Date(), gisteren).contains(piet));
 
	Date einddatum = DateUtils.maakDate(2009, 1, 1);
	BiTemporalObject.TEST_OPVOER_TIJDSTIP = wijzigingsdatum; 
 
	jan.stopManagenVanWerknemer(kees, einddatum);
	kees.uitDienst(einddatum);
 
	assertEquals(1, jan.getTeManagenWerknemers().size());
	assertFalse(jan.getTeManagenWerknemers().contains(kees));
 
	jan.stopManagen(einddatum);
	assertTrue(jan.getTeManagenWerknemers().isEmpty());
}

In deze test maken we een manager Jan aan, die werkt op de afdeling Inkoop op 1 januari 2003. Vanaf deze dag begint hij ook met het managen van twee werknemers Kees en Piet. Vanaf 1 januari 2006 vervangt een nieuwe werknemer Johan Piet, die uit dienst gaat. Op 1 januari 2009 stopt Jan met het managen van al zijn werknemers.

Hibernate mappings

De hibernate mapping van de klasse WerknemerRelatie is analoog aan de mapping van de AfdelingRelatie uit deel 3.

De mapping van de werknemer is uitgebreid met de subclass Manager, die de werknemers als BiTemporalCollection property mapt.

Werknemer.hbm.xml

<hibernate-mapping package="nl.ordina.bitemporal.example" default-access="field">
	<class name="Werknemer" table="Werknemer" discriminator-value="Werknemer">
		<id name="id" column="id">
			<generator class="identity"></generator>
		</id>
		<discriminator column="type" />
		<version name="versie" />
 
		<!-- properties -->
		<property name="naam" column="naam" />
 
		<!-- de verwijzing naar de afdelingrelatie -->
		<component name="afdelingRelatie" 
			class="nl.ordina.temporal.bitemporal.BiTemporalProperty"
			lazy="false">
			<bag name="alleHistorischeVersies"
				cascade="all, delete-orphan" >
				<key column="werknemerId" />
				<one-to-many
					class="AfdelingRelatie" />
			</bag>
		</component>
 
		<subclass name="Manager" discriminator-value="Manager">
		<!-- de verwijzing naar de werknemerrelatie -->
			<component name="werknemers" 
				class="nl.ordina.temporal.bitemporal.BiTemporalCollection"
				lazy="false">
				<bag name="alleHistorischeVersies"
					cascade="all, delete-orphan" >
					<key column="managerId" />
					<one-to-many
						class="WerknemerRelatie" />
				</bag>
			</component>
		</subclass>
	</class>
</hibernate-mapping>

LET OP: In bovenstaand artikel wordt gesproken over BiTemporalCollection. Dit is niet dezelfde als de BiTemporalCollection van Fowler , wat eigenlijk de BiTemporalProperty uit deel 3 is.




Websphere classloading issues

Door: Sander Abbink, 18 September 2009

Most of us who deploy on Websphere have at some point encountered classloading issues. Websphere provides a lot of libraries which are loaded by default (parent first) before the libraries supplied by the application. IBM explains the classloading order on this site:
http://www.ibm.com/developerworks/websphere/library/techarticles/0112_deboer/deboer.html

At my current position some Websphere installations have installed feature packs while others have not. You can view which feature packs are installed by running:
versionInfo.bat/sh from the Profile directory (e.g.: /opt/IBM/WebSphere/AppServer/profiles/AppSrv01/bin).
This can also cause version mismatches.

The following examples are from Websphere 6.1

Viewing classloader order:
You can view the order in which the libraries are loaded by following these links in the admin console:
Enterprise Applications > [ear module] > Manage Modules > [ war module] -> view module classloader.

You can expand the tree or export the contents of the Classloader viewer to a XML-file. Here you can easily tell if a library is provided by Websphere or your application.

Changing classloader order:
To change the classloading order follow the following link:
Enterprise Applications > [ear module] > Class loader

You can set the value of Class loader order to parent-last or application-first (depends on Websphere version) to make sure the jar-files of your application are loaded first. If the jar-files are also present in WEB-INF/lib you can alter the setting in:
Enterprise Applications >[ear module] > Manage Modules > [war module] (again parent-last or application-first)

If you cannot change these settings and you are deploying through WTP then you must change the file: application.xml.  You can enter these settings at the deployment tab (maybe only in RAD?)

Creating your own classloader
Another way of preventing Websphere to load its own libraries first is to use a shared library.Jar-files in the shared-library are automatically placed on the classpath. You can create a special classloader to make sure certain libraries are loaded before anything else:

Application servers > server1 > Class loader

Select ‘new classloader’ and choose ‘Classes loaded with application class loader first’. After you have created this classloader you can attach your shared library to this loader.  After this you can restart your application server for these changes to take effect. For me this is the cleanest solution but remember this affects all applications deployed on the server. The benefit is that you can change the classloading order on a per-library basis i.s.o. changing the order for all the libraries in the application.




Annotation based value injection in Spring 2.5

Door: Michel.Schudel, 14 July 2009

De <component-scan> Spring configuratie geeft je de mogelijkheid om je java beans te annoteren met @Component, @Service, @Repository, etc. Als Spring wordt opgestart zullen deze beans automatisch door Spring worden opgetuigd. Je hoeft dan geen beandefinitie meer in je applicationContext op te nemen. Zie ook hier voor meer informatie.

Alles goed en wel, maar wat nu als je component, behalve referenties naar andere beans, ook simpele String en Integer values nodig heeft? De enige mogelijkheid die tot nu toe had is de beandefinitie alsnog in je applicationContext op te nemen.

Spring 3.0 krijgt gelukkig de annotatie @Value waarmee je dit kan bereiken. Je kan dan je method als volgt annoteren:

@Value("#{my.property}")
public void setDezeProperty(String myProperty) {
...
}

In Spring 2.5 bestaat deze voorziening nog niet, maar Op StackOverflow heeft iemand een PropertyAnnotationPlaceholderConfigurer geschreven die precies hetzelfde doet.

In Spring 2.5 kan je dan je method als volgt annoteren:

@Property(value="my.property")
public void setDezeProperty(String myProperty) {
...
}

Waarbij my.property dan de naam van een property is die uit een property file komt.




To Roo or not To Roo

Door: philippe, 22 June 2009

Spring Roo (Milestone2) is the newest Spring brain child. This time however not a (integration) framework, but a real application generation tool.

Using a command line tool (Yes really) you not only tell Roo to generate code for you, but you also use it to modify your generated code.
For example with a simple:

create project -topLevelPackage nl.ordina.compcomp

a project is created.

And with a:

new persistent class jpa -name ~.domain.Competence

a JPA class is generated.

Adding attributes to the JPA class is also done wit a command like:

add field string description

Web interface generation is done by:

new controller automatic ~.web.CompetenceController

In short this is it. A web application with CRUD functionality. The fun part is however the code that is generated. One statement from the Spring Roo website that really intrigued me, is the ability of Roo to once generate your application and the allow you to continue without using Roo. How did the solfe the generation gap and how is it that I can continue developing the application without Roo.

So lets see what Roo has generated:

package nl.ordina.compcomp.domain;
 
import javax.persistence.Entity;
import org.springframework.roo.addon.entity.RooEntity;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.tostring.RooToString;
 
@Entity
@RooEntity
@RooJavaBean
@RooToString
public class Competence {
 
private String description;
}

Thats it. But you say where is the rest then. Or do they have some fancy runtime framework that acts on the beautiful annotations. Well yes and no. Besides the java class a number of AspectJ files are also generated. And these files contain the extra generated code. The generation gap is solved by adding functionality using AOP. The aspect files are generated over and over again. The java class file however is only generated once and can be modified by the programmer.

If you like to continue without Roo, simply tell it to Roo. What will happen is that the Roo annotation are removed from your java files and the Aspects will be merged into your class. At that point the Roo dependency is no longer needed.

Is this the way to go? Who knows must tell. I don’t. The code is still in Milestone phase so it is not written in stone and may contain some bugs or missing functionality. The reasoning behind Roo and some implementation however choices I personally find interesting.

What do you think? (more…)




The strange world of Java autoboxing

Door: Roy van Rijn, 19 June 2009

So, you’ve probably been using Java 5 for a while now..? How do you like the new features?

One of the new things is autoboxing. Java is slowly realizing that having primitives wasn’t the best idea. Having only objects instead of primitives (like modern languages, for example Scala) feels more natural and fits OO-programming.

To ease the pain Java introduced this bandage: Autoboxing.

Autoboxing means automatic translation (in some cases) of primitives to the corresponding objects.


So you can do:

int primitiveFifty = 50;
Integer objectFifty = primitiveFifty;



Even better, it allows you to automagically do int-calculations with objects:

Integer calculate = 10;
calculate += 20;



Java translates everything into int, then back to Integer without you noticing anything. This is great! (NOT!)


Autoboxing works great with simple examples like this, but it gets spooky when you use it in combination with widening and narrowing. Let me give you an example:

Byte b = 1;



This is clear, the ‘int’ 1 will be translated without noticing to a Byte. In the following example, the output is too as expected:

Byte b = null;
 
Byte n = 10;
if(false) {
    n = 1;
} else {
    n = b;
}
System.out.println(n);



The result of this piece of code will be ‘null’. Of course, the false will be executed, and then ‘n = b’ will be called. This causes n to be null.


Lets inline this if-then-else to do the same, but shorter:

Byte b = null;
 
Byte n = 10;
n = (false) ? 1 : b;
System.out.println(n);



The code is 100% the same as the example above isn’t it, we only inlined the if…? We do if(false) and the result is either ‘n = 1′ or ‘n = b’…?


Wrong!


When you execute the code you’ll get a NullPointerException!


Why is this? Let us translate the examples back to Java 1.4 code, without using autoboxing:

Byte b = null;
 
Byte n = Byte.valueOf(new Integer(10).byteValue());
if(false) {
    n = Byte.valueOf(new Integer(1).byteValue());
} else {
    n = b;
}
System.out.println(n);



As we can see, Java does a lot to go from int to Byte.

The translation of our second example is less inituative. The following ‘rules’ are from the language specification for the conditional ‘(Operand1 ? Operand2 : Operand2);’.


We have the following code in Java 5: (false) ? (int)1 : (Byte)null);


  • If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
  • If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
  • If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
    • If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
    • If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
    • If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
    • If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
    • If one of the operands is of type; Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
    • Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
  • Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).




In our case, the second (int) and third (Byte) are both convertable to numeric types. The third sub-rule applies to our case:


“If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.”


Now we can translate the inline-code to the Java 1.4 variant:

Byte b = null;
Byte n = new Byte((byte)10);
n = Byte.valueOf((false)? (byte)1 : b.byteValue());
 
System.out.println(n);



As you can see, Java tries to get the byte value in both cases. To do this on b, it needs to call .byteValue(), which results in a NullPointerException…


So how do you safely inline it? You have to make sure the two values in the conditional have the same type as the type you expect, in our case:

Byte b = null;
Byte n = 10;
 
//Make sure we have Byte = ()?Byte:Byte;
n = (false)? (Byte)(byte)1 : b; 
 
System.out.println(n);



So, be alert when using autoboxing and widening/narrowing in the same piece of code. Happy coding!




Generating Business objects with CGLIB

Door: Assen Kolov, 17 June 2009

Don’t code what you can generate

At my currrent project I ended up generating run-time proxies for my Business Objects with CGLIB. As this is somewhat unusual for a straightforward enterprise application, I still wonder about the pros and cons of this approach.

The main thing the application does is product configuration – given a definition of some tree of products, it lets the user create and edit product attributes, add, remove and edit subproducts according to that definition. It is a requirement that there is an external definition and it is expected to change regularly, so as much as possible of the product logic should be defined there.

A fragment of some imaginary domain objects definition to illustrate the case:

<productdefinition id="someProduct">
 <date id="startingDate" nullable="false">
 <enumeration id="area">
  <enum default="true" value="World">
  <enum value="Europe">
 </enumeration>
 <boolean id="allInclusive" defaultvalue="false">
 <range id="limit" min="10" max="5000" step="10" defaultvalue="1250">
 <subproduct type="someSubproduct">
  <multiplicity mincount="0" maxcount="1" defaultcount="1"/>
 </subproduct>
<productdefinition>

<productdefinition id="someSubproduct">
 <text id="reason" pattern="\a{1,30}"/>
</productdefinition>

Whatever the Java implementation:

  • When creating SomeProduct some values have to be set initially: setArea(‘World’), setLimit(1250) etc. A properly initialized instance of SomeOtherProduct should be added as a subproduct.
  • someProduct.setStartDate(null) or someProduct.setArea(“Japan”), someProduct.setLimit(10000) should all cause an error with an error code because the provided value is not acceptable according to the definition.
  • Rendering a GUI for editing a product attribute and processing the user input is uniform for all product attributes
  • etc. etc.
  • An intuitive approach was to try something PropertyBean-like with named properties:

    interface Product {
     Object getPropertyValue( String propertyName);
     void setPropertyValue( String propertyName, Object propertyValue);
     AttributeDefinition getAttributeDefinition( String propertyName);
    . . .
    }
    
    class ProductImpl implementing Product { . . . }

    The implementation is straightforward and a lot of code fits this model perfectly. Still, there is code in the use case implementations which looks somewhat clumsy and prone to errors with this interface:

    someProduct.setPropertyValue("someSubproduct[0].reason", "Lousy service");

    An example without nested properties looks even more awkward, you can imagine. Forget code completion, type checking, tool refactoring facilities. The natural Java way of doing this is:

    someOtherProduct.getSomeProduct().get(0).setArea("Europe");

    And you need to invent some trics to write the equivalent of:
    <form:input path=”someProduct.someSubproduct[0].reason”/>

    The interface that I actually would like for the use case implementations is:

    interface SomeProduct {
     Date getStartingDate();
     void setStartingDate( Date date) throws BadValueException; // Null not accepted
     List getSomesubproduct();
     SomeSubproduct addSubproduct();
     void deleteSubproduct( Subproduct p);
    . . .
    }
    
    interface SomeSubproduct() {
     String getReason();
     void setReason( String reason);
    }

    Given existing ProductImpl instances, coding the implementations of those interfaces would be a repetitive task, all getter/setters/addXXX/deleteXXX etc. will look alike. A code generation library could generate this trivial code for us. CGLib is a code generation library (a lot of tautology here) and I know it is good enough for Hibernate, so I thought it should do the job for me too. With a productImpl instance and a desired interface, the CGLib code needed to produce the proxy is surprisingly little:

    class ProductProxyFactory {
    
     public static Object createProxy(ProductImpl productImpl, Class clazz) {
      Enhancer e = new Enhancer();
      e.setSuperclass(clazz);
      Callback callback = new ProductProxyCallback(clazz.getSimpleName(),
        productImpl, productMapping);
      e.setCallback(callback);
      return e.create();
     }
    }
    
    public class ProductProxyCallback implements MethodInterceptor {
     private ProductImpl productImpl;
    
     public Object intercept(Object object, Method method,
                             Object[] args, MethodProxy  proxy) throws Throwable {
      String methodName = method.getName();
      if ( methodName.startsWith("get")) {
        String propertyName = findMappedPropertyName(methodName); // find mapped property from method name
        Object propertyValue = productImpl.getPropertyValue(propertyName );
        Class subProductClass = findSubProductClass(propertyName ); // property value might be a product too
        if (subProductClass != null) {
          return createSubproductProxy(result, subProductClassName, mapped);
        } else {
          return result;
      } else {
       // Process setXXX, addXXX, deleteXXX methods and methods inherited from Object
     . . .

    Let’s see an example:

    // There is some old code that still creates product using the clumsy Product interface
    ProductImpl productImpl = new ProductImpl( someProductDefinition);
    // and sets some data
    productImpl.setProperty( "startDate", today);

    // Let’s create a proxy implementing SomeProduct interface:
    SomeProduct someProduct = (SomeProduct)createProxy( productImpl, SomeProduct.class);
    Date date = someProduct.getStartingDate()

    The code above is schematic, of course, in reality the application does not have to create any proxies explicitly. Not bad, the old classes have received a new face – another interface to the same data, and the only code that has to be written for each new interface is the interface definition itself.

    Pros & Cons

    - This approach adds complexity. This part of the application has confused more than one project members joining the team – ‘Hey, why can’t I find the implementation of SomeProduct?”. Is it worth maintaining this extra complixity for the needs af small audience – the developpers of one application?
    - The code above is schematic, the real implementation has to take care of more details – external mapping file, caching proxy instances etc. The real code is already more complicated than the example and could potentially become too complicated, as business analysts add new requirements to the application.
    - Object identity issues pop up if you use proxies and this case is not different – it is possible to create many proxies working on the same underlying productImpl.
    - Some OOP has gone here. Try to override SomeProduct.getStartDate() or to set a breakpoint on it. I guess it happens when you mess up with AOP.

    Let’s not forget the pros:

    - No typing, retyping and copy-pasting of trivial code or introducing errors there. Changing a product is limited to editing tha Java interface and a mapping file.
    - There is one well-defined place where all products are defined. There is one well-defined class where the behaviour of all products is defined, and no lazy or evil programmer can override it.

    Is that DSL?
    I wonder how this approach relates to the DSL concept of trading generality for expressiveness. The starting point is undoubtedly a domain-specific XML. While many DSL tools emphasize the code-generation phase, here objects are generated at run time. An obvious difference is that one can not inspect/change/override the generated code. While this is a DSL according to scholars (see http://ftp.cwi.nl/CWIreports/SEN/SEN-E0309.pdf), it is probably not the sort that is hot right now. Would it be more DSL if Java code was generated? I am curious on your thoughts on this.




JavaOne 2009 started!

Door: Stephan Oudmaijer, 2 June 2009

JavaOne 2009 has just started. The worlds leading Java conference will be held from 2-5 June in San Franciso (US). If you are interested you can follow the general sessions via the JavaOne homepage, due to the time difference in Europe you can watch and listen to the general sessions around dinner time. Check it out: http://java.sun.com/javaone/

I`ve just finished watching the opening session which contained a lot of promotion for JavaFX. Sony talked about Java enabling interactive Blu-ray applications. Verizon anounced a partnership with Sun for  applications for their wireless network.

Larry Allison, CEO of Oracle, was the last guest speaker. He talked about how excited he was about the Oracle takeover of Sun and Java being the key platform for current and future Oracle (Fusion) products. He also mentioned that Sun and Oracle maybe hitting the mobile/nettop market and launching a Google Android alike platform.

I`ll be posting some JavaOne updates this week.




Book: “The Inmates are Running the Asylum”

Door: Vincent Hartsteen, 17 May 2009

In my previous posting Can software developers build usable GUI’s? I mentioned that I was reading The inmates are running the asylum written by Alan Cooper. During my vacation in Italy last May I’ve finished reading the book. The fact that I read this book while I was relaxing in the sun on a campsite at Lake Garda may sound like a detail that isn’t worth mentioning. But actually I think it is. I’ve tried to read IT related books before while being on vacation but I never (I repeat, never) managed to finish the book. Dan Brown or John Grisham can keep my attention during a vacation but Martin Fowler or Mark Cade fail to do so. But Alan Cooper has written The inmates… in a very entertaining manner and I have enjoyed reading it.

But what is the book about? Just like About Face. Essentials of User-Interface Design the book is about the usability of software based products. In The inmates are running the asylum Alan Cooper focusses on the role of the Interaction Designer (which should not be confused with a GUI Designer). He states that in many projects the focus is on adding features instead on fulfilling (real) end-user goals. The reason for this is the way in which software-engineering is organized. The inmates (that is us, the developers) are running the show (and we shouldn’t) according to Alan Cooper.

Many software-based products are so-called Dancing Bearware. At first it is fun to see a Bear moving around as if it is dancing. But after a while you’ll notice that the bear isn’t dancing that good. And is it really dancing? The same goes for many software-based products. In a demo it really looks nice and seems to work fine. But after a while when you have a closer look you’ll notice that working with the product isn’t that fun anymore. Recognizeable? To me it is (have a look in the box with all the nice gadgets you had over the last few years…).

Cooper gives many examples of where software-based products fail to support its end-user. However he doesn’t leave it at that. He points out where in his view the real problem lies and what can be done about it. He describes a method that his company uses to improve the interaction between humans and software-based products. After explaining the basics of this method (where he introduces personas) he gives some examples of projects where he used his method. In my opinion this method can be combined with RUP (personas instead of human-actors). Coopers method isn’t the silver bullit with which we are guaranteed to build perfect applications. But it shows that software-based products should also be looked at from a point-of-view that isn’t natural for programmers. This is where the Interaction Designer comes into play and where the organization needs to be changed so that this role is fitted in the correct spot. And they should be running the asylum, or at least have a lot more influence, instead of us…

Like I said before, the style of the book is very entertaining and on top of that it gives a nice insight in how software-based products can fail. I would suggest this book in addition to all the hard-core technical books we have on our bookshelves.




Oracle neemt Sun over

Door: Stephan Oudmaijer, 20 April 2009

Oracle en Sun hebben een akkoord bereikt over de overname van Sun door Oracle. Lange tijd leek het erop dat IBM een van de belangrijkste kanshebber was om Sun over te nemen. Echter konden IBM en Sun het niet eens worden over de voorwaarden waaronder de deal door zou gaan en de prijs per aandeel. Oracle betaalt de aandeelhouders van Sun $9.50 per aandeel, hetgeen neerkomt op een deal van $7.4 billion dollar. Dat is  $0.10 per aandeel meer dan IBM bereid was om neer te tellen.

De overname van Sun door Oracle levert wel een aantal interessante vraagstukken op, wat gaat Oracle bijvoorbeeld doen met Java? Op dit moment is er nogal wat onenigheid over het Java Community Proces (JCP), het standardisatie proces voor Java. Velen hopen dat Oracle de JCP nu echt open zal maken. Anderen zeggen dat er geen sprake van een inspraak proces is. Aan de andere kant, misschien is het wel niet in het belang van Oracle om Java nog opener te maken om het zodoende concurrent IBM lastiger te maken?

Verder zijn er nog NetBeans en  GlassFish? Aangezien Oracle BEA heeft overgenomen, bekend van o.a. de applicatie server WebLogic, is er bij Oracle een overschot aan applicatie servers ontstaan. Zelfde geldt voor ontwikkelomgeving NetBeans vs JDeveloper, zullen beide naast elkaar blijven bestaan of worden ze samengevoegd?

Daarnaast is Oracle plotsklaps eigenaar geworden van MySQL, door velen gezien als het open source alternatief voor Oracle. Moeten we ons nu gaan verdiepen in PostgreSQL? ;)

Voor het orginele persbericht zie: http://www.oracle.com/corporate/press/2009_april/018363.htm




Can software developers build usable GUI’s?

Door: Vincent Hartsteen, 17 April 2009

Most developers would answer this question the same way that Bob the Builder would when asked if he can fix it. “Yes I can” would be the reply. And I’m sure that Bob can. Why? Because Bob does his building based on a blueprint that tells him exactly what to build and what to use for building it.

In construction the architect is responsible for producing the blueprint. The architect talks to the customer in order to get a list of requirements for the building. For instance is the building used as an office-space or is it used as a family-home. Should it be a single or multi story building and how many rooms should it have. And of course the amount of money the customer is willing to spend. Geared with the list of requirements the architect uses his design skills and his technical knowledge to make a first sketch of the building. The architect discusses this sketch with his team of electrical, mechanical and structural engineers to find out if it feasible from a technical point-of-view. For instance is the construction mechanically strong enough, are the selected materials useable, etcetera. This should all result in a blueprint for a building that meets the customer’s requirements, is usable with respect to the needs of the customer and is aesthetically pleasing. Finally the constructionworkers take the blueprint and start building the construction.

In software development some similar process takes place. In short the information analist and the customer discuss about the requirements for the system. What functionality should the system offer, what is the expected load etcetera. The information analist works in close cooperation with the software architect. The software architect is responsible for making the technical decisions (which frameworks to use, what platform, how many nodes, etc.) such that the non-functional requirements (scalability, extensibility, manageability, etc.) can be met. The UML-artifacts created by the information analist and the software architect (e.g. use-case model, software architecture document, use-case realizations, etc.) are input for the developers.

One major difference between the blueprint for the construction workers and the UML-artifacts for the developers is that the blueprint has usability embedded. The architect has knowledge about how to make a construction usable to its users. When there is a requirement that a house must have 2 bathrooms the architect knows that to make them usable he could place one on the groundfloor and the other on the first floor. He could have fulfilled the requirement by putting both bathrooms on the attic but that would not have made them very useable. Very often a developers gets a requirement which basically comes down to: “the application must have a web-interface or a GUI and it should be user-friendly”. That’s it. So now it is up to the developers to figure out what “user-friendly” is.

Most developers have a technical mindset and find it hard to step into the role of the end-user. It is the end-user that decides if the application is user-friendly. He does so when the application supports the end-user to do his job without problems. So in order to design a user-friendly interface it must be clear what the application is used for and how it is used. Very often user-friendlyness is misconceived as “protect the end-user from making mistakes”. If end-users use the application every now and then to do some work it can be valid to pop-up a confirmation dialog. Expert users that use the application on a regular basis get annoyed by such dialogs.

There are currently lots of tools/frameworks that allow developers to build goodlooking user-interfaces: JavaFX, Flex, SAF, JSF, Wicket, and the list goes on. But goodlooking is not the same as user-friendly. With the tooling mentioned before we could build a very nice 3D, animated, gradient color dialog box that is very annoying (remember the Office paperclip?). So basically we have the tools to create cool and goodlooking user-interfaces but most of us lack the knowledge of how to build usable ones.

My point is that there should be a role in the softwaredevelopment team, just like a information analist or a software architect, that is responsible for designing the interaction with the end-user. Just like the architect in construction. I haven’t come across a person with such a role in the many projects I’ve worked on over the past years. Until that time it is up to us developers to design user-interfaces and for that we need to broaden our horizon and try to place ourselves on the end-user seat.

A few years ago I read “About Face. The Essentials of User Interface Design” (first edition) written by Alan Cooper which got me interested on this subject (3rd edition is the most recent version). The book describes the problems that occur when developers start designing user-interfaces and give advice on how to improve on that, all illustrated with entertaining examples. If you get into the situation where you as a developer are responsible for the design of the user-interface this book might help you do a better job.

Vincent Hartsteen

N.B.: Currently I’m reading “The Inmates are running the Asylum”, also by Alan Cooper. This book describes the role of “interaction designers”. I’ll write my findings when I’ve finished the book.