Wednesday, May 23, 2012

Wicket Notification Dialog - JavaScript refactoring

This blog contains update to Wicket - Disappearing Notification Dialog post.
I've moved JavaScript from Java code to separate file - it is in general good patern to not mix up those.


Previous post has following JavaScript:
effectBehavior.setCallback(new JQueryAjaxBehavior(this) {

    @Override
    public CharSequence getCallbackScript() {
        String fadeOut = "function(){$('" + dialogContainerId + ":visible').fadeOut();}";
        String callbackScript = "setTimeout(" + fadeOut + ", "
                        + Long.toString(config.getNotificationDisplayTimeMilis()) + ");";
        return callbackScript;
    }
});
Now we will move this JavaScript to notifier.js file, define there JS functions and call those functions from Java code.
notifier.js
function fadeOutDialog() {
    $("${dialogContainerId}:visible").fadeOut();
}

function playBounce() {
    setTimeout(fadeOutDialog, "${notificationDisplayTimeMilis}");
}
and modified getCallbackScript method:
effectBehavior.setCallback(new JQueryAjaxBehavior(this) {

    @Override
    public CharSequence getCallbackScript() {
        return "playBounce()";
    }
});
Additionally to execute playBounce() from notifier.js we need to register it on our HTML page and pass arguments  ${dialogContainerId} and ${notificationDisplayTimeMilis}
We will create JavaScriptTemplate as global variable in Notifier class, and overwrite renderHead  method which will append JavaScript into page header and also will set required properties:
    private JavaScriptTemplate notifierJs = 
      new JavaScriptTemplate(new PackageTextTemplate(Notifier.class, "notifier.js"));

    @Override
    public void renderHead(IHeaderResponse response) {
        Map<String, String> cssVariables = new HashMap<String, String>();
        cssVariables.put("dialogContainerId", dialogContainerId);
        cssVariables.put("notificationDisplayTimeMilis", 
                   Integer.toString(config.getBounceTimeMilis()));
        response.renderString(notifierJs.asString(cssVariables));
    }

Monday, May 21, 2012

Apache HTTP Commons and Cookie support for REST Services

Apache HTTP commons is a framework for managing client HTTP connections - compared to standard Java Sockets it offers easier way to handle persistent connections, pooling, and high level socket API. It also support cookies - and I would like to concentrate on this subject now, especially in REST context.

This is the typical way how most Java applications are using HTTP client. 
public class RestClient {

    static HttpClient httpClient = null;
    static {
        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager();
        httpClient = new DefaultHttpClient(cm);
    }

    public HttpResponse executeGet(URI url) throws Exception {
        return httpClient.execute(new HttpGet(url));
    }
}
For the most cases it is also a preferred way - HttpClient is thread save, and keeping single instance saves resources and increases performance.

Bu there is a catch, if your have a multi-thread server application, which shares single instance of HttpClient and you have to also manage cookies.
Multiple threads use single HttpClient to connect to different URLs at the same time - HttpClient is thread save, so this is allowed - but have you checked how DefaultHttpClient manages cookies? It simulates browser, but in our case this browser is being used simultaneously by several users....

HttpClient executes call against remote REST service and in response it receives a Cookie - this Cookie is valid for given domain and path and our request was executed on concreate URL - everything looks normal. When we use the same instance of HttpClient to execute another call on the same URL it will send back cookies, that were returned by previous response. If cookies were issued for sub-domain or root path, they will be also send. This is the way how normal browser would behave. And here is a catch! Our HttpClient behaves like browser but we are using it in multi-thread environment, where we communicate with many different services within different scopes - now this can have advantages, but it can be also source for serious problems.

Lets consider few scenarios - we will use as example a small application which executes calls for different users, each one is identified by integer ID (URI part).

Scenario A  - cookies with root path
HttpClient client sends request to http://registration.foo.com/register/234234, as response it receives some data and also cookie:

Set-Cookie: USER_ID=234234; Expires=Thu, 01 Jan 2049 00:00:01 GMT; Path=/; Domain=.foo.com

