Watching a Directory for Changes

Watch Service Overview

The WatchService API is fairly low level, allowing you to customise it. You can use it as is, or you can choose to create a high-level API on top of this mechanism so that it is suited to your particular needs. The WatchService API lets you receive notification events upon changes to the subject (directory or file).

The steps involved in implementing the API are:

  • Create a WatchService. This service consists of a queue to holdWatchKeys
  • Register the directory/file you wish to monitor with this WatchService
  • While registering, specify the types of events you wish to receive (create, modify or delete events)
  • You have to start an infinite loop to listen to events
  • When an event occurs, a WatchKey is placed into the queue
  • Consume the WatchKey and invoke queries on it

Let’s follow this via an example. We create a DirWatcher Java program whose responsibility is to watch a particular directory (Downloads in my case). The steps are provided below:

1. Creating an WatchService object:

WatchService  watchService = FileSystems.getDefault().newWatchService();

2. Obtain a path reference to your watchable directory. I suggest you parameterize this directory so you don’t hard code the file name.

path = Paths.get(System.getProperty("user.home") + "/Downloads");

3. The next step is to register the directory with the WatchService for all types of events:

path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

When registering an object with the watch service, you specify the types of events that you want to monitor. The supported StandardWatchEventKinds event types follow:

  • ENTRY_CREATE – A directory entry is created.
  • ENTRY_DELETE – A directory entry is deleted.
  • ENTRY_MODIFY – A directory entry is modified.
  • OVERFLOW – Indicates those events might have been lost or discarded. You do not have to register for the OVERFLOW event to receive it.

4. Initiate the infinite loop and start taking the events:

