Monday, October 18, 2010

Creating a MathQuestion

In the previous post I talked about information hiding in Context.

In this post I show a simple component that is using that feature. The component makes a mathematical question and let's the user answer to it. The functionality is following:

  • Question contains two integer values and an operation (+ or -)
  • New values and operation is randomly generated when component is created
    or if user asks for it.
  • When user answer to the question, a message of success or failure is shown

The example shows that it is only needed to send the answer back to the server when answered. The original values and the operation are already there.

So, let's begin. First I decided to model the value handling as enum
enum Operation {
        
        ADDITION("+") {
            @Override
            public long calculate(long val1, long val2) {
                return val1 + val2; 
            }
        },
        
        SUBTRACTION("-") {
            @Override
            public long calculate(long val1, long val2) {
                return val1 - val2; 
            }
        };
        
        private final String name;
        
        private Operation(String name) {
            this.name = name;
        }
        public abstract long calculate(long val1, long val2);
    }

Next the component is needed. First let's create a skeleton of all properties it needs:

public class MathQuestion extends EnhancedElement {
    
    @EmbeddedElement
    private SimpleLabel answer = new SimpleLabel();
    
    private Operation operation;
    
    @EmbeddedAttribute
    private long val1;
    
    @EmbeddedAttribute
    private long val2;
    
    @EmbeddedAttribute
    public String operation() {
        return operation.name;
    }
}

So the component contains the values and the operation as text which are shown to the user.

That component contains also premade component SimpleLabel that is used to contain the actual answer. You may find similar example from tutorial.

Next let's add the rest.
public MathQuestion() {
        tryAgain();
    }
    
    @RemoteMethod
    public void tryAgain() {
        Random rand = new Random();
        val1 = rand.nextInt(201) - 100;
        val2 = rand.nextInt(201) - 100;
        operation = Operation.values()[rand.nextInt(2)];
        answer.setValue("");
        refresh();
    }
    
    @RemoteMethod
    public void guess(Long guess) {
        if (guess != null) {
            if (guess == operation.calculate(val1, val2)) {
                answer.setValue("Your answer was correct :-)");
            } else {
                answer.setValue("Your answer was incorrect :-(");
            }
            answer.refresh();
        }
    }

Now, I have added to remote methods that can be called from the client. The tryAgain() simply resets the values and refreshes the component. The guess() takes a guess from the client and does the calculation to check if the answer is correct. Then it sets a proper message to answer-label and refreshes it.

Ok, that was the Java-side. Next the template is needed:

<xsl:template match="MathQuestion">
  <div id="{@id}">
    <xsl:call-template name="MathQuestion" />
  </div>
</xsl:template>

<xsl:template match="MathQuestion.update">
  <replaceInner id="{@id}">
    <xsl:call-template name="MathQuestion" />
  </replaceInner>
</xsl:template>

<xsl:template name="MathQuestion">
  <xsl:value-of select="@val1" />
  &#160; 
  <xsl:value-of select="@operation" />
  &#160;
  <xsl:value-of select="@val2" />
  &#160; = &#160;
  <input type="text" size="3" id="{@id}_guess" />
  
  <input type="button" class="button" value="Answer"
    onclick="contextfw.call('{@id}', 'guess', $('#{@id}_guess').val());" />
  
  &#160;<xsl:apply-templates select="answer" />
  <br/>
  <input type="button" class="button" value="Try again"
    onclick="contextfw.call('{@id}', 'tryAgain');" />
  
</xsl:template>

That is about it. The template is quite straight forward. All the properties that were bound as attributes are shown, and elements are put to the desired places. Then with some jQuery answer is read from input and then the request is made.

Thursday, October 14, 2010

Context and Tapestry 5 compared

This article compares Tapestry 5 to Context. I have chosen Tapestry for the comparison, because I have some experience in it and to me it is a kind of benchmark on modern component based frameworks on Java-world. You may have a different opinion of course.

On the side note, comparison between Context and Wicket might also have been interesting, but since I have no experience in Wicket I am not able to do it.

In overall these two frameworks have very different approaches. Where Tapestry focuses on page pooling and page component reuse, Context chooses an approach where page component are not reused at all.

I would also like to note that this comparison is limited and it is not to start flame war. I try to be objective, however my opinion is of course biased and favors Context.

So, let's begin with similarities:

Dependency injection


Tapestry has it's own dependency mechanism and Context is using Guice. They both have about same power, although I have a feeling that Guice is more flexible than Tapesty IoC. This leads to similar kind of design choices for service layer components and they will probably look about the same.

Next, the differences, and the are a lot of them

Page component handling