Request above stored cookie in HttpClient instance - its CookieStore exactly. Some time later the same instance of HttpClient is being used to execute following request: http://registration.foo.com/register/333222. Since domain and path for request match, response to this request will forward cookie which was set by previous call.
The question is - do you really wanted to send this cookie back within this response (different User ID in URL)?
Actually we have just used standard HttpClient and the rest was caused by service that we've called - it set cookie on wrong path, which did not contain  User ID. But still, this might be an issue - depends how this cookie will be interpreted - it might be used as User ID instead of ID from URL (nasty, but hey - it's REST - there are no rules).

Scenario B - identical URL for different context (users)
We call REST service which uses for all methods the same URL, User IDs are part of PUT/POST request's body. In this case our multi-thread application will use the same URL to execute calls for different users. HttpClient will send back all cookies for each request! Message body contains different User ID, but this is not relevant for CookieStore - only URL matters. Now imagine what happens when our REST interface will try to use cookie to open HTTP session - yes - we would mix up sessions.



Using single HttpClient is in general good idea, but we have to be careful when it comes to cookies. Most REST services do not use them, but in this case we should simply disable cookie support, just to be sure.

We can also create new instance of HttpClient for each request (conversational state). This is just fine, until you use shared ThreadSafeClientConnManager instance for each new HttpClient.


Monday, May 14, 2012

Wicket - Notification Dialog Component

In previous post I've given example of Info Dialog, which is being triggered trough AJAX request, shows notification message over current HTML page, bounces few times and finally disappears.
Java Code below contains the same dialog, but this time it's being re-factored to reusable component - the Notifier.

The Notifier can be included in parent page for all other pages, this parent page will have show-dialog method, which will forward calls to our Notifier panel.


Abstract parent class for all pages - MyParentPage.java

public abstract class MyParentPage extends WebPage {

  private Notifier notifier;

  public MyParentPage() {
    notifier = new Notifier("notifierDialog", new NotifierConfig());
    add(notifier);
  }

  protected void showNotification(AjaxRequestTarget target, String infoHeader,
          String infoContent) {
    notifier.showNotification(target, infoHeader, infoContent);
  }
}


MyParentPage.html

<html>
<body>
  <div wicket:id="notifierDialog" />
  <div class="xyz"></div>
  <wicket:child />
</body>
</html>


Notifier Component - Notifier.java

/**
 * Notify Dialog component - add this panel to any wicket page in order to support ajax
 * notifications.
 * 
 * Integration example:
 * 
 * 1) Add Notifier to markup in your WebPage or Panel
 *    Notifier notifier = new Notifier("notifierDialog", new NotifierConfig());
 *    add(notifier);
 * 
 * 2) Add Notifier as div in corresponding HTML page - on the top of the page
 *    <div wicket:id="notifierDialog" />
 *  
 * 3) In order to show info message call:
 *    #createNotifierMessage(WebMarkupContainer)
 * 
 * @author mmiklas
 */
public class Notifier extends Panel {

  /** Panel configuration */
  private NotifierConfig config;

  /** Parent for all display widgets in this dialog */
  private WebMarkupContainer dialogContainer = null;

  /** HTML ID of {@link #dialogContainer} */
  private String dialogContainerId = null;

  /** Dialog header model - use it to replace dialog's header message */
  Model<String> headerModel = null;

  /** Dialog content model - use it to replace dialogs content text */
  Model<String> messageModel = null;

