Use Guava Cache To Improve Performance

  • Posted on: 18 April 2015
  • By: Zhijun Chen

It is quite often that you might want to use cache a value when it is expensive to compute or retrieve, e.g. involves several hibernate queries and serialization.

Guava Cache is one of the options if you just need to cache the value in RAM instead of files.

In my case, I'm using CacheLoader as I've got key/value pairs. The following utility class shows how this can be done.

package uk.co.zhijun.manager.cache;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Calendar;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

/**
 * 
 */
public class StudentCache {

  private static Logger logger = LoggerFactory
      .getLogger(StudentCache.class);

  private LoadingCache<Long, Student> studentCache;
  private StudentManager studentManager;

  public StudentCache(int expirationTimeMinutes) {
    buildStudentCache(expirationTimeMinutes);
  }

  private void buildStudentCache(int expirationTimeMinutes) {
    studentCache = CacheBuilder.newBuilder()
        .expireAfterAccess(expirationTimeMinutes, TimeUnit.MINUTES)
        .removalListener(studentRemovalListener)
        .build(new CacheLoader<Long, Student>() {

          @Override
          public Student load(Long id) throws Exception {
            return studentManager.getStudentById(id);
          }
        });
  }

  public Student getStudent(Long id) throws ExecutionException {
      return studentCache.get(id);
  }

  public void invalidateStudentCache(Long id) {
    studentCache.invalidate(id);
  }

  private RemovalListener<Long, Student> studentRemovalListener = new RemovalListener<Long, Student>() {
    public void onRemoval(RemovalNotification<Long, Student> removal) {
      logger.info(
          "Refreshing student, studentId = {}, was evicted? {}",
          removal.getKey(), removal.wasEvicted());
    }
  };

  /* provided for spring setter injection */
  public void setStudentManager(StudentManager studentManager) {
    this.studentManager = studentManager;
  }
}

You can then register this class in the spring context and use it. The cache should be evicted everytime a change is going to affect the Student object.

...
<bean id="studentCache" class="uk.co.zhijun.manager.cache.StudentCache">
  <constructor-arg name="expirationTimeMinutes" value="10" />
  <property name="studentManager" ref="studentManager" />
</bean>
...

Create Vagrant Windows Base Box With Virtualbox

  • Posted on: 25 December 2014
  • By: Zhijun Chen

Recently I've been trying to set up a Selenium Grid using Vagrant virtual machines, and one of the things I need is a Vagrant Windows base box.

  • Create and set up a Windows VM using Virtualbox

Open Virtualbox and click on new button to create a new Windows 8 (32 bit) virtual machine.

RAM settings: 1GB RAM.

Use virtual hard drive and select "Dynamically allocated" for storage.

Storage size: 16GB.

Install the operating system either from .iso file or cd disk.

Wait for the installation to complete and follow the instruction to create a local account. The username and password should both be "vagrant". Install software required in this VM, e.g. Chrome, Firefox, Java, etc.

Alternatively, you can use Chef to manage the software requirements.

  • Turn the Windows VM into Vagrant base box

Once the Windows VM is ready, we need to turn it into a Vagrant base box. Vagrant uses either SSH or Windows Remote Management to communicate with Windows. I use WinRM because it is more conventional. Here are the requirements:

    • Turn off UAC. To do this, press Ctrl+Esc to open start screen. Type "uac" under settings in search box, and drag the slider all the way to the bottom.

    • Disable complex passwords, under gpedit.msc

    • Disable "shutdown tracker", under gpedit.msc

To Enable and cnofigure WinRM you'll need to set the WinRM service to auto-start and allow unencrypted basic auth. Run the following commands from a regular Windows command prompt:

 winrm quickconfig -q winrm set winrm/config/winrs @{MaxMemoryPerShellMB="512"} winrm set winrm/config @{MaxTimeoutms="1800000"} winrm set winrm/config/service @{AllowUnencrypted="true"} winrm set winrm/config/service/auth @{Basic="true"} sc config WinRM start= auto
  • Use the base box