Context is taking an approach where, for each new page request, a set of brand new components are created. When the page is closed, those components are thrown away.

In tapestry, very little is "wasted" and page components are reused as much as possible. Now, I do not know what kind of burden for Tapestry it is to create new page components, but I feel it a kind of unnecessary, because in real life applications maybe hundreds of objects are created per request anyway. Just think about how many objects is needed to create a single call through Hibernate, when it actually returns something.

In Context the page components live as long as the page is visible and for page updates there is no need to create new objects. They are reused. This leads to another difference.

Page statefulness


When I began creating Context, I had a very clear vision about that pages should be stateful. And I mean each single page instance should have its own state. I understand that it fights against REST-kind of thinking and so on, but I found it very necessary. This means that each page has a transparent state, that can be used just like that.

This approach has many advantages.

Browser tabs

One thing that has been bothering with other component based frameworks is that they do not play well with browser tabs. Think about a page that is counting how many times a button is pressed in that current page.

If that page is opened in two browser tabs and the count is stored in session, you may end up having a very strange looking behavior if that button is pressed in different browser tabs.

Information hiding

Think about a page that is making a question which is dynamically generated for each page request. Where is the correct answer stored? If the page has no state, the correct answer need to be encoded to the HTML somehow, possibly revealing it. With Context, you can safely leave to answer to the server, and then simply check whether the answer was correct or not.

This can be useful also in situations where there exists an id or other kind of secret that should not revealed at any times. With Context it is easy to create a surrogate ids that are shown in HTML and can be matched to correct ids on server side.

Tapestry seems to have more limited choice of states. It basically has session context that can be used for more permanent storage or in Flash-mode where the object is stored for a short period of time. Tapestry also has a kind of page state, but that is encoded into URLs and forms. If feel that kind of clunky.

Page updates


Context is built from the ground up to be Ajaxed. There does not exist a fallback mode where Javascript is not needed. Tapestry on the other hand can be used via form submits and basic page updates that do not require Javascript. I have had some ideas how to mimic page update via post submits, but nothing concrete exists.

The interesting thing is the Ajax-side. How is that handled from developer perspective?

Tapestry has concept called Zone. Zone is normally a div-element with specific id. From the Java-code it is possible to access that zone and make an update to it. When a request is made a method, mapped in the template files, is called by Tapestry. Then that method must return the zone-access back to Tapestry so that it knows what to update.

How about if multiple zones need to be updated? Well then the page needs to create a multizone-update an gather all the zones that are needed and return them all.

The problem with that approach is that the page component needs to take care of all the updates, and I feel that it limits developer creativeness.

Context is taking a totally different approach. Components are independent. All that needs to be done is to register all components to the page context, which is in most cases done transparently, and then call its refresh()-method. The underlying framework will do the rest.

The preferred way is even to do so that the refresh()-method is not called externally at all. I mean, why should other component be responsible for knowing when the component needs to be refreshed? You simply call some method on the component that has an effect to its state, and then the component can choose itself, whether to refresh or not. Maybe the data was invalid or null and refresh is not needed.

With Context it is easy to create page wide components that can be called from every single other component and ask it do to something.

A very good example can be seen on Context-homepage. On the right hand side there exists a link "Show page comments". If you add a new comment in the opened component, you will get a feedback saying "Thank you for your page comment". That Feedback-component can be called from any other component on the page and simply ask "Please, show this message to the user". The caller does not have to know how it is really done. Also doing the Feedback-component took only about a half an hour to do. And even then most of the time I used trying too make it look good with css and graphics.

At the moment, I do not have enough experience to know how the page comment feature could be done with Tapestry so I can not say whether it is difficult or not.

But, this is enough for now. Maybe later I will continue this comparison. Thank you for reading this blog post.

- Marko Lavikainen

Sunday, October 10, 2010

Context version 0.5.1 released

This release contains some fixes. Changes are:

Quickstart-project


The archetype was pointing to version prior to 0.5.0. Now it points correctly to 0.5.1

Annotation @EmbeddedCollection removed


There existed an annotation called @EmbeddedCollection and it was meant to inject automatically collections to DOM-tree. However, this idea never really became a good one, because I could not find a proper way to make it flexible enough.

So, that annotation was removed and replaced with CollectionElement which implements the CSimpleElement. That element has the needed flexibility.

ObjectBuilder fixed


ObjecBuilder was fixed so that elements and attributes with null values are completely ignored from DOM-tree. This allows the distinction between empty and null values.

Homepage


It is now possible for everyone to add page comments in the homepage. Simply click "Show page comments" and you can read comments and add new ones. I may also give replies to them.