  /**
   * Displays notification dialog with given header and message.
   * <p>
   * Dialog display command is being rendered as AJAX response
   * {@link AjaxRequestTarget#appendJavaScript(CharSequence)}. This response contains
   * dialog body, which will replace currently empty "notifier"-div, and also jquery
   * code, which plays bounce effect and hides dialog after one second.
   */
  public void showNotification(AjaxRequestTarget target, String header, String message) {
    Validate.notNull(target, "target");
    Validate.notNull(header, "header");
    Validate.notNull(message, "message");

    // update text models
    headerModel.setObject(header);
    messageModel.setObject(message);

    // replace empty #notifier with real content
    target.add(dialogContainer);
    dialogContainer.setVisible(true);

    // after setting dialog to visible play bounce effect
    JQueryEffectBehavior effectBehavior = new JQueryEffectBehavior(dialogContainerId,
            "bounce", config.getBounceTimeMilis());

    // dialog should disappear after one second - I did not
    // find any better way to add callback java script.
    effectBehavior.setCallback(new JQueryAjaxBehavior(this) {

      @Override
      public CharSequence getCallbackScript() {
        String fadeOut = "function(){$('" + dialogContainerId + ":visible').fadeOut();}";
        String callbackScript = "setTimeout(" + fadeOut + ", "
                + Long.toString(config.getNotificationDisplayTimeMilis()) + ");";
        return callbackScript;
      }

      @Override
      protected JQueryEvent newEvent(AjaxRequestTarget target) {
        return null;
      }
    });

    target.appendJavaScript(effectBehavior.toString());
  }

  public Notifier(String id, NotifierConfig config) {
    super(id);
    Validate.notNull(config, "config");
    this.config = config;

    // div containing notify dialog
    dialogContainer = createDialogContainer();
    add(dialogContainer);

    // HTML ID for java script references
    dialogContainerId = "#" + dialogContainer.getMarkupId();

    // initialize jquery
    initEffectLib(dialogContainerId);

    // labels building info dialog
    headerModel = createNotifierHeader(dialogContainer);
    messageModel = createNotifierMessage(dialogContainer);
  }

  /**
   * @return body of the info dialog. HTML ID: "notifierMessage"
   */
  private Model<String> createNotifierMessage(WebMarkupContainer dialogContainer) {
    Model<String> model = new Model<String>();
    Label notifierMessage = new Label("notifierMessage", model);
    dialogContainer.add(notifierMessage);
    return model;
  }

  /**
   * @return header of the info dialog. HTML ID: "notifierHeader"
   */
  private Model<String> createNotifierHeader(WebMarkupContainer dialogContainer) {
    Model<String> model = new Model<String>();
    Label notifierHeader = new Label("notifierHeader", model);
    dialogContainer.add(notifierHeader);
    return model;
  }

  @Override
  protected void onBeforeRender() {
    super.onBeforeRender();

    // showNotification(....) renders #notifier (div) with info dialog
    // content. Java script will hide this dialog on client side after one second
    // (callback after bounce effect).
    // Page refresh would re-render whole HTML page, this would include in this case
    // also #notifier containing recent dialog - wicket component remembers last ajax
    // update on #notifier - and this is the whole dialog.
    //
    // Setting visibility to false on #notifier ensures, that old dialog will not
    // re-appear on page refresh
    dialogContainer.setVisible(false);

  }

  /** Initializes jquery effects library */
  private void initEffectLib(String infoDialogId) {
    add(new JQueryBehavior(infoDialogId, "effect"));
  }

  /**
   * @return hidden dialog container. It must be rendered as empty div, in order to
   *         replace it with info dialog content
   */
  private WebMarkupContainer createDialogContainer() {
    WebMarkupContainer container = new WebMarkupContainer("notifier");
    container.setOutputMarkupId(true);
    container.setOutputMarkupPlaceholderTag(true);
    container.setVisible(false);
    return container;
  }
}


Notifier Config Class

public class NotifierConfig {

  private int notificationDisplayTimeMilis = 1000;

  private int bounceTimeMilis = 500;

  public int getNotificationDisplayTimeMilis() {
    return notificationDisplayTimeMilis;
  }

  public void setNotificationDisplayTimeMilis(int notificationDisplayTimeMilis) {
    if (notificationDisplayTimeMilis < 0) {
      return;
    }
    this.notificationDisplayTimeMilis = notificationDisplayTimeMilis;
  }

  public int getBounceTimeMilis() {
    return bounceTimeMilis;
  }

