draptik

mostly tech stuff

Node.js Backend Providing REST

TL;DR

My AngularJS demo app has a new backend implementation using node.js.

After some reading I decided I’ll stick with node’s express module.

OK. Here is a ‘minimal’ setup for a node.js server:

app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
var express = require('express');
var app = express();

//CORS middleware
var allowCrossDomain = function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, X-XSRF-TOKEN');
  next();
};

app.use(allowCrossDomain);

/* we'll use the same port as tomcat... */
var MY_PORT = 8080; // default: 4730


/* REST API =========================================== */
var baseUrl = '/ngdemo/web';

/* GET ALL -------------------------------------------- */
app.get(baseUrl + '/users', function(req, res) {
  res.json(userRepository.getAll());
});

/* GET Dummy ------------------------------------------ */
app.get(baseUrl + '/dummy', function(req, res) {
  res.json({id: 0, firstName: 'JonFromREST', lastName: 'DoeFromREST'});
});

/* GET By Id ------------------------------------------ */
app.get(baseUrl + '/users/:id', function(req, res) {
  console.log('trying to retrieve user with id: ' + req.params.id);
  var user = userRepository.getById(req.params.id);
  res.json(user);
});


/* POST Create ---------------------------------------- */
app.post(baseUrl + '/users', function(req, res) {
  if(!req.body.hasOwnProperty('firstName') || !req.body.hasOwnProperty('lastName')) {
    res.statusCode = 400;
    return res.send('Error 400: POST syntax incorrect.');
  }

  var newUser = userRepository.addNewUser(req.body.firstName, req.body.lastName);
  res.json(newUser);
});

/* PUT (Update) --------------------------------------- */
app.put(baseUrl + '/users/:id', function (req, res) {
  if(!req.body.hasOwnProperty('id') || !req.body.hasOwnProperty('firstName') || !req.body.hasOwnProperty('lastName')) {
    res.statusCode = 400;
    return res.send('Error 400: PUT syntax incorrect.');
  }
  var changedUser = userRepository.changeUser(req.params.id, req.body.firstName, req.body.lastName);
  res.json(changedUser);
});

/* DELETE --------------------------------------------- */
app.delete(baseUrl + '/users/:id', function(req, res) {
  console.log('trying to delete user with id: ' + req.params.id);
  userRepository.deleteUser(req.params.id);
  res.json(true);
});

/* ==================================================== */

app.listen(process.env.PORT || MY_PORT);

/* Mmmhh... how can I place the code below into a seperate file and load it here? */

function User(id, firstName, lastName) {
  this.id = id;
  this.firstName = firstName;
  this.lastName = lastName;
};


function UserRepository() {

  this.users = [];

  this.createUsers = function() {
    var numberOfUsers = 10;
    for (var i = 0; i < numberOfUsers; i++) {
      var id = i + 1;
      this.users.push(new User(id, 'Foo' + id, 'Bar' + id));
    };
    return this.users;
  };

  this.getMaxUserId = function() {
    return Math.max.apply(Math, this.users.map(function(user) {
      return user.id;
    }));
  };

  this.getNumberOfUsers = function() {
    return this.users.length;
  };

  this.getAll = function() {
    return this.users;
  };

  this.getById = function(id) {
    var foundUser = false;
    for (var i = 0; i < this.users.length; i++) {
      var user = this.users[i];
      console.log('...checking user.id ' + user.id);
      if (user.id == id) {
        foundUser = true;
        return user;
      };
    };
    if (!foundUser) {
      console.log('Could not find user with id: ' + id);
      return 'user with id ' + id + ' not found.';
    };
  };

  this.addNewUser = function(firstName, lastName) {
    var newUser = new User(this.getMaxUserId() + 1, firstName, lastName);
    this.users.push(newUser);
    return this.getById(newUser.id);
  };

  this.changeUser = function(id, firstName, lastName) {
    var user = this.getById(id);
    user.firstName = firstName;
    user.lastName = lastName;
    return user;
  };

  this.deleteUser = function(id) {
    // sorry, i'm tired and don't know javascript that well...
    var indexToDelete = -1;
    for (var i = 0; i < this.users.length; i++) {
      var user = this.users[i];
      if (user.id == id) {
        indexToDelete = i;
        break;
      };
    };

    if (indexToDelete >= 0) {
      this.users.splice(indexToDelete, 1);
    };
  };
};

As you can see I am just dabbling with JS…

But hey: It works! ;–)

Source code for this post

You can clone a copy of this project here: https://github.com/draptik/angulardemorestful.