while(true) {
    try {
        key = watchService.take();
.
.
.

    } catch (InterruptedException e) {
        System.out.println("InterruptedException: "+e.getMessage());
    }

 

5. Run through the events on the key:

for (WatchEvent<?> event : key.pollEvents()) {
      WatchEvent.Kind<?> kind = event.kind();
      System.out.println("Event on " + event.context().toString() + " is " + kind);
 }

For example, if you modify or delete the Download directory, you would see statement as shown below on the console respectively:

Event on newFile.txt is ENTRY_CREATE
Event on newFile.txt is ENTRY_MODIFY

 

Example source code:

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

/**
 * Created by marom on 28/09/16.
 */
public class DirWatcher {


        private Path path = null;
        private WatchService watchService = null;

        protected void init() {
            path = Paths.get(System.getProperty("user.home") + "/Downloads");
            try {
                watchService = FileSystems.getDefault().newWatchService();
                path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
            } catch (IOException e) {
                System.out.println("IOException"+ e.getMessage());
            }
        }


        protected void doRounds() {
            WatchKey key = null;
            while(true) {
                try {
                    key = watchService.take();
                    for (WatchEvent<?> event : key.pollEvents()) {
                        WatchEvent.Kind<?> kind = event.kind();
                        System.out.println("Event on " + event.context().toString() + " is " + kind);
                    }
                } catch (InterruptedException e) {
                    System.out.println("InterruptedException: "+e.getMessage());
                }
                boolean reset = key.reset();
                if(!reset)
                    break;
            }
        }
}

 

public class Main {

    public static void main(String[] args) {

        DirWatcher watcher = new DirWatcher();
        watcher.init();
        watcher.doRounds();
    }
}

 

Simple Jersey example with Intellij IDEA Ultimate and Tomcat

Jersey

RESTful Web Services in Java.

Jersey is a reference implementation of the JAX-RS 2.0 API. Jersey provides it’s own API that extends the JAX-RS toolkit with additional features and utilities to further simplify RESTful service and client development.

In this example I will be using:

  • IntelliJ Idea Ultimate 2016.2
  • Jersey 2.23.2 (latest stable release of Jersey as of time of writing)
  • JDK 8
  • Tomcat 8.0.35

Create new project

To create a new project, open up the New Project dialog in IntelliJ. In the left menu, select Java Enterprise. To the right, select Web Application and ensure that Create web.xml is checked.

newproject

 

Add Maven support

To add Maven to your project, right-click on your project’s name in the Project sidebar and select Add Framework Support.

In the Add Frameworks Support, select Maven.

Once this is done, the pom.xml file will open in the editor. You will need to enter at least a groupId, usually your package name. At this point, it is helpful to enable auto-importing for library dependencies.

maven

Add Jersey dependencies to the Maven pom.xml file

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.programmerscuriosity</groupId>
    <artifactId>SimpleJersey</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.glassfish.jersey</groupId>
                <artifactId>jersey-bom</artifactId>
                <version>${jersey.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
        </dependency>
    </dependencies>

    <properties>
        <jersey.version>2.23.2</jersey.version>
    </properties>
    
</project>

 

Create a Resource

Inside of the src/main/java directory, create a new package matching your groupId.

And then create a simple java class – MyResource in my case.

package com.programmerscuriosity;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

/**
 * Created by marom on 27/09/16.
 */
@Path("myresource")
public class MyResource {

    /**
     * Method handling HTTP GET requests. The returned object will be sent
     * to the client as "text/plain" media type.
     *
     * @return String that will be returned as a text/plain response.
     */
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getIt() {
        return "Got it!";
    }
}

Modify index.jsp file by adding a link to the Jersey resource

<html>
  <head>
    <title>Simple Jersey</title>
  </head>
  <body>
  <p><a href="rest/myresource">Jersey resource</a>
  </body>
</html>

 

Check if Tomcat is configured properly.

tomcat-configuration

tomcat-deployment

 

Configure your servlet

Modify your web.xml file

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.programmerscuriosity</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
</web-app>

 

Before we run our application, first we need to add Jersey jars to the /WEB-INF/lib directory

add-artifacts

After selecting all jars right click and choose “Put into /WEB-INF/lib

Run your Application

jersey-resource
got-it

 

DONE!

 

 

Git for an absolute beginners

Before I started using Git on my personal and then on commercial projects, I was using for many years  Subversion – svn in short. SVN is still very popular but Git earned its strong position as well by being used in large and small projects for more than 10 years already.

Installing Git

The official website of Git has detailed information about installing on Linux, Mac, or Windows. In this case, I will be using Ubuntu 16.04 for demonstration purposes, and installing git in Linux (Ubuntu) is simple as typing:

marom@marom-E7440:~$ sudo apt-get install git

Before we dive into configuration and will start using Git a bit of terminology introduction is necessary.

Here’s the git terminology:

  • master – the repository’s main branch. Depending on the workflow it is the one people work on or the one where the integration happens
  • clone – copies an existing git repository, normally from some remote location to your local environment.
  • commit – submitting files to the repository (the local one); in other VCS it is often referred to as “checkin”
  • fetch or pull – is like “update” or “get latest” in other VCS. The difference between fetch and pull is that pull combines both, fetching the latest code from a remote repo as well as performs the merging.
  • push – is used to submit the code to a remote repository
  • remote – these are “remote” locations of your repository, normally on some central server.
  • SHA – every commit or node in the Git tree is identified by a unique SHA key. You can use them in various commands in order to manipulate a specific node.
  • head – is a reference to the node to which our working space of the repository currently points.
  • branch – is just like in other VCS with the difference that a branch in Git is actually nothing more special than a particular label on a given node. It is not a physical copy of the files as in other popular VCS.

Initial Configuration

We need to configure our name and email. You can do it as follows, replacing the values with your own name and email.

marom@marom-E7440:~$ git config --global user.name "Courious Programmer"
marom@marom-E7440:~$ git config --global user.email "blog@programmerscuriosity.com"

It is important to note that if you do not set your name and email, certain default values will be used.

Create a new Git Repository

Before starting, let’s create a new directory where the git repository will be created and ‘move’ into it.

marom@marom-E7440:~/SmallProjects$ mkdir blogGitIntroduction
marom@marom-E7440:~/SmallProjects$ cd blogGitIntroduction/
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ 

Now we can initialize our first git repository.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git init
Initialized empty Git repository in /home/marom/SmallProjects/blogGitIntroduction/.git/

To check the status of our repository git status command can be used.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

Create and commit a new file

The next step is to create a new file and add some content to it.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ touch blogpost.txt

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ echo "Hello, Git World!" > blogpost.txt

Let’s check what happen in our git repository.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	blogpost.txt

nothing added to commit but untracked files present (use "git add" to track)

To “register” the file for committing we need to add it to git using:

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git add blogpost.txt 

And again, let’s check what happen in our git repository.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git add secondFile.txt 
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git commit -m "add second file to the project"
[master 763b757] add second file to the project
 1 file changed, 1 insertion(+)
 create mode 100644 secondFile.txt

 

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

	new file:   blogpost.txt

We can now commit it to the repository.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git commit -m "add first file to the project"
[master (root-commit) 55db76b] add first file to the project
 1 file changed, 1 insertion(+)
 create mode 100644 blogpost.txt

Let’s add another file to the project.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ echo "this is text from second file in project" > secondFile.txt
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git add secondFile.txt 
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git commit -m "add second file to the project"
[master 763b757] add second file to the project
 1 file changed, 1 insertion(+)
 create mode 100644 secondFile.txt

 

Create a (feature) branch

Branching and merging is what makes Git so powerful and for what it has been optimised, being a distributed version control system (VCS). Indeed, feature branches are quite popular to be used with Git. Feature branches are created for every new kind of functionality you’re going to add to your system and they have usually deleted afterwards once the feature is merged back into the main integration branch (normally the master branch). The advantage is that you can experiment with new functionality in a separate, isolated “playground” and quickly switch back and forth to the original “master” branch when needed. Moreover, it can be easily discarded again (in case it is not needed) by simply dropping the feature branch. But let’s get started and create the new feature branch:

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git branch new-feature-branch

To see a list of all branches we need to use git branch command.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git branch
* master
  new-feature-branch

A star next to master branch indicates that this is a current branch.

Let’s switch to this newly created branch and modify one of the files.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git checkout new-feature-branch 
Switched to branch 'new-feature-branch'
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git branch
  master
* new-feature-branch
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ echo "Let's modify file in the branch" >> blogpost.txt
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ cat blogpost.txt 
Hello, Git World!
Let's modify file in the branch

And then add and commit it to the branch.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git add blogpost.txt 
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git commit -m "modify file on the branch"
[new-feature-branch 3a30916] modify file on the branch
 1 file changed, 1 insertion(+)

 

Ok, it’s time to complicate it and cause some conflicts. To do this we need to switch to ‘master’ branch and modify the blogpost.txt file.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git checkout master
Switched to branch 'master'
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git branch
* master
  new-feature-branch
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ echo "This change was done on master branch" >> blogpost.txt
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ cat blogpost.txt 
Hello, Git World!
This change was done on master branch

 

If you need to commit a file that was already added to the git repository you can use command ‘commit -a -m’

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git commit -a -m "change blogpost file"
[master 1a139b5] change blogpost file
 1 file changed, 1 insertion(+)

Merge and resolve conflicts

The next step would be to merge our feature branch back into ‘master’. This is done by using the merge command.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git merge new-feature-branch 
Auto-merging blogpost.txt
CONFLICT (content): Merge conflict in blogpost.txt
Automatic merge failed; fix conflicts and then commit the result.
marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ cat blogpost.txt 
Hello, Git World!
<<<<<<< HEAD
This change was done on master branch
=======
Let's modify file in the branch
>>>>>>> new-feature-branch

Edit file and resolve conflict and commit changes to the branch.

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ cat blogpost.txt 
Hello, Git World!

This change was done on master branch

Let's modify file in the branch

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git commit -a -m "resolve merge conflict"
[master 7e2abc0] resolve merge conflict

 

Rollback

What if we want to undo everything back to the state before the commit?

First, we need to get SHA identifiers (unique ‘id’ for each node in git tree).

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git log
commit 4afb2d5caf73f48be16cc78875b8fe58c72ce4dd
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 10:02:16 2016 +0200

    prepare for rollback

commit 7e2abc0dc4f56672bf0f209551698c942dbc7071
Merge: 1a139b5 3a30916
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 09:57:50 2016 +0200

    resolve merge conflict

commit 1a139b5dd7a9f42e4f7f570081b65dbc4d27529b
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 09:37:40 2016 +0200

    change blogpost file

commit 3a309165db700a00fbe1a0f2451700c59532d629
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 09:26:56 2016 +0200

 

and then move back in time to the desired node/commit

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git reset --hard 7e2abc0
HEAD is now at 7e2abc0 resolve merge conflict

 

marom@marom-E7440:~/SmallProjects/blogGitIntroduction$ git log
commit 7e2abc0dc4f56672bf0f209551698c942dbc7071
Merge: 1a139b5 3a30916
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 09:57:50 2016 +0200

    resolve merge conflict

commit 1a139b5dd7a9f42e4f7f570081b65dbc4d27529b
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 09:37:40 2016 +0200

    change blogpost file

commit 3a309165db700a00fbe1a0f2451700c59532d629
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sun Sep 11 09:26:56 2016 +0200

    modify file on the branch

commit 763b75761c9d95a68d1d1e56bd4d2b0390c03379
Author: Courious Programmer <blog@programmerscuriosity.com>
Date:   Sat Sep 10 19:13:28 2016 +0200

 

And that’s it. It is everything that is needed to start using git. I have covered here only simple commands. For more advanced commands and explanations I highly recommend reading this book: Pro GIT.

JUnit – assertThat (Hamcrest) – part 3

Ok, it’s time to continue our series of examples Hamcrest matchers usage. Previous blog posts can be found here (part 1part 2).

  • endsWith()

Creates a matcher that matches if the examined String ends with the specified String.

   @Test
    public void endsWithExample() {

        assertThat("Let's test endsWith matcher", endsWith("matcher"));
    }

 

  • equalTo()

Creates a matcher that matches when the examined object is logically equal to the examined object.

   @Test
    public void equalToValueExample() {

        String testValue = "text";

        assertThat(testValue, equalTo("text"));
    }

 

equalTo() can also be used on Arrays in which case it will check the length of the Array and ensure that all the values in the input test array are logically equal to the values of the specified array.

   @Test
    public void equalToArrayExample() {

        assertThat(new String[] {"Jon", "Paul"}, equalTo(new String[] {"Jon", "Paul"}));
    }

 

  • equalToIgnoringCase()

Creates a matcher of String that matches when the examined string is equal to the specified expectedString, ignoring case.

   @Test
    public void equalToIgnoringCaseExample() {

        String testValue = "TeXt";

        assertThat("text", equalToIgnoringCase(testValue));
    }

 

  • equalToIgnoringWhiteSpace()

Creates a matcher of String that matches when the examined string is equal to the specified expected String when whitespace differences are (mostly) ignored. To be exact, the following whitespace rules are applied:

  1. all leading and trailing whitespace of both the expected String and the examined string are ignored
  2. any remaining whitespace, appearing within either string, is collapsed to a single space before comparison
   @Test
    public void equalToIgnoringWhiteSpaceExample() {

        String testValue = "all leading    and trailing whitespace of both    the       expectedString and     the examined string   are       ignored    ";

        // Then
        assertThat(testValue, equalToIgnoringWhiteSpace("all leading and trailing whitespace of both the expectedString and the examined string are ignored"));
    }

 

  • greaterThan()

Creates a matcher of Comparable object that matches when the examined object is greater than the specified value, as reported by the compareTo method of the examined object.

   @Test
    public void greaterThanExample() {

        Integer testValue = 5;

        assertThat(6, is(greaterThan(testValue)));
    }

 

  • greaterThanOrEqualTo()

Creates a matcher of Comparable object that matches when the examined object is greater than or equal to the specified value, as reported by the compareTo method of the examined object.

    @Test
    public void greaterThanOrEqualToExample() {

        Double testValue = 5.5;

        assertThat(5.5, is(greaterThanOrEqualTo(testValue)));
    }

 

  • hasEntry()

Creates a matcher for Maps matching when the examined Map contains at least one entry whose key equals the specified key and whose value equals the specified value.

  @Test
    public void hasEntryExample() {

        Map<Integer, String> myMap = new HashMap<>();
        myMap.put(1, "value");

        assertThat(myMap, hasEntry(1, "value"));
    }

 

  • hasKey()

Creates a matcher for Maps matching when the examined Map contains at least one key that satisfies the specified matcher.

  @Test
    public void hasKeyExample() {

        Map<Integer, String> myMap = new HashMap<>();
        myMap.put(1, "value");
        myMap.put(2, "anotherValue");

        assertThat(myMap, hasKey(1));

 

  • hasValue()

Creates a matcher for Maps matching when the examined Map contains at least one value that is equal to the specified value.

   @Test
    public void hasValueExample() {

        Map<Integer, String> myMap = new HashMap<>();
        myMap.put(1, "value");
        myMap.put(2, "anotherValue");

        assertThat(myMap, hasValue("anotherValue"));
    }

 

  • hasItem()

Creates a matcher for Iterables that only matches when a single pass over the examined Iterable yields at least one item that is equal to the specified item. Whilst matching, the traversal of the examined Iterable will stop as soon as a matching item is found.

   @Test
    public void hasItemExample() {

        List<Integer> testList = Arrays.asList(50, 60, 4, 67, 9);

        assertThat(testList, hasItem(67));

    }

 

  • hasItems()

Creates a matcher for Iterables that matches when consecutive passes over the examined Iterable yield at least one item that is equal to the corresponding item from the specified items. Whilst matching, each traversal of the examined Iterable will stop as soon as a matching item is found.

    @Test
    public void hasItemsExample() {

        List<Integer> testList = Arrays.asList(50, 60, 4, 67, 9);

        assertThat(testList, hasItems(67, 60));
    }

 

  • hasSize()

Creates a matcher for Collections that matches when the size() method returns a value equal to the specified size.

   @Test
    public void hasSizeExample() {

        List<Integer> testList = Arrays.asList(50, 60, 4, 67, 9);

        assertThat(testList, hasSize(5));
    }

 

  • instanceOf()

Creates a matcher that matches when the examined object is an instance of the specified type.

   @Test
    public void instanceOfExample() {

        Object integer = 5;

        assertThat(integer, instanceOf(Integer.class));
    }