  public void setBounceTimeMilis(int bounceTimeMilis) {
    if (bounceTimeMilis < 0) {
      return;
    }
    this.bounceTimeMilis = bounceTimeMilis;
  }

}


Notifier.html

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">

<wicket:head>
    <wicket:link>
        <link rel="stylesheet" type="text/css" href="notifier.css" />
    </wicket:link>
</wicket:head>

<body>
    <wicket:panel>
        <div wicket:id="notifier" class="notifier-content">
            <div wicket:id="notifierHeader" class="notifier-header">Message Header</div>
            <div wicket:id="notifierMessage" class="notifier-message">Message Body Text</div>
        </div>
    </wicket:panel>
</body>
</html>


notifier.css

.notifier-content {
    font-size: 12px;
    height: 100px;
    width: 240px;
    padding: 4px;
    position: fixed;
    background-color: #EDEDED;
    border-color: #BFBFBF;
    border-width: 1px;
    border-style: solid;
    border-radius: 4px;
}

.notifier-header {
    background-color: #F5A729;
    border-color: #E78F08;
    border-style: solid;
    border-width: 1px;
    color: white;
    font-weight: bold;
    border-radius: 4px;
    margin: 0;
    padding: 4px;
    text-align: center;
}

.notifier-message {
    padding-top: 6px;
}

Friday, May 11, 2012

Memcached Spring integration

This is very simple example showing how to integrate Spring with memcached.

MemcachedClientFactoryBean is a Spring factory, which creates instance of MemcachedClient. Client class manages connections to memcached server farm, and takes care of .... mostly everything that is required for daily usage ;)

Configured Transcoder serializes Java objects - they are available on memcached server in binary form.

Spring bean below has single injection -  "memcached.client", this is the bean name of the factory, but spring recognises, that injected bean is a factory and does not inject factory itself, but uses it to create Bean instance - in this case memcached client.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"

    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="memcached.client" class="net.spy.memcached.spring.MemcachedClientFactoryBean">
        <property name="servers" value="host1:1122,host2:1122" />
        <property name="protocol" value="BINARY" />
        <property name="transcoder">
            <bean class="net.spy.memcached.transcoders.SerializingTranscoder" />
        </property>
        <property name="locatorType" value="ARRAY_MOD" />
        <property name="opTimeout" value="2000" />
        <property name="failureMode" value="Cancel" />
        <property name="useNagleAlgorithm" value="false" />
        <property name="timeoutExceptionThreshold" value="20" />
    </bean>
</beans> 
@Named
public class MemcachedSpringBeanExample {

    private MemcachedClient memcached;

    @Inject
    protected MemcachedSpringBeanExample(@Named("memcached.client") MemcachedClient memcached) {
        this.memcached = memcached;
    }

    public void doSomething(UasAccountId uasAccountId, LastLoginHistory histiry) {
        memcached.add("my_bean_key", 2000, new Object[] { "val1", "val2" });
    }
}

Wednesday, May 9, 2012

Wicket - disappearing Notification Dialog with bounce effect (jquery)


The example below displays notification dialog, which appears over HTML content, bounces few times and finally disappears. There is also small CSS to give it some nice look.
Such dialog can be used as a central point for asynchronous application notifications.

The implementation is based on single Wicket Web Page - this should be redesigned to reusable component, but simple page is usefull to get an idea.

At the beginning the dialog is hidden - Wicket Web Markup Container is being rendered as empty div (effectDialog).
Clicking on "Show Dialog" button sends Wicket Ajax Event to the server, and as response browser receives new HTML part, which replaces empty effectDialog. This new HTML code contains our Info Dialog, and also jQuery code. This code will play bounce effect once dialog is painted, and hide it after one second.


Java Code

public class FeedbackWebPage extends WebPage {