To Use the box, run the following command: 

vagrant box add my-box /path/to/the/new.box
vagrant init my-box
vagrant up

  

Connecting To Remote MySQL From Hibernate Using Tunnelling Over SSH

  • Posted on: 12 December 2014
  • By: Zhijun Chen

For certain reasons sometimes you may want to connect to remote MySQL server over SSH tunnel when you maintain web applications, e.g. debug a production issue which is hard to replicate locally. Most IDEs like Eclipse offer the debug tools which you can put break points and step through. You can find out the problem once connecting to remote database and debug with caution using local code copy.

The following steps illustrate how to achieve this for Hibernate based web applications (Presume you have the user set up for SSH and database connection).

  • Open MySQL tunnel over SSH

 You will only need the following command to open a tunnel:

ssh -L 3333:db.remotehost.com:3306 username@remotehost.com

This will open port 3333 on local machine to connect to MySQL port 3306 on remote server, enter your password on remote host once prompted.

  • Update Hibernate configuration file

Once we've got the tunnel, the next step would be updating Hibernate configuration file hibernate.cfg.xml to connect to the port 3333 as follows:

<hibernate-configuration>
  <session-factory>
    ...
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3333/remotedb</property>
    <property name="hibernate.connection.username">remote_user</property>
    <property name="hibernate.connection.password">remote_password</property>
    ...
  </session-factory>
</hibernate-configuration>

The remotedb is the database to connect on remote server, remote_user/remote_password is the username and password for that database on remote MySQL database.

Now you can open your web application and it should use the remote database you specified.

Solution for "InvalidPathException: Malformed input" in Tomcat

  • Posted on: 3 December 2014
  • By: Zhijun Chen

Today I came across the following Java Exception within Tomcat Web Application:

"java.nio.file.InvalidPathException: Malformed input or input contains unmappable characters."

This is due to file system not able to handle special characters in a file name, e.g. Tromsø.png
The solution to this issue is actually quite simple. Set the system language to be UTF8 using the following code:

export LANG = en_US.UTF8

Then restart Tomcat. Now Tomcat should pick up the system property and set the file.encode to be UTF8.

Create Drupal 7 Slide Show Using Views Slideshow Modules

  • Posted on: 12 November 2014
  • By: Zhijun Chen

I've been building Drupal websites during my spare time recently. One of the common features in modern sites is a home page slideshow.
There are a few ways to achieve this in Drupal. In this tutorial I will be using Views Slideshow module together with Views module.

  • Install modules and libraries

Download the following modules, install and enable the related modules via admin modules interface.

  1. Views
  2. jQuery Update
  3. Views slideshow
  4. Ctools
  5. Libraries API

Download jQuery Cycle plugin and install it by placing the js file inside jquery.cycle directory under sites/all/libraries.

  • Optional: Apply patch for customized navigation texts

By default the Views slideshow module does not offer customized navigation texts, only "previous" and "next" available for paging.
In my case I would like to use "<" and ">" as navigation buttons and style them afterwards simply because I prefer CSS3 to images. :)
In order to achieve this you need to apply the patch from this thread.
Once the patch is applied, add the following code to theme's template.php file (replace THEME with your theme name):

 function THEME_preprocess_views_slideshow_controls_text_previous(&$vars) {
  $vars['previous_text'] = '<';
}

/**
 * Implements template_preprocess_views_slideshow_controls_text_next().
 */
function THEME_preprocess_views_slideshow_controls_text_next(&$vars) {
  $vars['next_text'] = '>';
}

/**
 * Implements template_preprocess_views_slideshow_controls_text_next().
 */
function THEME_preprocess_views_slideshow_controls_text_pause(&$vars) {
  $vars['start_text'] = '';
}

 

  • Create content type

Next step we need to add a content type for slide pages. Add a content type by navigating to Structure -> Content types -> Add content types.
Here is an example content type I created:

  • Create contents