To checkout the correct version for this demo, use the following code:

1
2
3
git clone git@github.com:draptik/angulardemorestful.git
cd angulardemorestful
git checkout -f step6-nodejs-backend

In case you are not using git you can also download the project as ZIP or tar.gz file here: https://github.com/draptik/angulardemorestful/releases/tag/step6-nodejs-backend

AngularJS and CORS

TL;DR

While splitting my AngularJS demo app into independent back- and frontend projects (running two different servers) I stumbled across cross domain issues during development. This post describes how to implement CORS on the server and/or client side of an application.

This post describes how I split the backend and frontend of my AngularJS demo app into separate applications.

Hopefully this will simplify switching the used backend technology in the future (i.e. replacing Java with .NET or Node.JS).

New directory structure

To begin with I created two new top level folders: backend/java-backend and frontend. Then I moved all Java code (including Java IDE settings, pom.xml, etc.) to the new java-backend folder. Since the frontend code (JS, CSS, HTML templates) was previously located in src/main/webapp I moved it to the new frontend folder.

The Java project folder webapp now only contains the following minimal setup:

1
2
3
4
backend/java-backend/src/main/webapp
├── index.jsp
└── WEB-INF
    └── web.xml

The frontend folder now has the following (simplified) structure:

1
2
3
4
5
6
7
8
9
10
11
12
frontend/
├── css
├── frontend-web-server.js
├── index.html
├── js
│   ├── app.js
│   ├── bootstrap
│   ├── controllers.js
│   ├── directives.js
│   ├── filters.js
│   ├── jquery
│   └── services.js

Frontend web server

Not knowing anything about node.js (yet), I just copied the simple web server from Google’s AngularJS demo application PhoneCat to frontend/frontend-web-server.js. For this server to run you will have to install node.js on your system. The frontend server will be running on port 8000.

frontend-web-server.js
1
2
3
// ...
var DEFAULT_PORT = 8000; // <-- frontend port
// ...

We can start/stop the frontend web server using the scripts start_frontend_server.sh and stop_frontend_server.sh.

Backend web server

The backend server is Tomcat. We can start/stop the backend web server using the scripts start_java_backend.sh and stop_java_backend.sh. The backend server will be running on port 8080.

CORS configuration

Cross-origin resource sharing (CORS) allows Javascript to make requests to other domains. Compared to JSONP which only allows the GET HTTP verb, CORS allows all HTTP verbs (GET, POST, PUT, DELETE), making it an ideal candidate for RESTful services. The only drawback: CORS requires a modern browser (see Wikipedia for details).

2014-01-03 CORS can be configured on the server and/or the client side. CORS must be configured on the server and the client side (Thanks to Richard for the pointer!). The following example demonstrates both approaches this.

Server side configuration example (Java)

The basic idea is to add additional header information to the different Access-Control-Allow-* properties of the HTTP response.

ResponseCorsFilter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package ngdemo.web.rest;

import com.google.inject.Singleton;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
    Allow CORS requests.
 */
@Singleton
public class ResponseCorsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException { }

    @Override
    public void destroy() { }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (servletResponse instanceof HttpServletResponse) {
            HttpServletResponse alteredResponse = ((HttpServletResponse) servletResponse);
            addHeadersFor200Response(alteredResponse);
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    private void addHeadersFor200Response(HttpServletResponse response) {
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With");
        response.addHeader("Access-Control-Allow-Headers", "GET, PUT, OPTIONS, X-XSRF-TOKEN");
    }
}

The above filter is used in the Guice configuration via the filter(...).through(...) method:

NgDemoApplicationSetup.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package ngdemo.infrastructure;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import ngdemo.web.rest.ResponseCorsFilter;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

public class NgDemoApplicationSetup extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new ServletModule() {

            @Override
            protected void configureServlets() {
                super.configureServlets();
                ResourceConfig resourceConfig = new PackagesResourceConfig("ngdemo/web");
                for (Class<?> resource : resourceConfig.getClasses()) {
                    bind(resource);
                }
                bind(JacksonJsonProvider.class).in(Scopes.SINGLETON);
                serve("/web/*").with(GuiceContainer.class);

                // CORS filter:
                filter("/web/*").through(ResponseCorsFilter.class);
            }
        });
    }
}

Client side configuration example (Javascript)

The opposite approach, configuring the client instead of the server, works by (1) setting the useXDomain property to true and (2) removing header properties.

app.js
1
2
3
4
5
6
7
8
9
10
11
'use strict';

