Neo4j Docker image with APOC procedure plugin installed

  • Posted on: 24 September 2017
  • By: Zhijun Chen

APOC is quite useful when running cypher query with Neo4j.

The following Dockerfile script provides pre-installed APOC plugins with Neo4j enterprise image.

FROM neo4j:3.2.3-enterprise

RUN apk add --no-cache --quiet \
bash \

WORKDIR /var/lib/neo4j

RUN mkdir scripts

COPY ./scripts/neo4j/apoc-warmup.cql scripts/apoc-warmup.cql

ENV NEO4J_HOME=/var/lib/neo4j

RUN curl --fail --silent --show-error --location --output apoc- $APOC_URI \
&& mv apoc- plugins/

EXPOSE 7474 7473 7687

CMD ["neo4j"]

NB: Check the APOC version to match the Neo4j version used.


Replace Apache2 server with Nginx on Ubuntu for Drupal sites

  • Posted on: 10 September 2017
  • By: Zhijun Chen

Here are the steps to move your Drupal site to Nginx from Apache2.

1. Stop Apache2

sudo service apache2 stop

2. Install Nginx

sudo apt-get install nginx

3. Edit /etc/nginx/site-enabled/default to the following config, make sure root is pointing to your site address

server {
    root /var/www/drupal8; ## <-- Your only path reference.

    location = /favicon.ico {
        log_not_found off;
        access_log off;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;

    # Very rarely should these ever be accessed outside of your lan
    location ~* \.(txt|log)$ {
        deny all;

    location ~ \..*/.*\.php$ {
        return 403;

    location ~ ^/sites/.*/private/ {
        return 403;

    # Allow "Well-Known URIs" as per RFC 5785
    location ~* ^/.well-known/ {
        allow all;

    # Block access to "hidden" files and directories whose names begin with a
    # period. This includes directories used by version control systems such
    # as Subversion or Git to store control files.
    location ~ (^|/)\. {
        return 403;

    location / {
        # try_files $uri @rewrite; # For Drupal <= 6
        try_files $uri /index.php?$query_string; # For Drupal >= 7

    location @rewrite {
        rewrite ^/(.*)$ /index.php?q=$1;

    # Don't allow direct access to PHP files in the vendor directory.
    location ~ /vendor/.*\.php$ {
        deny all;
        return 404;

    # In Drupal 8, we must also match new paths where the '.php' appears in
    # the middle, such as update.php/selection. The rule we use is strict,
    # and only allows this pattern with the update.php front controller.
    # This allows legacy path aliases in the form of
    # blog/index.php/legacy-path to continue to route to Drupal nodes. If
    # you do not have any paths like that, then you might prefer to use a
    # laxer rule, such as:
    #   location ~ \.php(/|$) {
    # The laxer rule will continue to work if Drupal uses this new URL
    # pattern with front controllers other than update.php in a future
    # release.
    location ~ '\.php$|^/update.php' {
        fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
        # Security note: If you're running a version of PHP older than the
        # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini.
        # See for details.
        include fastcgi_params;
        # Block httpoxy attacks. See
        fastcgi_param HTTP_PROXY "";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param QUERY_STRING $query_string;
        fastcgi_intercept_errors on;
        # PHP 5 socket location.
        #fastcgi_pass unix:/var/run/php5-fpm.sock;
        # PHP 7 socket location.
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

    # Fighting with Styles? This little gem is amazing.
    # location ~ ^/sites/.*/files/imagecache/ { # For Drupal <= 6
    location ~ ^/sites/.*/files/styles/ { # For Drupal >= 7
        try_files $uri @rewrite;

    # Handle private files through Drupal. Private file's path can come
    # with a language prefix.
    location ~ ^(/[a-z\-]+)?/system/files/ { # For Drupal >= 7
        try_files $uri /index.php?$query_string;

    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires max;
        log_not_found off;

4. Restart Nginx and you should see your site up and running.

sudo service nginx start

5. Remove Apache2 dependencies

sudo apt-get purge apache2 apache2-utils apache2.2-bin apache2-common
sudo apt-get autoremove

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.


import java.util.Calendar;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

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


public class StudentCache {

  private static Logger logger = LoggerFactory

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

  public StudentCache(int expirationTimeMinutes) {

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

          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) {

  private RemovalListener<Long, Student> studentRemovalListener = new RemovalListener<Long, Student>() {
    public void onRemoval(RemovalNotification<Long, Student> removal) {
          "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="">
  <constructor-arg name="expirationTimeMinutes" value="10" />
  <property name="studentManager" ref="studentManager" />

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/
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

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:

    <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>

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.

  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:

  `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`) 

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.

USE test_db$$
		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, ''), 'Test', 'User', 
				'LKJMoSCO3eQSFoHlni9ludxTBkBHpPmk', 1, 'USER', 
			SET x = x + 1;
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() {

   * Add a widget
   * @param w the widget to be added
  public void add(IsWidget w) {

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

   * Clear the widgets
  public void clear() {

  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) {

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

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


  private void show(int newIndex) {
    if (newIndex == currentIndex) {

    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);

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();
FlowPanel panel2 = new FlowPanel();

// slide when needed

The custom sliding widget has been tested using two widgets.