Once the content type is created, we can create some contents for slideshow by navigating to Content -> Add Content -> [Your content type]

  • Create slideview

After contents are created, we finally reach the last step: create the view.
Navigate to Structure -> Views -> Add new view. Select the display format to be Slideview as shown in the following:

Edit the view to include all the fields in the slideview, for example:

You can configure the Slideview by clicking on the settings link on the right, here you can choose the effect, and add pager:

Jenkins Build Pipeline Plugin With Manual Retry Build Setup

  • Posted on: 6 September 2014
  • By: Zhijun Chen

Build Pipeline Plugin is very useful in product release process. As the name suggests, You can get a pipeline view of upstream and downstream Jenkins jobs by chaining them together.

The following steps provide plugin setup under Ubuntu system.

  • Install Build Pipeline Plugin

Update Jenkins to the latest version using the following command if needed.

sudo apt-get update
sudo apt-get install jenkins

Navigate to Manage Jenkins -> Manage Plugins -> Available, type "build pipeline plugin", select the plugin and install. Restart Jenkins after installation.

  • Chain Jenkins jobs together

As show in the following picture, I've got three jobs named Release, Test and Deploy.

The sequence would be: Release -> Test -> Deploy. To create the chain, go to each job and specify the following post build actions when applicable.

The following picture gives an example of Test project.

Note: The manual step is required if you would like manual retry button on pipeline to work.

  • Set up pipeline

Create a pipeline view by clicking on the "+" sign next to All tab under Jenkins job view. This will take you to the following screen.

Configure the pipeline to allow manual trigger on pipeline steps and set the initial job.

Save the pipeline view and you should have the following view after a few executions. The retry button on each step should also work.

Create MySQL Test Data For Varbinary Column

  • Posted on: 16 August 2014
  • By: Zhijun Chen

I recently came across a scenario where I needed to create hundreds of test data for a MySQL table with varbinary column defined.

Here is the table schema:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(100) NOT NULL,
  `first_name` varchar(20) NOT NULL,
  `last_name` varchar(20) NOT NULL,
  `password` varchar(100) NOT NULL,
  `company_id` int(11) NOT NULL,
  `creation_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `dtype` varchar(10) NOT NULL,
  `uuid` varbinary(16) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_name` (`user_name`),
  KEY `user_company_key` (`company_id`),
  CONSTRAINT `user_company_key` 
  FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) 
  ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

When I used uuid() method to generate column value, I got an error of "Data too long for column".  Finally I managed to get around the problem using the following procedure.

DELIMITER $$
USE test_db$$
DROP PROCEDURE IF EXISTS addUserTest$$
CREATE PROCEDURE addUserTest()
	BEGIN
		DECLARE x INT;
		SET x = 1;
		WHILE x <= 500 DO
			INSERT INTO user (`user_name`, `first_name`, `last_name`, 
				`password`, `company_id`, `dtype`, `uuid`) VALUES 
				(CONCAT('test_user', x, '@example.com'), 'Test', 'User', 
				'LKJMoSCO3eQSFoHlni9ludxTBkBHpPmk', 1, 'USER', 
				UNHEX(REPLACE(UUID(),'-','')));
			SET x = x + 1;
		END WHILE;
	END$$
DELIMITER ;
CALL addUserTest;

unhex(replace(uuid(), '-', '')) generates suitable values for varbinary(16) column.
The concat() method concatenates strings together in MySQL while replace() method replaces one string with another.

Implement sliding panel with GWT layout panel

  • Posted on: 14 June 2014
  • By: Zhijun Chen

Google Web Toolkit doesn't come with sliding widget which is quite common in other JavaScript library. However, using LayoutPanel provided by GWT, we can easily implement sliding widget by using CSS and animation effect. The following code gives an example of horizontal sliding widget.

 // import statements
public class SlidingPanel extends ResizeComposite implements HasWidgets, HasOneWidget {