angular.module('ngdemo')
        .config(['$httpProvider', function ($httpProvider) {
        // ...

        // delete header from client:
        // http://stackoverflow.com/questions/17289195/angularjs-post-data-to-external-rest-api
        $httpProvider.defaults.useXDomain = true;
        delete $httpProvider.defaults.headers.common['X-Requested-With'];
    }]);

Source code for this post

You can clone a copy of this project here: https://github.com/draptik/angulardemorestful.

To checkout the correct version for this demo, use the following code:

1
2
3
git clone git@github.com:draptik/angulardemorestful.git
cd angulardemorestful
git checkout -f step5-split-frontend-backend-cors

In case you are not using git you can also download the project as ZIP or tar.gz file here: https://github.com/draptik/angulardemorestful/releases/tag/step5-split-frontend-backend-cors

Link Collection #1

When I try to learn new technologies I stumble across many sites. If a site is useful to the problem at hand, I normally add a comment in my source code referencing the URL.

But if I have the same question again a year (or a week) later, I forgot where I put the URL. Then I start to go through my browser’s bookmarks and history. Then I look through the multitude of ‘read-it-later’ services, such as Pocket, Evernote, etc. Sometimes I’m lucky and I actually find what I’m looking for. Most of the time I end up repeating the same search procedure again (which is a waste of time).

So here’s my first link collection. Themed AngularJS…

RESTful CRUD With AngularJS

This post will show how to perform typical CRUD (create, read, update and delete) operations in AngularJS when consuming a RESTful web service.

A prerequisite for this demo is a working RESTful web service. For a basic introduction on creating a Java based RESTful web service, see my introduction on how to consume a RESTful web service with AngularJS created by a Java backend. For completeness sake I’ve added a Java based sample at the end of this post.

Frontend (AngularJS)

Views (Partials)

We will create three views.

The first view will display all users (user-list.html):

The view also provides links to edit (ng-click="editUser(user.id)") and delete (ng-click="deleteUser(user.id)") specific users as well as a link to create a new user (ng-click="createUser()").

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="span6">
    <table class="table table-striped table-condensed">
        <thead>
        <tr>
            <th style="min-width: 80px;">First name</th>
            <th style="min-width: 80px;">Last name</th>
            <th style="width:20px;"> </th>
            <th style="width:20px;"> </th>
        </tr>
        </thead>
        <tbody>
        <tr ng-repeat="user in users">
            <td>{{ user.firstName }}</td>
            <td>{{ user.lastName }}</td>
            <td><a ng-click="editUser(user.id)" class="btn btn-small btn-primary">edit</a></td>
            <td><a ng-click="deleteUser(user.id)" class="btn btn-small btn-danger">delete</a></td>
        </tr>
        </tbody>
    </table>
    <a ng-click="createNewUser()" class="btn btn-small">create new user</a>
</div>

The second and third view (user-detail.html and user-creation.html) both provide a form for entering the user properties.

They only differ in the actions provided. These actions (cancel(), updateUser(), createNewUser()) are invoked using ng-click:

user-[detail|creation].html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div class="container">
    <h1>User detail</h1>

    <form novalidate="novalidate" class="form-horizontal">
        <div class="control-group">
            <label class="control-label" for="inputFirstName">First name:</label>
            <div class="controls">
                <input type="text" id="inputFirstName" ng-model="user.firstName"/>
            </div>
        </div>
        <div class="control-group">
            <label class="control-label" for="inputLastName">Last name:</label>
            <div class="controls">
                <input type="text" id="inputLastName" ng-model="user.lastName"/>
            </div>
        </div>
        <div class="control-group">
            <div class="controls">
              <!-- user-detail.html: -->
                <a ng-click="cancel()" class="btn btn-small">cancel</a>
                <a ng-click="updateUser()" class="btn btn-small btn-primary">update user</a>

              <!-- user-creation.html: -->
                <a ng-click="createNewUser()" class="btn btn-small btn-primary">create new user</a>
            </div>
        </div>
    </form>
</div>

Controller

Next we will create three controllers corresponding to the three views.

UserListCtrl

UserListCtrl provides three functions editUser, deleteUser and createUser.

  • editUser and createUser merely redirect to a different partial view using AngularJs’s $location function.
  • deleteUser calls the UserFactory service method delete (which we will create shortly).

Furthermore the $scope.users is filled with the result from the UsersFactory.query() function.

Note that all required dependencies are injected into the controller’s signature (function ($scope, UsersFactory, UserFactory, $location)).

controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var app = angular.module('ngdemo.controllers', []);