  public FeedbackWebPage() {
    String infoHeader = "Info Header";
    String infoContent = "Variables containing text content for our dialog "
        + "should be extracted to proper Wicket Model - we keep it simple"
        + " for demonstration proposals";

    // div containing info dialog
    final WebMarkupContainer infoDialog = new WebMarkupContainer("effectDialog");

    // hide dialog, but leave empty HTML tag in order to display it later
    infoDialog.setOutputMarkupId(true);
    infoDialog.setOutputMarkupPlaceholderTag(true);
    infoDialog.setVisible(false);
    add(infoDialog);

    // HTML ID for java script references
    final String infoDialogId = "#" + infoDialog.getMarkupId();

    // initialize jquery
    add(new JQueryBehavior(infoDialogId, "effect"));

    // labels building info dialog
    Label feedbackHeader = new Label("feedbackHeader", infoHeader);
    infoDialog.add(feedbackHeader);
    Label feedbackMessage = new Label("feedbackMessage", infoContent);
    infoDialog.add(feedbackMessage);

    // submit button - it will show our dialog
    Form<Void> form = new Form<Void>("form");
    add(form);
    form.add(new AjaxButton("open-dialog", form) {

      @Override
      protected void onSubmit(AjaxRequestTarget target, Form<?> form) {

        // replace empty #effectDialog with real content
        infoDialog.setVisible(true);
        target.add(infoDialog);

        // after setting dialog to visible play bounce effect
        JQueryEffectBehavior effectBehavior = new JQueryEffectBehavior(infoDialogId,
            "bounce", 500);

        // dialog should disappear after one second 
        effectBehavior.setCallback(new JQueryAjaxBehavior(this) {

          @Override
          public CharSequence getCallbackScript() {

            // add timer, to hide dialog after one second
            // TODO move this JavaScript to notifier.js (next blog post)
            String callbackScript = "setTimeout(function(){$(\"" + infoDialogId
                + ":visible\").fadeOut();}, 1000);";
            return callbackScript;
          }

          @Override
          protected JQueryEvent newEvent(AjaxRequestTarget target) {
            return null;
          }
        });

        String jsEffect = effectBehavior.toString();
        target.appendJavaScript(jsEffect);
      }

      @Override
      protected void onError(AjaxRequestTarget target, Form<?> form) {
      }
    });

  }
}

HTML Page - FeedbackWebPage.html

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">

<wicket:head>
    <wicket:link>
        <link rel="stylesheet" type="text/css" href="feedbackWebPage.css" />
    </wicket:link>
</wicket:head>

<body>
    <div wicket:id="effectDialog" class="ed-content">
        <div wicket:id="feedbackHeader" class="ed-header">Message Header</div>
        <div wicket:id="feedbackMessage" class="ed-message">Message Body Text</div>
    </div>
    <table border="1">
        <tr><th>COL1</th> <th>COL2</th> <th>COL3</th> </tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
        <tr><td>value 1</td> <td>test message</td> <td>some more text</td></tr>
    </table>
    <form wicket:id="form">
        <button wicket:id="open-dialog">Show Dialog</button>
    </form>
</body>
</html>

CSS - feedbackWebPage.css

.ed-content {
    font-size: 12px;
    height: 100px;
    width: 240px;
    padding: 4px;
    position: fixed;
    background-color: #EDEDED;
    border-color: #BFBFBF;
    border-width: 1px;
    border-style: solid;
    border-radius: 4px;
}

.ed-header {
    background-color: #F5A729;
    border-color: #E78F08;
    border-style: solid;
    border-width: 1px;
    color: white;
    font-weight: bold;
    border-radius: 4px;
    margin: 0;
    padding: 4px;
    text-align: center;
}

.ed-message {
    padding-top: 6px;
}

POM.XML - dependencies

<dependency>
    <groupId>org.apache.wicket</groupId>
    <artifactId>wicket-core</artifactId>
    <version>1.5.5</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.2</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.6.2</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-servlet-api</artifactId>
    <version>7.0.22</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>com.googlecode.wicket-jquery-ui</groupId>
    <artifactId>jquery-ui-core</artifactId>
    <version>1.1</version>
</dependency>