  private final LayoutPanel layoutPanel = new LayoutPanel();
  private int currentIndex = -1;

  /**
   * Default constructor
   */
  public SlidingPanel() {
    initWidget(layoutPanel);
  }

  /**
   * Add a widget
   *
   * @param w the widget to be added
   */
  public void add(IsWidget w) {
    add(w.asWidget());
  }

  /**
   * Add a widget
   */
  public void add(Widget w) {
    layoutPanel.add(w);
    if (currentIndex < 0) {
      currentIndex = 0;
    } else {
      layoutPanel.setWidgetLeftWidth(w, 100, Unit.PCT, 100, Unit.PCT);
    }
  }

  /**
   * Clear the widgets
   */
  public void clear() {
    setWidget(null);
  }

  public Widget getWidget() {
    return layoutPanel.getWidget(currentIndex);
  }

  /**
   * @return widget iterator
   */
  public Iterator iterator() {
    return layoutPanel.iterator();
  }

  /**
   * Remove a widget
   *
   * @param w widget to be removed
   * @return result
   */
  public boolean remove(Widget w) {
    return layoutPanel.remove(w);
  }

  /**
   * Set the widget
   *
   * @param w the widget
   */
  public void setWidget(IsWidget w) {
    if (w != null) {
      setWidget(asWidgetOrNull(w));
    }
  }

  @Override
  public void setWidget(Widget widget) {
    int newIndex = layoutPanel.getWidgetIndex(widget);

    if (newIndex < 0) {
      newIndex = layoutPanel.getWidgetCount();
      add(widget);
    }

    show(newIndex);
  }

  private void show(int newIndex) {
    if (newIndex == currentIndex) {
      return;
    }

    boolean fromLeft = newIndex < currentIndex;
    final Widget current = layoutPanel.getWidget(currentIndex);
    Widget widget = layoutPanel.getWidget(newIndex);
    currentIndex = newIndex;

    layoutPanel.setWidgetLeftWidth(widget, 0, Unit.PCT, 100, Unit.PCT);
    if (fromLeft) {
      layoutPanel.setWidgetLeftWidth(current, 100, Unit.PCT, 100, Unit.PCT);
    } else {
      layoutPanel.setWidgetLeftWidth(current, -100, Unit.PCT, 100, Unit.PCT);
    }
    layoutPanel.animate(500);
  }
}

So now we've created a custom sliding widget, the way to use it is also very straightforward: Add all the widgets, and show one of the widgets when needed.

SlidingPanel slidingPanel = new SlidingPanel();
FlowPanel panel1 = new FlowPanel();
slidingPanel.add(panel1);
FlowPanel panel2 = new FlowPanel();
slidingPanel.add(panel2);

// slide when needed
slidingPanel.setWidget(panel2);

The custom sliding widget has been tested using two widgets.

Integrate jScrollPane with GWT

  • Posted on: 21 May 2014
  • By: Zhijun Chen

jScrollPane is a cross-browser jQuery plugin which converts a browser's default scrollbars into an HTML structure which can be easily skinned with CSS. It is very helpful if you need a cross-browser style scrollbar.

If you build your web application using Google Web Toolkit (GWT), the easiest way to integrate jScrollPane is using JavaScript Native Interface (JSNI) feature of GWT. JSNI allows you to integrate JavaScript directly into your application's Java source code. The detail guide for JSNI in GWT can be found here.

Download jScrollPane and related files from here and include them inside your main page as follows:

...
<head>
  <link type="text/css" href="style/jquery.jscrollpane.css" rel="stylesheet" />
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script type="text/javascript" src="script/jquery.mousewheel.js"></script>
  <script type="text/javascript" src="script/jquery.jscrollpane.min.js"></script>
  <script type="text/javascript">
    <!-- The following code will enable us to use jQuery as keyword instead of $ -->
    $.noConflict();
  </script>
</head>
...

Now that we are able to use jScrollPane, we can integrate it into GWT using the following JSNI code.