app.controller('UserListCtrl', ['$scope', 'UsersFactory', 'UserFactory', '$location',
    function ($scope, UsersFactory, UserFactory, $location) {

        // callback for ng-click 'editUser':
        $scope.editUser = function (userId) {
            $location.path('/user-detail/' + userId);
        };

        // callback for ng-click 'deleteUser':
        $scope.deleteUser = function (userId) {
            UserFactory.delete({ id: userId });
            $scope.users = UsersFactory.query();
        };

        // callback for ng-click 'createUser':
        $scope.createNewUser = function () {
            $location.path('/user-creation');
        };

        $scope.users = UsersFactory.query();
    }]);
  /* ... */

UserDetailCtrl and UserCreationCtrl

UserDetailCtrl provides the function updateUser, which in turn invokes the service method UserFactory.update. The $scope.user is filled with the result from calling UserFactory.show. cancel is just a convenient link redirecting back to the user-list view.

UserCreationCtrl provides the function createNewUser, calling UsersFactory.create.

Again, both controllers use $location to redirect back to the user-list partial view.

controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* ... */
app.controller('UserDetailCtrl', ['$scope', '$routeParams', 'UserFactory', '$location',
    function ($scope, $routeParams, UserFactory, $location) {

        // callback for ng-click 'updateUser':
        $scope.updateUser = function () {
            UserFactory.update($scope.user);
            $location.path('/user-list');
        };

        // callback for ng-click 'cancel':
        $scope.cancel = function () {
            $location.path('/user-list');
        };

        $scope.user = UserFactory.show({id: $routeParams.id});
    }]);

app.controller('UserCreationCtrl', ['$scope', 'UsersFactory', '$location',
    function ($scope, UsersFactory, $location) {

        // callback for ng-click 'createNewUser':
        $scope.createNewUser = function () {
            UsersFactory.create($scope.user);
            $location.path('/user-list');
        }
    }]);

Don’t forget to map the views to the corresponding controllers in app.js using the $routeProvider:

app.js
1
2
3
4
5
6
7
angular.module('ngdemo', ['ngdemo.filters', 'ngdemo.services', 'ngdemo.directives', 'ngdemo.controllers']).
    config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/user-list', {templateUrl: 'partials/user-list.html', controller: 'UserListCtrl'});
        $routeProvider.when('/user-detail/:id', {templateUrl: 'partials/user-detail.html', controller: 'UserDetailCtrl'});
        $routeProvider.when('/user-creation', {templateUrl: 'partials/user-creation.html', controller: 'UserCreationCtrl'});
        $routeProvider.otherwise({redirectTo: '/user-list'});
    }]);

Service

AngularJS can consume the web service using $resource. This module is injected via 'ngResource'.

We create two factories:

  • UsersFactory (note the plural s) calls the web service with methods not requiring an id (query and create).
  • UserFactory calls the web service with methods requiring a user id (show, update and delete).
services.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var services = angular.module('ngdemo.services', ['ngResource']);

services.factory('UsersFactory', function ($resource) {
    return $resource('/ngdemo/web/users', {}, {
        query: { method: 'GET', isArray: true },
        create: { method: 'POST' }
    })
});

services.factory('UserFactory', function ($resource) {
    return $resource('/ngdemo/web/users/:id', {}, {
        show: { method: 'GET' },
        update: { method: 'PUT', params: {id: '@id'} },
        delete: { method: 'DELETE', params: {id: '@id'} }
    })
});

Backend (Java)

Here is an example of a RESTful web service created with Java:

UserRestService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package ngdemo.web.rest;

import com.google.inject.Inject;
import ngdemo.domain.User;
import ngdemo.service.contract.UserService;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;

@Path("/users")
public class UserRestService {

    private final UserService userService;

    @Inject
    public UserRestService(UserService userService) {
        this.userService = userService;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<User> getAllUsersInJSON() {
        return userService.getAllUsers();
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User getUserById(@PathParam("id") int id) {
        return userService.getById(id);
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public User create(User user) {
        return userService.createNewUser(user);
    }

    @PUT
    @Path("{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public User update(User user) {
        return userService.update(user);
    }

    @DELETE
    @Path("{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public void remove(@PathParam("id") int id) {
        userService.remove(id);
    }
}

You can clone a copy of this project here: https://github.com/draptik/angulardemorestful.

To checkout the correct version for this demo, use the following code:

1
2
3
git clone git@github.com:draptik/angulardemorestful.git
cd angulardemorestful
git checkout -f step4-angularjs-crud

In case you are not using git you can also download the project as ZIP or tar.gz file here: https://github.com/draptik/angulardemorestful/releases/tag/step4-angularjs-crud

Unit Testing RESTful Services

In my two previous posts I gave an introduction on how to consume a RESTful web service with AngularJS created by a Java backend and use Guice in the Java backend.

In this post I will show how to create a unit test for this web service.

Most of this code is inspired by a blog post from Paulo Renato de Athaydes.

We will need to install some new dependencies:

  • jetty-maven-plugin
  • junit
  • jersey-client
  • jersey-grizzly2

Grizzly will be our web server for testing.

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
<?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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <name>ngdemo Maven Webapp</name>
    <groupId>ngdemo</groupId>
    <artifactId>ngdemo</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jersey.version>1.17.1</jersey.version>
        <guice.version>3.0</guice.version>
    </properties>

    <build>
        <finalName>ngdemo</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>8.1.11.v20130520</version>
                <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <connectors>
                        <connector implementation="org.eclipse.jetty.nio.SelectChannelConnector">
                            <port>8080</port>
                            <maxIdleTime>60000</maxIdleTime>
                        </connector>
                    </connectors>
                    <stopKey/>
                    <stopPort/>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>

        <!-- javax: XML binding -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.1</version>
        </dependency>

        <!-- RESTful web service: Jersey ====================================== -->
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-server</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-servlet</artifactId>
            <version>${jersey.version}</version>
        </dependency>
        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-json</artifactId>
            <version>${jersey.version}</version>
        </dependency>

        <!-- Guice ============================================================= -->
        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>${guice.version}</version>
        </dependency>

        <dependency>
            <groupId>com.google.inject.extensions</groupId>
            <artifactId>guice-servlet</artifactId>
            <version>${guice.version}</version>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-guice</artifactId>
            <version>${jersey.version}</version>
        </dependency>

        <!-- Required for bypassing web.xml via Guice. Used in TestServlet.java -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>


        <!-- Unit testing ====================================================== -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-client</artifactId>
            <version>${jersey.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.sun.jersey</groupId>
            <artifactId>jersey-grizzly2</artifactId>
            <version>${jersey.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Our class under test is UserRestService.java:

UserRestService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package ngdemo.rest;

import com.google.inject.Inject;
import ngdemo.domain.User;
import ngdemo.service.contract.UserService;

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


@Path("/users")
public class UserRestService {

    private final UserService userService;

    @Inject
    public UserRestService(UserService userService) {
        this.userService = userService;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User getDefaultUserInJSON() {
        return userService.getDefaultUser();
    }
}

Here is the corresponding unit test class UserRestServiceTest.java:

UserRestServiceTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package ngdemo.tests;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory;
import com.sun.jersey.guice.spi.container.GuiceComponentProviderFactory;
import ngdemo.repositories.contract.UserRepository;
import ngdemo.repositories.impl.UserRepositoryImpl;
import ngdemo.service.contract.UserService;
import ngdemo.service.impl.UserServiceImpl;
import org.glassfish.grizzly.http.server.HttpServer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;

import static junit.framework.Assert.assertEquals;

public class UserRestServiceTest {

    static final URI BASE_URI = getBaseURI();
    HttpServer server;

    private static URI getBaseURI() {
        return UriBuilder.fromUri("http://localhost/").port(9998).build();
    }

    @Before
    public void startServer() throws IOException {
        System.out.println("Starting grizzly...");

        Injector injector = Guice.createInjector(new ServletModule() {
            @Override
            protected void configureServlets() {
                bind(UserService.class).to(UserServiceImpl.class);
                bind(UserRepository.class).to(UserRepositoryImpl.class);
            }
        });

        ResourceConfig rc = new PackagesResourceConfig("ngdemo.rest");
        IoCComponentProviderFactory ioc = new GuiceComponentProviderFactory(rc, injector);
        server = GrizzlyServerFactory.createHttpServer(BASE_URI + "rest/", rc, ioc);

        System.out.println(String.format("Jersey app started with WADL available at "
                + "%srest/application.wadl\nTry out %sngdemo\nHit enter to stop it...",
                BASE_URI, BASE_URI));
    }

    @After
    public void stopServer() {
        server.stop();
    }

    @Test
    public void testGetDefaultUser() throws IOException {
        Client client = Client.create(new DefaultClientConfig());
        WebResource service = client.resource(getBaseURI());
        ClientResponse resp = service.path("rest").path("users")
                .accept(MediaType.APPLICATION_JSON)
                .get(ClientResponse.class);
        System.out.println("Got stuff: " + resp);
        String text = resp.getEntity(String.class);

        assertEquals(200, resp.getStatus());
        assertEquals("{\"firstName\":\"JonFromREST\",\"lastName\":\"DoeFromREST\"}", text);
    }
}

In the startServer method we create an injector for Guice, which we can then pass into the GuiceComponentProviderFactory to create the inversion of control (IoC) container.

Together with the ResourceConfig the IoC container is passed to Grizzly’s server factory to create the web server for testing.

Within the actual test method testGetDefaultUser we only have to setup the Jersey Client to retrieve the response (from the Grizzly server).

Here’s the test output from Maven:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ mvn test
...
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running ngdemo.tests.UserRestServiceTest
Starting grizzly...
Jul 19, 2013 1:50:35 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  ngdemo.rest
Jul 19, 2013 1:50:35 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
  class ngdemo.rest.UserRestService
Jul 19, 2013 1:50:35 PM com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Jul 19, 2013 1:50:35 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.17.1 02/28/2013 12:47 PM'
Jul 19, 2013 1:50:36 PM com.sun.jersey.guice.spi.container.GuiceComponentProviderFactory getComponentProvider
INFO: Binding ngdemo.rest.UserRestService to GuiceInstantiatedComponentProvider
Jul 19, 2013 1:50:37 PM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [localhost:9998]
Jul 19, 2013 1:50:37 PM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Jersey app started with WADL available at http://localhost:9998/rest/application.wadl
Try out http://localhost:9998/ngdemo
Hit enter to stop it...
Got stuff: GET http://localhost:9998/rest/users returned a response status of 200 OK
Jul 19, 2013 1:50:37 PM org.glassfish.grizzly.http.server.NetworkListener stop
INFO: Stopped listener bound to [localhost:9998]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.604 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.378s

Test time is 2.6 sec. Not bad considering we are starting a web server, deploying our app, creating a client, running the test and shutting down the web server.

Running this test from within IntelliJ takes: 0.009 sec…

You can clone a copy of this project here: https://github.com/draptik/angulardemorestful.

To checkout the correct version for this demo, use the following code:

1
2
3
git clone git@github.com:draptik/angulardemorestful.git
cd angulardemorestful
git checkout -f step3-backend-test

In case you are not using git you can also download the project as ZIP or tar.gz file here: https://github.com/draptik/angulardemorestful/releases/tag/step3-backend-test

Guice in Java Web Application

Google’s Guice framework promises to be a lightweight(!) Inversion-of-Control (IoC) container.

Advantages compared to Spring:

  • Spring is much more than an IoC container, and therefore overkill for many projects.
  • Configuration by code. NO XML.

Based on my previous post showing how to use AngularJS with a Java RESTful backend I extended the simple demo application to use Guice.

Let’s say we have a UserServiceImpl class which depends on a UserFactory interface. The UserFactory interface is injected into the constructor of the UserServiceImpl class.

The only thing we have to do is add the @Inject annotation to the constructor so that Guice can do its job.

UserServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package ngdemo.service.impl;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import ngdemo.domain.User;
import ngdemo.service.contract.UserFactory;
import ngdemo.service.contract.UserService;

import java.util.List;

public class UserServiceImpl implements UserService {

    private final UserFactory userFactory;

    @Inject
    public UserServiceImpl(UserFactory userFactory) {
        this.userFactory = userFactory;
    }

    @Override
    public List<User> getDefaultUsers() {
        return this.userFactory.createUsers();
    }

    @Override
    public User getDefaultUser() {
        return this.userFactory.createUser();
    }

}

For the IoC container to know which implementation to inject we have to create a Guice Module which derives from AbstractModule:

UserModule.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package ngdemo.infrastructure;

import com.google.inject.AbstractModule;
import ngdemo.service.contract.UserFactory;
import ngdemo.service.contract.UserService;
import ngdemo.service.impl.UserFactoryImpl;
import ngdemo.service.impl.UserServiceImpl;

public class UserModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(UserFactory.class).to(UserFactoryImpl.class);
        bind(UserService.class).to(UserServiceImpl.class);
    }
}

The UserModule class demonstrates the advantage of Guice vs. Spring: NO XML. When using Spring you normally would have to create Spring beans in an XML file like this:

applicationContext.xml (pseudo code)
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <bean id="userService" class="ngdemo.service.impl.UserServiceImpl">
      <constructor-arg ref="userFactory"/>
  </bean>
  
  <bean id="userFactory" class="ngdemo.service.impl.UserFactoryImpl" />
      
</beans>

Next we have to create a replacement for the servlets required by the servlet container:

ngdemo.infrastructure.NgDemoApplicationSetup.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package ngdemo.infrastructure;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

public class NgDemoApplicationSetup extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {

        return Guice.createInjector(new ServletModule() {

            @Override
            protected void configureServlets() {

                super.configureServlets();

                // Configuring Jersey via Guice:
                ResourceConfig resourceConfig = new PackagesResourceConfig("ngdemo/rest");
                for (Class<?> resource : resourceConfig.getClasses()) {
                    bind(resource);
                }
                serve("/rest/*").with(GuiceContainer.class);
            }
        }, new UserModule()); // <-- Adding other Guice Dependency Injection Modules
    }
}

And finally the file web.xml:

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app id="WebApp_ID" version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
  http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Restful Web Application</display-name>

    <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>ngdemo.infrastructure.NgDemoApplicationSetup</listener-class>
    </listener>
</web-app>

The file web.xml is now free of any <servlet> tags. The only thing that has to be configured in XML is the <listener-class>. The value of the <listener-class> is our Java class NgDemoApplicationSetup, so all further configuration can be defined in a type safe manner.

You can clone a copy of this project here: https://github.com/draptik/angulardemorestful.

To checkout the correct version for this demo, use the following code:

1
2
3
git clone git@github.com:draptik/angulardemorestful.git
cd angulardemorestful
git checkout -f step2-guice

In case you are not using git you can also download the project as ZIP or tar.gz file here: https://github.com/draptik/angulardemorestful/releases/tag/step2-guice

AngularJS Example Using a Java RESTful Web Service

AngularJS is the current MVV-Whatever JavaScript framework by Google. Among other things, it provides bidirectional data binding.

Although I’m neither a Java nor a JavaScript expert, I choose the following scenario for my ‘Hello-World’ example:

  1. Java backend provides a RESTful web service.

  2. AngularJS consumes the web service.

That’s it.

Project structure

I intentionally put the backend and frontend code in the same project to simplify the example. In a real project you probably want to have seperate projects for front- and backend.

1
2
3
4
5
6
7
8
+---------------------------------------------------+
| demo project                                      |
|                                                   |
| +----------------+              +---------------+ |
| | backend (Java) | < -(REST)- > | frontend (JS) | |
| +----------------+              +---------------+ |
|                                                   |
+---------------------------------------------------+

Since the backend is Java based, I used a Maven default structure (maven-archetype-site-simple):

project structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
├── _documentation
│   └── readme.txt
├── ngdemo.iml
├── pom.xml
└── src
    └── main
        ├── java
        │   └── ngdemo
        │       ├── domain
        │       │   └── User.java
        │       ├── rest
        │       │   └── UserRestService.java
        │       └── service
        │           └── UserService.java
        └── webapp
            ├── css
            │   └── app.css
            ├── img
            ├── index-async.html
            ├── index.html
            ├── index.jsp
            ├── js
            │   ├── app.js
            │   ├── controllers.js
            │   ├── directives.js
            │   ├── filters.js
            │   └── services.js
            ├── lib
            │   └── angular
            │       ├── angular-cookies.js
            │       ├── angular-cookies.min.js
            │       ├── angular.js
            │       ├── angular-loader.js
            │       ├── angular-loader.min.js
            │       ├── angular.min.js
            │       ├── angular-resource.js
            │       ├── angular-resource.min.js
            │       ├── angular-sanitize.js
            │       ├── angular-sanitize.min.js
            │       └── version.txt
            ├── partials
            │   └── partial1.html
            └── WEB-INF
                └── web.xml

src/main/java is the backend.

src/main/webapp/js is the frontend.

src/main/webapp/ also includes a copy of angular-seed.

RESTful web service (backend)

Jersey is the Java reference implementation for providing REST.

Install the following dependencies in your pom.xml:

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- .. -->
<!-- RESTful web service: Jersey -->
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-server</artifactId>
    <version>1.17.1</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-servlet</artifactId>
    <version>1.17.1</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.17.1</version>
</dependency>
<!-- .. -->

Add the following servlet snippet to your web.xml:

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- .. -->
<servlet>
    <servlet-name>jersey-serlvet</servlet-name>

    <servlet-class>
        com.sun.jersey.spi.container.servlet.ServletContainer
    </servlet-class>

    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>ngdemo.rest</param-value>
    </init-param>

    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>jersey-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<!-- .. -->

Enough configuration for now: Create a simple User object…

User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package ngdemo.domain;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class User {

    private String firstName;
    private String lastName;

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

…and a service class…

UserService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
package ngdemo.service;

import ngdemo.domain.User;

public class UserService {

    public User getDefaultUser() {
        User user = new User();
        user.setFirstName("JonFromREST");
        user.setLastName("DoeFromREST");
        return user;
    }
}

…and finally the RESTful Service… (Update 2015-08-07 small fix, thanks Jason):

UserRestService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package ngdemo.rest;

import ngdemo.domain.User;
import ngdemo.service.UserService;

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

@Path("/users")
public class UserRestService {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User getDefaultUserInJSON() {
        UserService userService = new UserService();
        return userService.getDefaultUser();
    }
}

Converting the User object to JSON via @Produces(MediaType.APPLICATION_JSON) requires jersey-json in web.xml (POJOMappingFeature).

Consuming web service from AngularJS (frontend)

Don’t forget to add angular-resources.js to your index.html

Consuming the web service:

services.js
1
2
3
4
5
6
7
8
9
10
11
var services = angular.module('ngdemo.services', ['ngResource']);

services.factory('UserFactory', function ($resource) {
    return $resource('/ngdemo/rest/users', {}, {
        query: {
            method: 'GET',
            params: {},
            isArray: false
        }
    })
});

Usage in controller:

controller.js
1
2
3
4
5
6
7
var app = angular.module('ngdemo.controllers', []);

app.controller('MyCtrl1', ['$scope', 'UserFactory', function ($scope, UserFactory) {
    UserFactory.get({}, function (userFactory) {
        $scope.firstname = userFactory.firstName;
    })
}]);

Usage in view:

1
2
3
4
5
<div>
    <p>
        Result from RESTful service is: {{ firstname }}
    </p>
</div>

Et voila:

Update (2013-07-18):

You can clone a copy of this project here: https://github.com/draptik/angulardemorestful.

To checkout the correct version for this demo, use the following code:

1
2
3
git clone git@github.com:draptik/angulardemorestful.git
cd angulardemorestful
git checkout -f step1

In case you are not using git you can also download the project as ZIP or tar.gz file here: https://github.com/draptik/angulardemorestful/releases/tag/step1

Listing Environment Variables (Linux)

Just a note to self: Get a list of all linux environment variables and their values using the export command.

Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pi@rpidev ~ $ export
declare -x HOME="/home/pi"
declare -x JAVA_HOME="/opt/java/jdk1.7.0_25"
declare -x LANG="en_GB.UTF-8"
declare -x LOGNAME="pi"
declare -x LS_COLORS="rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:"
declare -x MAIL="/var/mail/pi"
declare -x OLDPWD="/home/pi/myinstall_notes"
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/opt/java/jdk1.7.0_25/bin"
declare -x PWD="/home/pi"
declare -x SHELL="/bin/bash"
declare -x SHLVL="1"
declare -x SSH_CLIENT="192.168.179.32 39295 22"
declare -x SSH_CONNECTION="192.168.179.32 39295 192.168.179.148 22"
declare -x SSH_TTY="/dev/pts/0"
declare -x TERM="xterm"
declare -x USER="pi"

ReorderList Demo for AjaxControlToolkit

AjaxControlToolkit’s ReorderList provides drag and drop functionality within a list.

ReorderListDemo.aspxSource Article
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="CssReorderList">
    <!-- ClientMode="AutoID" is required for certain versions of AjaxControlToolkit  -->
    <ajaxToolkit:ReorderList ID="MyReorderList" runat="server"
                             DataKeyField="MyId"
                             SortOrderField="MyPosition"
                             PostBackOnReorder="False"
                             ClientIDMode="AutoID"
                             DragHandleAlignment="Left"
                             ItemInsertLocation="Beginning"
                             AllowReorder="true"
        >
        <ItemTemplate>
            <div style="background-color: yellow;" class="CssItemArea">
                <asp:HiddenField runat="server" ID="hdfMyId"
                  Value="<%# ((DummyViewModel)Container.DataItem).MyId %>" />
                <asp:Label runat="server" ID="lblName"
                  Text="<%# ((DummyViewModel)Container.DataItem).MyName %>"/>
                <asp:Label runat="server" ID="lblPosition"
                  Text="<%# ((DummyViewModel)Container.DataItem).MyPosition %>"/>
            </div>
        </ItemTemplate>
        <DragHandleTemplate>
            <div class="CssDragHandle"><strong>DRAG ME</strong></div>
        </DragHandleTemplate>
    </ajaxToolkit:ReorderList>
<div>

I’ve placed a working example project on github.