public native void initCustomScrollbar(String id) /*-{
    var settings = {
        autoReinitialise : true,
        horizontalDragMaxWidth : 0
    };

    $wnd.jQuery('#' + id).jScrollPane(settings);
}-*/;

We need to set an id on GWT widget then call this method when it is needed (could be inside a GWT DeferredCommand):

FlowPanel scrollPanel = new FlowPanel();
scrollPanel.getElement().setId('scrollPanel');
// This method may be needed to put int a scheduler deferred command
initCustomScrollbar('scrollPanel');

Now you should be able to wrap all methods in jScrollPane through JSNI by access api as follows:

var api = $wnd.jQuery('#' + id).data('jsp');

Use CRest to consume RESTful Web Service for Android

  • Posted on: 20 April 2014
  • By: Zhijun Chen

There are several ways to consume RESTful web services from Android. One option would be using the builtin HttpClient library and parsing the response using an XML/Json parser library, e.g. Gson. After several hours research, I chose CRest Android version because of its simplicity. CRest is a lightweight JAX-RS compatible framework aiming to simplify the integration of external REST services into Java applications.

* Libraries required

The following libraries are required and should be put inside Android /libs folder. Maven dependencies are also available if you are using android maven plugin.

* RESTful Web Service Example and sample Response

Suppose we have a REST service GET call which simply returns the user with id: http://192.168.0.100:8000/api/v1/user/{id}?format=json. And the sample JSON response is given as follows.

 {
  "id": 1,
  "first_name": "Zhijun",
  "last_name": "Chen",
  "name": "zhijun.chen"
}
* Java Model Objects

A Pojo is needed to map the JSON response into Java objects.

package uk.co.zhijun.model;

import org.codehaus.jackson.annotate.JsonProperty;

public class User {
	private Long id;
	@JsonProperty("first_name")
	private String firstName;
	@JsonProperty("last_name")
	private String lastName;
	private String name;
	
	public User() {
	}
	
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}
	
	public String getFirstName() {
		return firstName;
	}
	
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	
	public String getLastName() {
		return lastName;
	}
	
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

Note: Any properties defined inside Java model but not in the JSON response should be annotated with @JsonIgnore

* Services Interfaces

Next we will create a service interface to consume the REST service.

import org.codegist.crest.annotate.Consumes;
import org.codegist.crest.annotate.EndPoint;
import org.codegist.crest.annotate.HeaderParam;
import org.codegist.crest.annotate.Path;
import org.codegist.crest.annotate.PathParam;

import uk.co.zhijun.model.User;

@EndPoint("http://192.168.0.100:8000")
@Path("/api/v1/user")
@Consumes("application/json")
public interface UserService {
	
	@Path("/{id}")
	@HeaderParam(value="Authorization", defaultValue="Basic {basic.auth.digest}")
	public User getUser(@PathParam("id") long id);
}

The @HeaderParam is needed when you need to add any http request header to the request, in this case it is an authentication token. @Consumes defines the type of response received from REST service. We will show how to pass in {basic.auth.digest} value.

* Android Integration

Now we have service interface ready, the final step would be integrated it into Android project. Android uses a seperate thread from main thread to execute http calls. And it is recommended to set CRest to use HttpClient to send requests. The following code gives an example.

private class FetchUserTask extends AsyncTask<Void, Void, User> {

			@Override
			protected User doInBackground(Void... params) {
				String username = "username";
				String password = "password";
				String basicAuthDigest = Base64.encode(username + ":" + password);

				CRest crest = CRest.placeholder("basic.auth.digest", basicAuthDigest)
						.setHttpChannelFactory(
								HttpClientHttpChannelFactory.class).build();
				UserService userService = crest.build(UserService.class);
				User user = userService.getUser(1);
				return user;
			}

			protected void onPostExecute(User user) {
				// do something with the response
			}
		}

As you can see, the authentication code is passed in using placeholder.

Pages