Archive

Programming

NOTE : this article is still under construction. However, the theoretical parts are more or less finalized, so feel free to read and comment on those.

1. Introduction

The total number of websites worldwide is rapidly approaching a billion according to Internet Live Stats [1]. You might wonder why you should add any more websites to that number. Still, assuming that you have at least a general interest in how stuff works, I would argue that it is useful to have some knowledge about this kind of pervasive technology. As a result, we will try to provide a explanatory example on how a website can be created.

There are many web technologies, and the Internet itself is a complex distributed system in its own right [2]. In this post we will use the following technologies: PHP, HTML, JavaScript, CSS, and MySQL. Although there are many alternatives to PHP, PHP remains one of the most prominent server-side programming languages today with approximately 250 million PHP-based websites and counting [3]. It is also relatively easy to learn and deploy, and over the years some PHP-based frameworks have been introduced allowing easier development of larger scaled websites (e.g. Codeigniter, Symphony, Zend, …).

The rest of this article is organised as follows. First we will introduce our learning objectives in a more specific manner, and present the problem description for our example. Next we will discuss the web platform and related technologies in greater detail. Finally we will show and explain some of the implementation aspects of our solution.

1.1 Learning objectives

In this article we will try to reach the following learning objectives:

  • Build a basic understanding of the complete lifecycle of a web page;
  • Basic client-side programming with JavaScript, HTML and CSS;
  • Basic server-side programming with PHP and database access and modeling with MySQL.
  • Introducing some documentation for the design and implementation of websites;

I assume you already have some experience with one or more programming languages, e.g., Java, perl, C#, et cetera. Of course, you can always try to understand the code without any programming experience. All in all, you can consider source code to be a formal way of writing instructions.

2. Setting up a development environment

Before we start with the theory, we will set up our development environment. The reason for doing so, is that it might be easier to understand some concepts if you see them happening live in front of you.

Since we will be developing in PHP, we will need some platform to run our PHP scripts on. There are many alternatives to set up a development environment for building PHP websites alone. In this article we will discuss two. The first one is to run a local version of your website; the second option we will present here is to use a web server and run your application directly online.

To edit HTML, CSS, JavaScript and PHP files, there are quite a few alternatives. Personally I often use a combination of Netbeans (with PHP plugin installed) and Notepad++, but you can easily find other tools that work equally well or better.

2.1 Local

2.1.1 Windows

To run a PHP website locally, you can download a local server that can run PHP scripts. For the Windows platform you can use WampServer, which you can download here (link to website). Once you’ve installed the server, run it and navigate to http://localhost/ using your web browser. If all goes well, you should now see the default welcome page of your server.

To add web pages to your local server, go to the folder wamp/www/, create a subfolder test/ for example, and finally create a new web page in this folder, for example index.php. Add the contents of listing 1 to this page. If you then navigate to http://localhost/test/, you should see the text “Hello world!” printed on your screen.

<html>
	<head>
		<title>Hello world!</title>
	</head>
	<body>
<?php
echo '<p>Hello world!</p>';
?>
	</body>
</html>

2.2 Online

To run your PHP scripts directly online, you’ll need a webserver that can host and run PHP files. If you don’t own such a web server, there are a number of online hosting services which you can use. If you are merely experimenting with PHP, it is probably best you sign up for a free account. host-ed.net provides such services for example, among many others.

If you are using online services, you will probably need some way to upload files to the web server. You can do this via an FTP client. FileZilla is a free FTP client and is relatively easy to use.

Now create a folder on the web server and add a new web page index.php to this folder with listing 1 as its contents. If you then navigate to this web page via your browser, you should see the text “Hello world!” printed on your screen.

3. The web platform

3.1 The World Wide Web

A website can be described by its domain name, e.g., districted.wordpress.com and its contents, i.e., a collection documents such as HTML pages, scripts and additional resources like images and videos, that reside on one or more web servers.

A domain name can be thought of as a path in the domain name system‘s (DNS) hierarchy, in this case starting at com, then followed by wordpress, and ending in districted.

Each of the website’s pages can then be accessed using a URL (uniform resource locator), which consists of a communication protocol and a path name, relative to the domain name [4]. For example the page index.php on the domain districted.wordpress.com can be accessed using the HTTP communication protocol yielding the following URL: https://districted.wordpress.com/index.php.

In the following subsection we will describe HTTP in more detail. Next we will explain briefly how state can be preserved over stateless HTTP. This will conclude our brief introduction to the world wide web.

3.1.1 The Hypertext Transfer Protocol

Arguably the most important application layer protocol for the world wide web is called the Hypertext Transfer Protocol (HTTP). HTTP is a stateless application-level request-response protocol that normally runs over TCP [4].

HTTP request

HTTP request and response headers are written in ASCII, and the HTTP contents are given in a MIME type [4]. A general HTTP request consists out of a method, header and body, cf. figure 1. HTTP supports a variety of methods, but only the ones shown in table 1 matter in practice. Other methods are for example DELETE, PUT, and HEAD [4]. Table 2 shows some of the possible request header contents.

<METHOD> /path/to/resource?query_string HTTP/1.1
<header>*

<BODY>

Figure 1 : The general structure of a HTTP request.

Table 1 : Part of the HTTP request API, adapted from [4].
Operation Description
GET Read a web page. Intended for information retrieval Typically the body is empty.
POST Append to a web page. Intended for submitting information. Typically the body contains the submitted information.
Table 2 : Some HTTP request message headers, adapted from [4].
Header Type Meaning
Accept Request The type of pages the client can handle.
Accept-Charset Request The character sets that are acceptable to the client.
Host Request The server’s DNS name.
Authorization Request A list of the client’s credentials.
Referer Request The previous URL from which the request came.
Cookie Request Previously set cookie sent back to the server.
HTTP response

The general structure of an HTTP response is shown below in figure 2, and contains a status code and message, a header and body. Important HTTP response status codes are listed in table 3. Some of the possible contents of HTTP response headers are given in table 4.

HTTP/1.1 <STATUS CODE> <STATUS MESSAGE>
<header>*

<BODY>

Figure 2 : The general structure of an HTTP response.

Table 3 : Status code response groups, adapted from [4].
Code Meaning Examples
1xx Information 100 = server agrees to handle client’s request.
2xx Success 200 = request succeeded; 204 = no content present
3xx Redirection 301 = page moved; 304 = cached page still valid
4xx Client error 403 = forbidden page; 404 = page not found
5xx Server error 500 = interal server error; 503 = try again later
Table 4 : Some HTTP request message headers, adapted from [4].
Header Type Meaning
Set-Cookie Response Cookie for the client to store.
Server Response Information about the server.
Content-encoding Response How the content is encoded, e.g. gzip.
Content-type Response The page’s MIME type.
Last-modified Response The last time the page was changed.
Location Response Tells the client where to send its request.
HTTP in action

Now that we have covered what HTTP requests and responses look like, you can investigate them yourself. To do so, press the F12 function key if you are using Google Chrome (or right click the current web page and select “Inspect element”), and open the network tab. If you refresh the current page, you will see the how the browser will send and receive HTTP requests to and from the server. If you click one of the requests, you can see the request in more detail.

Figure 3 shows the general communication pattern for fetching a web page over HTTP. Note that we make an abstraction of the lower protocol layers (transport layer, network layer, …) when describing the communication channels and protocols (TCP, IP, …).

http request and response

Figure 3 : HTTP request and response.

3.1.2 Cookies

When a client visits a website, often some kind of state needs to be maintained, e.g., login data, shopping basket contents, et cetera. When a client performs some action on the website, the server will need some way to associate the client with his/her correct data located on the server.

The solution that is implemented in all modern browsers is the use of sessions through cookies. A cookie is a string that the server can associate with a browser [4]. As shown in table 4, the Set-Cookie header is used by the server to ask the client to set a cookie. In subsequent requests by the client, the cookie is then sent back using the Cookie header, as shown in table 2.

3.2 Web applications

In what follows we will introduce the technologies that we will actually be using during the rest of this article. However, note that there are many other technologies to construct web applications. Feel free to run the examples on your own local or online server.

3.2.1 HTML

HyperText markup language (HTML) is a markup language for creating web pages and other information that can be displayed in a web browser. HTML pages usually contain, or contain references to, CSS and JavaScript code. Since HTML is mainly concerned with the organization of the contents of the page, a good design practice is to include references to scripts and stylesheets rather than to directly code them into the page.

The structure of a page can be described by the document object model (DOM). This is a representation where an HTML document is organised in a hierarchical manner that is accessible to programs [4, 5]. The root of the page is a <html> element, containing a <head> and <body> element. In the <head> element the title, meta data, and references to scripts and stylesheets can be added. The <body> element contains the contents of the page, i.e., text, images, et cetera.

The formatting commands in HTML correspond to a set of tags with associated attributes [4]. These tags and attributes can be used in HTML to include pointers to, and content from other sites, e.g., the <a> tag with href attribute provides a clickable link to a URL, the <img> tag with src attribute links to an image that is automatically retrieved and displayed, the <script> tag with src and type attributes can link to a script that is automatically downloaded and executed, et cetera. A full overview of all tags and attributes can be found here (link to website). The W3C also provides a validation service for your HTML pages here (link to website).

There are also a number of optional attributes that can be added to any tag. The id and class attributes form the foundation for many of the dynamic web pages today, as well as styling through CSS. The id attribute uniquely identifies an element in an HTML page, whereas the class attribute can be assigned to a group of elements. In more complex designs, HTML elements often have multiple classes to allow incremental styling and scripting over groups of elements.

Listing 2 shows an example of a simple HTML web page. When developing a website, it is often handy to use dummy text for static pages if you don’t have any available yet. A well-known dummy text is called Lorem Ipsum, and can be found here (link to website). The resulting, rendered page is shown in figure 4.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>EXAMPLE</title>
        <meta name="description" content="Example web page"/>
        <meta name="keywords" content="html example" />
		<link rel="stylesheet" type="text/css" href="style.css" />
		<script type="text/javascript" src="script.js"></script>
    </head>
    <body>
		<!-- This is a comment. -->
		<div id="header">
			<h1>HTML Example</h1>
		</div>
        <div id="contents" class="bordered-dotted">
            <p>This is an example paragraph in an example <a href="http://en.wikipedia.org/wiki/HTML">HTML</a> web page. <b>Lorem Ipsum</b> is simply dummy text of the printing and typesetting industry. The following lists the first paragraph of the dummy text.</p>
			<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean at felis lobortis, molestie arcu ut, suscipit tellus. Duis venenatis facilisis sem, sit amet tristique leo eleifend vel. Curabitur mollis urna nec libero ultricies ultricies. Aliquam tempor at enim ac vestibulum. Suspendisse sit amet ligula eu diam varius laoreet in sit amet velit. Phasellus ante sapien, pellentesque sit amet dolor id, dapibus placerat tellus. Vivamus vel eros quis erat pellentesque tincidunt at non neque. Nulla malesuada pharetra mi vel feugiat.</p>
			<p>Aliquam tempus posuere augue, et lobortis enim venenatis ac. Vestibulum faucibus leo at malesuada scelerisque. Etiam at dolor dolor. Quisque at magna ac augue eleifend egestas at ut turpis. Proin euismod erat rhoncus odio interdum lacinia. Etiam ultricies tellus dolor, non porta felis ornare vel. Integer sed diam urna. Cras convallis at lacus ac egestas. Aliquam placerat non metus at sagittis. Morbi fermentum euismod arcu, vitae facilisis tortor sollicitudin sit amet. Cras iaculis dapibus quam vel rutrum.</p>
			<button onclick="changeBorder();">Change border</button>
			<button id="btn-new-p" onclick="addParagraph();">Add Paragraph</button>
        </div>
    </body>
</html>
rendered html page

Figure 4 : The rendered HTML page as shown in listing 2.

3.2.2 CSS

Where HTML is concerned with structure, CSS (Cascading Style Sheets) deals with the appearance of a web page. The syntax of a CSS rule consists of a list of affected HTML elements that can be addressed using HTML tags names and/or id and class names, followed by the body of the rule between braces. A single declaration, consists of the affected CSS property, e.g., height, width, color, et cetera, followed by a colon and the value, closed by a semicolon. Classes are referenced by a dot, ids by a number sign.

Note that the order of commands is important in CSS. For example the statement border: #222222 dotted 2px; in the first CSS rule is overruled by the second statement border-top: 0px;, which adjusts the top of the border. Secondly, the CSS selectors are also applied left-to-right and based on a hierarchy of priorities where the most specified selector prevails: a class will have priority over a tag name, and an id will have priority over a class.

A complete overview of CSS commands can be found here (link to website). An example is shown in listing 3 and references the HTML page in listing 2.

/* This is a comment. */
.bordered-dotted {
	border: #222222 dotted 2px;
	border-top: 0px;
}

.bordered-solid {
	border: #222222 solid 2px;
	border-top: 0px;
}

div#header {
	padding: 10px;
	background-color: #222222;
}

h1 {
	color: #eeeeee;
}

div#contents {
	padding: 10px;
	color: blue;
}

/* This rule will be applied, i.e, the text color will be black (not red or blue). */
#contents p {
	padding-bottom: 10px;
	color: black;
}

p {
	color: red;
}

a {
	color: #222222;
	text-decoration: none;
}

a:hover {
	color: black;
	text-decoration: underline;
}

a:visited {
	color: #222222;
	text-decoration: none;
}

When writing CSS rules, it is often useful to plan ahead. Make sure the structure of the HTML elements allows you to implement your design. Using <div> or <span> elements as containers for other elements that require specific styling, for example to let elements float next to each other, illustrates the close interaction between style and structure.

Nowadays browsers, such as Google Chrome or Firefox, provide built-in tools that can help for web development, especially for writing CSS code. When right-clicking an element, select the “Inspect element” option. Next, navigate through the HTML elements and observe how the styling attributes change. You can even add CSS declarations to the CSS rules via the interface.

3.2.3 PHP

PHP is a programming language used mainly for building websites. It is a scripting language, but can be used to create object-oriented programs as well. PHP runs on a web server. As illustrated in figure 5, when a client requests a PHP page, the PHP interpreter on the server will first run the PHP script, after which the server returns the resulting page via an HTTP response to the client [6].

http request for php page

Figure 5 : HTTP request for a page containing a PHP script that is run at the server before the result is sent back to the client.

PHP files have the .php extension. PHP scripts can be used to produce any text-based file format, including HTML and its variants, XML, JSON, CSV, plain text, CSS, JavaScript, and so on. Usually however, PHP files will contain plain HTML and JavaScript aside from the PHP commands themselves.

Syntax

PHP commands are placed between the PHP start <?php and end ?> delimiters. The syntax of PHP is similar to other programming languages like JavaScript or Perl, albeit with some particular differences.

Variables in PHP start with a dollar sign followed by at least one letter and other characters, e.g., $a, $a1, $a_var, $abc, et cetera, are all valid variable names. Note that variables don’t have explicit types, similar to Perl or JavaScript. PHP also has a number of built-in global variables. $_GET and $_POST are arrays that contain key-value pairs of the GET and POST requests respectively. The $_COOKIE and $_SESSION variables are also arrays containing all cookie and session key-value pairs respectively.

Conditional statements are similar to other programming languages. The ifelse construct evaluates a boolean statement and performs the body of the if construct if true, and the one from the else statement otherwise. In boolean statements, there are four values that are evaluated as “false”, namely: any variable or expression that returns 0, an empty string or a string containg 0, and finally the constant false itself [6]. For example, the expression 98*0+1 will yield true, while the expression 8-3*5+7 will yield false.

Loops are similar to other languages, again with while, for, and foreach, as familiar constructs.

Function declarations start with the function keyword, followed by the list of parameters between brackets. The body of the function is written between braces. A full list of built-in functions can be found here (link to website). Some interesting functions are print, echo, include, array_key_exists, strlen, trim, count, ini_set, session_start, setcookie, is_numeric, and so on.

Listing 4 shows an example PHP script where a new array is created and assigned to a variable $names, and finally printed using a function printArray. The printArray function prints an ordered list by looping through each element of the array and printing HTML tags around it. In listing 5, this script is directly included into a PHP page that already contains some HTML.

<?php
// This is a comment.
$names = array(	0 => "John",
				1 => "Francis",
				2 => "Martin",
				3 => "Peter"
);
printArray($names);

/**
 * Prints an ordered list in HTML code from the elements of the given array.
 * For each element a button is added to request more information.
 * @param $array the array of elements to print
 */
function printArray($array) {
	print ("<ol>");
	foreach ($array as $value) {
		print ("<li>" . $value
			. "<button onclick=\"getInfo('" . $value
			. "');\">info</button><div id=\"" . $value . "\"></div></li>");
	}
	print ("</ol>");
}
?>
<html>
	<head>
		<title>Example PHP</title>
		<script type="text/javascript" src="script2.js"></script>
	</head>
	<body>
		<h1>Names</h1>
<?php
include 'script.php';
?>
	</body>
</html>
Input handling

One of the most important differences between dynamic and static web pages, is that dynamic web pages allow user interaction beyond simple navigation. A web page can ask the user for specific input, e.g., login data, and also remember previous actions and choices.

Listing 6 shows a form with as method POST and action attribute the file form.php, which is in this case, the file itself. What will happen when the input element of the type submit is clicked, is that the contents of the other input elements, in this case the text input element with as name name, will be sent to the server via an HTTP request via the given method (POST) and target page (form.php). When the HTTP request arrives at this page, the PHP script in this page will be run at the server. The named parameter will be retrieved via the global parameter $_POST and printed on the page that is returned to the client’s browser.

<html>
	<head>
		<title>Example Form</title>
	</head>
	<body>
		<form method="POST" action="form.php">
			Enter your name :
			<input type="text" name="name"/>
			<input type="submit" value="Submit"/>
		</form>
<?php
if(isset($_POST["name"]) && !empty($_POST["name"]))
	print "<p>Hello, " . $_POST["name"] . "!</p>";
?>
	</body>
</html>

Of course, the example shown here is rather pointless. In practice, the input can be stored in the database or used to perform a search query, et cetera. Radiobuttons, password input, and other input types are all supported, so feel free to experiment.

3.2.4 JavaScript

JavaScript is a client-side scripting language. Like PHP, it can also be used in an object-oriented way, but for smaller projects, using separate functions is often more than enough.

Since JavaScript is run in the browser, the code has to be downloaded and run on the client-side. As a result, security and efficiency are important aspects of the execution of JavaScript. Like CSS, some characteristics are browser-dependent, which has led to the development of JavaScript libraries, e.g., jQuery, that try to provide transparancy at this level.

Syntax

The JavaScript syntax resembles some combination of PHP and Java. Note that variables are created using the var command and are not strongly typed.

Changing the DOM

To build interactive web pages, you’ll usually want to update only parts of a web page, instead of loading a whole new page. JavaScript enables you to do this, as it allows you to modify parts of a web page and even load new content from a remote server. The DOM object can be accessed through the public document variable. The document variable has a number of properties and methods associated with it, that will allow you to change the contents and structure of the web page. The full documentation for the HTML DOM Document object can be found here (link to website). For other JavaScript documentation, W3Schools provides an overview which can be found here (link to website).

Listing 7 shows a relatively simple script with a function to alter the class attribute of an HTML element, and another function to add and remove certain elements from the DOM tree of the HTML page.

function changeBorder() {
	var element = document.getElementById("contents");
	if (element.className == "bordered-solid")
		element.className = "bordered-dotted";
	else
		element.className = "bordered-solid";
}

function addParagraph() {
	// Create a paragraph with given text.
	var p = document.createElement("p");
	var node = document.createTextNode("Etiam ut mauris sed libero vulputate mollis vitae in nisi. Duis ultrices, mi sed scelerisque gravida, mi urna aliquam arcu, in gravida magna eros in orci. Integer fringilla, diam id consectetur tempor, mi libero luctus enim, vitae vulputate magna magna sed nibh. Etiam faucibus enim ut pharetra mattis. Nam gravida accumsan nisi, non fringilla sem ultricies sit amet. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec mi enim, commodo at vestibulum quis, auctor in libero.");
	p.appendChild(node);
	
	// Add the paragraph to the DOM.
	var element = document.getElementById("contents");
	element.appendChild(p);
	
	// Remove the button from the DOM.
	var button = document.getElementById("btn-new-p");
	element.removeChild(button);
}
Ajax

We have already seen how you can modify the DOM through JavaScript; now we will take a closer look at how you can use JavaScript to load content from another server and inject it into the current web page. The technique we will describe here is called AJAX, which is a technique to create asynchronous requests, i.e., when a request is made in the code, the code will continue with the rest of the script without waiting for the result of the request [8].

The script shown in listing 8 is included in the resulting HTML page from listing 5. When a name is clicked in the list, the contents of the (initially empty) div element will be filled with additional information about that person. To do this, we will get data in JSON format from an external resource that is accessible through an HTTP request. Note that there are other possibilities than using JSON for this purpose, we could also have chosen XML, CSV, or other alternatives.

var request = null;
var id;

function getInfo(name) {
	id = name;
	var element = document.getElementById(id);
	if (element.innerHTML == "") {
		request = new XMLHttpRequest();
		var url = "getinfo.php?name=" + escape(name);
		request.open("GET", url, true);
		request.onreadystatechange = showInfo;
		request.send(null);
	} else {
		element.innerHTML = "";
	}
}

function showInfo() {
	if (request.readyState == 4)
		if (request.status == 200) {
			var response = JSON.parse(request.responseText);
			var element = document.getElementById(id);
			element.innerHTML = response.name
				+ " works as a " + response.job
				+ " at " + response.company + ".";
		}
}

To create a HTTP request in JavaScript, we create a XMLHttpRequest object – it should be noted that not all browsers support this object. Next we add the method, cf. table 1, the url with additional url-escaped parameters, and a boolean value true to indicate that the request is asynchronous. Next we add an event listener to the onreadystatechange property of the request. As you can see in the referenced method, as soon as the readyState property equals 4, the rest of the code in that method will be executed. The last statement in the getInfo function sends the request to the URL.

The source code presented here is far from ideal, but should be enough to illustrate some of the basics of AJAX. Modern web developers also tend you use jQuery more and more, as it provides better support for Ajax than raw JavaScript. Nonetheless, it is not a bad idea to start by learning how it all works at a lower level.

PHP meets JavaScript

JavaScript usually runs at the client-side, but as we saw in the previous paragraph, it can be used to make requests to the server to fetch certain resources. The requested resources can be static, for example an image or text file, but can also depend on parameters added to the HTTP request. In the last case, we will have to take those parameters from the request and let the server compute an adequate response.

When we are using PHP, the request parameters are stored in the global variables $_GET and $_POST for the GET and POST methods respectively. As we saw earlier, the server will simply return the computed PHP script over an HTTP response. If we look at listing 8, the client sends the getinfo.php page a variable called name using the GET method, and expects some result in a json format. Listing 9 shows a script that provides this functionality. In practice, we won’t use a static array of course to retrieve data from, but instead make a connection to a database.

<?php
$info = array(	"John" 		=> array("name" => "John Wright", "job" => "Programmer", "company" => "DistrICT Ltd."),
				"Francis" 	=> array("name" => "Francis O'Connor", "job" => "CRM consultant", "company" => "DistrICT Ltd."),
				"Martin" 	=> array("name" => "Martin Lloyd", "job" => "Test engineer", "company" => "DistrICT Ltd."),
				"Peter" 	=> array("name" => "Peter Richards", "job" => "CEO", "company" => "DistrICT Ltd.")
);
printInfo(urldecode($_GET["name"]), $info);

function printInfo($name, $info) {
	print json_encode($info[$name]);
}
?>

4. Requirement analysis and design

4.1 Data modeling and SQL

Data forms the core of almost any software system today, and data modeling and storage are of course important aspects of websites as well. Although in modern applications there has been a tendency towards big data with its associated technologies (e.g. NoSQL), for small-scaled websites, relational databases will do the job nicely.

A database is a collection of persistent data and is managed by a database managment system. A database runs on a database server, which is a “collection of programs that enables users to create and maintain a database” [9]. A database server has one or more database languages. Structured Query Language (SQL) is a database language designed for managing relational databases in which commands can be expressed that can be executed on a database server [9].

SQL variants like PostgreSQL, SQLite, and MySQL, are very much suited for building websites. In this article we will focus on MySQL, as it is already supported in the WampServer package; in fact the “m” stands for “MySQL”.

4.1.1 The relational model

The relational model is, from a formal perspective, a collection of mathematical definitions, largely based on predicate logic and set theory [9].

The relational model consists of the following terminology (among others). A table is a collection of rows, which in turn consist of a set of values, each of which is associated with a column. The columns correspond to typed properties of the table. For example the Name column of a Person table could have as type VARCHAR(50) for example. Other columns of the Person could then be an address, date of birth, social security number and so on. The contents of the table must also answer to a number of integrity rules or constraints; for example the social security number has to be a nine-digit number. Finally it should be possible to uniquely identify each row. This is done through keys. A primary key is a column of the table containing unique values for that table. In our example, we could use the social security number column as a primary key for the Person table. To reference other tables, a table can have an additional column containing a foreign or referential key [9]. For example, the Person could have a foreign key licence plate, referencing another table Car.

A modeling tool that is usually used for modeling relational databases is the Entity relationship diagram (ERD). As the name implies it is a diagram in which entities, which usually correspond to the tables of a database, and the relationships among them are shown. An ERD for the example from the previous paragraph is shown in figure 6.

erd

Figure 6 : ERD example.

4.1.2 (My)SQL commands and syntax

We won’t go into too much detail here, but instead refer to W3School’s tutorial which can be found here (link to website). Note that the SQL syntax is often quite elborate with a number of options for each statement, but usually you only need to know a small subset of them. Overall SQL is, just like PHP, CSS and JavaScript, heavily documented and the solutions to most of the problems you will encounter, you will be able to find somewhere online. The full MySQL syntax can be found here (link to website).

When working with WampServer, PHPMyAdmin is already provided. This is a software tool for managing MySQL databases and will be available at http://localhost/phpmyadmin/. It will save you some trouble of writing most of the SQL commands yourself and lets you easily inspect the database’s contents.

4.1.3 SQL and PHP

TODO : example, name spaces, …

4.2 Use cases and domain modeling

Use cases and domain models are standard requirement analysis tools. For more information on these techniques we refer to previous posts.

4.3 Modeling website structure

The structure of a website is tightly coupled with the use cases and usability of your design. One way to model the structure of your website is to use a site map. A site map is a document containing a description of all the web pages and how they are linked to each other. Also database connections or links to other (external) resources can be included. In this way, a site map forms a more technical description of the website than for example a storyboard. It is however less formal than, let’s say, a component diagram for example. Figure 7 shows an example of a site map.

site map exmaple

Figure 7 : Site map as (informal) modeling tool.

4.4 User interface design

We already have discussed techniques such as paper and digital prototyping in previous posts, so we won’t go in more detail here.

5. Implementing a PHP website

5.1 Problem description

A friend of yours is a cartoonist in her spare time and she would like to own her own custom website. She asked you to build one for her.

The general idea of the website should be as follows. The main page should contain the latest published cartoon and a left and right button to go to the previous or next ones. There should also be a page with some info about her and her work.

5.2 Analysis

TODO : create document containg the requirement analysis.

5.3 Implementation

TODO : implementation for the case study.

6. Final thoughts

TODO : conclusion.

References

[1] Internet Live Stats, 2014, “Total number of Websites”, Online, available at: http://www.internetlivestats.com/total-number-of-websites/, accessed 15 June 2014.

[2] G. Coulouris, J. Dollimore, T. Kindberg and G. Blair, 2011, “Distributed Systems: Concepts and Design (5th Edition)”, M. Horton, Red., Addison-Wesley, 1063 pages.

[3] PHP.net, 2013, “Usage Stats for January 2013”, Online, available at: http://php.net/usage.php, accessed: 15 June 2014.

[4] A. S. Tanenbaum, 2010, “Computer Networks (5th Edition)”, Prentice Hall; 5 edition (October 7, 2010), 0132126958, 960 pages.

[5] E. Castledine and C. Sharkie, 2010, “jQuery Novice to Ninja”, SitePoint Pty. Ltd., first edition, 48 Cambridge Street Collingwood VIC Australia 3066, ISBN 978-0-9805768-5-6, 407 pages.

[6] D. Sklar, 2004, “Learning PHP 5”, O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472, 348 pages.

[8] R.M. Riordan, 2008, “Head First Ajax”, O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472, 497 pages.

[9] R.F. van der Lans, 2006, “Het SQL leerboek”, sixth edition, Sdu Uitgevers bv, Den Haag, 752 pages.

1. Introduction

In the last ten years over 250,000 mobile applications have been developed, and this statement is based on a paper by Wasserman [1] dating back to 2010. Meanwhile, the number of users, mobile devices, applications and developers has only grown. As a result, having at least some knowledge of mobile development has become an attractive skill for a software developer in general.

In this article we will try to give a brief introduction to mobile development and the processes that it involves. Similarly to previous projects we will include a requirement analysis of the given problem description and an analysis of the user interface. If you wish to learn how to create an Android app without all the rest, you can also simply follow Google’s tutorial here (link to website).

The technology we will use to implement the final product is Android. Reasons for chosing Android are among other that Android is Java-based, meaning that we can refer to previous posts, but also that Android actually the market leader in mobile operating systems (OS) [3]. Android also has a strong portal integration making it easier for developers to publish applications on their portal, i.e., the Google Play Store. Android is also more open than for instance Apple’s iOS or Microsoft’s Windows Phone [4].

1.1 Learning objectives

Some of the learning objectives for this post are as follows:

  • Familiarization with mobile development using Android;
  • Introducing user interface design techniques;
  • Introducing a structured approach for mobile application development;
  • Implementation of a user interface and application back-end in Android;
  • Basic data persistence;

1.2 Problem description

A friend of yours has recently bought an Android phone. He likes writing songs, but as song ideas don’t always pop up when there is a piece of paper around to write them down, he would like to use his phone to write down his musings. He asked you if you could create such an application for him.

The application was to be very simple: one needed to be able to create a new note, browse and search for past notes and finally delete and edit old ones. Additional constraints are that the app should be very easy to use and should work regardless of network connectivity.

2. Mobile development

2.1 The mobile landscape

A characteristic of mobile development is the fragmentation in the mobile landscape. As the technology only emerged relatively recently, a relatively large number of competitors have tried to dominate the market. Charland et al. [2] identify no less than nine technologies that are to be learned before one can cover most of the mobile platforms out there. More recently however, Google’s Android, Apple’s iOS and Microsoft’s Windows Phone and up have emerged as the front runners in this competition [2, 3]. Although this suggests development of native applications can be reduced to development for two to three technologies, the cost of developing and maintaining applications in these technologies would still create a considerable overhead.

In their paper, Charland et al. [2] investigate the success and future of cross-platform technologies to counter this heterogeneity. Still, although web-based mobile application are an interesting alternative, native technologies are likely to boast greater performance levels at the cost of development time to cover as many platforms as possible.

2.2 Mobile sofware engineering

In a paper by Wasserman [1] some of the aspects of software engineering for mobile application development are analyzed. He identifies user experience as especially critical for the success of mobile applications [1]. In [2] a distinction is made between aspects of user experience that can be controlled, i.e., the ‘implementation’, and those that must be understood but cannot be controlled, i.e., the context. The implementation refers to performance, integration with platform features (such as specific sensors) and design. The context includes hardware and platform specifics, UI conventions and the environment in which the application is used.

One of the key issues for user experience in mobile development is the design of user interfaces. The screen is relatively small and may differ greatly depending on the device. The mobile user interface is based upon widgets, touch, physical motion and virtual and physical keyboards, in contrast to the traditional desktop style [1]. In section 4 we will study this topic in greater detail.

Reliability, security and quality have to be taken in consideration, just like for traditional applications. In the case of mobile development, special care should be taken with issues related to for example connectivity loss and battery life [1].

3. Software design

As in previous posts we will use standard specifications to model the requirements of our application: use cases and a domain model. The resulting document can be found here (link to pdf).

4. User interface design

There is a variety of techniques to design user interfaces. In this post we will use techniques to model the flow of the user interface (screen transition diagram), and sketches of the visual elements (paper prototypes or mockups). These paper prototypes will then be used in user evaluations.

4.1 Application flow

To visualize the idea and flow of an application, one can use a story board. A story board is a sequence of drawings depicting a specific usage of the application. In some cases designers interpret story boards as a very high-level concept where the actors are drawn and the details of user interface are neglected, illustrating mainly the usage of the application in a specific context [7]. Alternatively, others may use very detailed drawings, focusing on the screens themselves [8].

To model the flow of the application, we can use the use cases and/or scenarios from our requirements analysis as input. Each transition between two screens is triggered by a specific action or event, for example clicking a button, swiping over a screen element, reaching the deadline of a timer, et cetera [9].

4.2 User interface design

4.2.1 Paper prototyping

Paper prototyping is defined as “a variation of usability testing where representative users perform realistic tasks by interacting with a paper version of the interface that is manipulated by a person ‘playing computer’, who doesn’t explain how the interface is intended to work” [6]. It is a technique for designing, testing, and refining user interfaces [5], and is closely related to usability testing [6]. In the last decade it has become a regularly applied technique in major businesses such as IBM, Digital, Honeywell, and Microsoft among others [5].

In [11] a number of benefits are associated with paper prototyping:

  • Potential usability problems can be detected at a very early stage in the design process before any code has been written.
  • Paper prototyping promotes communication between designers and users.
  • Paper prototypes can be created and refined relatively easily, allowing for rapid design iterations.
  • Only minimal resources and materials are required.
Making a paper prototype

To start, we will need a number of materials to create the physical paper prototype. Typical materials are blank paper for drawing prototype pieces, some kind of fixed background upon which other paper prototype elements are placed, pens for hand-drawing the prototype, and scissors for cutting out certain pieces, making popups and so on [5]. Feel free to come up with additional materials that can be used to facilitate the simulation of a real interface. An important remark though, is that the paper prototype is usually bigger than the actual interface, and does not have to look very artistic or realistic, as long as it conveys the main ideas of the design [5].

For mobile applications the “background” can be constructed as follows. Start with drawing the casing and screen of a common mobile device on a sheet of paper. Next, make two incisions at the bottom and top of the screen. When creating a new interface for your prototype, cut of the edges so that you can slide the screen through the incisions, replacing the default white screen of your paper “casing”.

Simulating the interaction

According to Snyder pretty much anything can be simulated, using a little imagination. It is still hard to simulate complex or subtle interaction. A number of examples taken from [5] are listed here:

  • Tooltips/mouseovers: Tell the users that some visual elements have a tooltip associated with them, and that they can ask if they wish to know what it says for a particular item;
  • Beeps: Simply say “beep” whenever the computer would, for example, when the user clicks outside a modal dialog box;
  • Drag and drop: Ask users to specify what they’re dragging and where they’re dropping it. The Computer then describes the visual changes that occur during this process;
  • Right mouse menus: Ask users to say when they are clicking the right mouse button. If they do, display the menu.

Note that some interactions such as drag and drop, and right mouse clicks often not come to mind of the test subject when using a paper prototype. Also, some interactions such as scrolling are not always incorporated in the paper prototype, but tested in a later stage of the interface design [5].

4.3 Improving the design

Prototypes are not just a technique for the analysis of an idea for application, but can serve as input for a method to iteratively improve the user interface. When we want to improve a user interface, we are generally talking about usability. In the ISO standard ISO 9241-11, usability is defined as “the extent to which a product can be used by specified users to achieve specified goals with effectiveness, efficiency and satisfaction in a specified context of use” [12].

Usability should not be considered a one-dimensional property of a user interface. Nielsen identifies several characteristics of usability in applications [13]:

  • Learnability : if the system is easy to learn, the user can get started quickly;
  • Efficiency : if the system is efficient to use, it will be possible to complete more work in less time;
  • Error rate and severity : if the system should be robust and minimize faults;
  • Memorability : once the system is learned, acquired skills should not be forgotten easily;
  • Satisfaction : the system should be pleasant to use.

According to these characteristics, objectives for the usability of an application can be determined, tested and improved over a series of iterations. The sum of incremental changes should converge to the optimal user interface. There is a variety of evaluation techniques, including usability engineering, expert (heuristic) evaluation and using questionnaires. Different evaluation techniques have different strengths and focus on different aspects of human-computer interaction [15,16,17].

To produce sound test results, it is imperative that the objectives of each iteration are clearly defined, how and by who they will be tested. To produce relevant test results, the number of subjects should be high enough. Nielsen proposes to use a minimum of 5 test users per iteration [14].

NOTE : We urge the reader to consult some of the referenced articles. In this text we have often been very brief to avoid losing focus of the exercise itself.

4.4 Result

The resulting diagrams and prototypes are listed here (link to pdf). We included two iterations in the document, but it should be clear that for larger applications more (paper as well as digital) prototypes and evaluations are likely to be needed to obtain adequate results.

5. Implementation

5.1 Getting started with Android

Google’s developer pages provide very good documentation for Android. Before we can return to the actual implementation, you might need to install some software first, so if you didn’t already, follow Google’s tutorial here (link to website) to set up the development environment. If you have an Android device click here (link to website) to see how you can configure the project to run on the device. If you don’t, you can set up a virtual device, as decribed here (link to website).

Figure 1 shows how to create a new Android application project. The general Android project structure is described here (link to website). How to run and build a project can be found here (link to website).


Figure 1 : Creating a new Android application project.

5.2 Model

I suggest we start with what we already learned from a previous post, which is how to create Java classes. In this case our model is as simple as it can get. We only need one class, namely the MyNote class as we named it in our project. Objects from this class are used to pass between the back-end and front-end of the application, between screen transitions, corresponding to moving from one Android Activity implementation to another.

A note has a title and a body, i.e., the actual text of the note. As a note is mapped on some table in the database it also has an id once it has been persisted, as well as a timestamp for when the note was first created. Setters for these fields have checks to verify whether or not the input is valid. The implementation for the MyNote class is (partially) shown in Listing 1.

public class MyNote implements Parcelable {

	private String title;
	private String text;
	private long id;
	private Timestamp timeCreated;
	
	public MyNote(String title, String text, long id, Timestamp timeCreated) throws NullPointerException, IllegalArgumentException {
		this(title, text);
		this.setID(id);
		this.setTimeCreated(timeCreated);
	}
	
	public final void setTitle(String title) throws NullPointerException, IllegalArgumentException {
		if (title == null) throw new NullPointerException("MyNote.title resolved as NULL.");
		if (title.length() == 0) throw new IllegalArgumentException("No empty MyNote.title allowed.");
		this.title = title;
	}
	
	public final void setText(String text) throws NullPointerException, IllegalArgumentException {
		if (text == null) throw new NullPointerException("MyNote.text resolved as NULL.");
		if (text.length() == 0) throw new IllegalArgumentException("No empty MyNote.text allowed.");
		this.text = text;
	}
	
	final void setTimeCreated(Timestamp timeCreated) {
		if (timeCreated == null) throw new NullPointerException("MyNote.timeCreated resolved as NULL.");
		this.timeCreated = timeCreated;
	}
	
	final void setTimeCreated(String timeCreated) {
		this.setTimeCreated(Timestamp.valueOf(timeCreated));
	}
	
	final void setID(long id) {
		this.id = id;
	}
	
	public String getTitle() {
		return title;
	}
	
	public String getText() {
		return text;
	}
	
	public long getID() {
		return id;
	}
	
	public Timestamp getTimeCreated() {
		return timeCreated;
	}
	
	@Override
	public String toString() {
		return this.getTitle();
	}
	
	@Override
	public boolean equals(Object o) {
		if (o == null) return false;
		if (! (o instanceof MyNote)) return false;
		MyNote m = (MyNote) o;
		if (m.getID() != this.getID()) return false;
		return true;
	}
	
	@Override
	public int hashCode() {
		return super.hashCode();
	}
}

In order to pass objects of this class between Android Activities, the MyNote class implements the Parcelable interface. As can be seen in Listing 2, a Parcelable class requires a constructor that takes a Parcel and constructs an object from it, a method implementation to create a Parcel from an object and finally a public static final field, CREATOR, which has the responsibility for the creation of a MyNote object from a Parcel.

public static final Parcelable.Creator<MyNote> CREATOR = new Parcelable.Creator<MyNote>() {

	@Override
	public MyNote createFromParcel(Parcel source) {
		return new MyNote(source);
	}

	@Override
	public MyNote[] newArray(int size) {
		return new MyNote[size];
	}
};

public MyNote(Parcel parcel) {
	this(parcel.readString(), parcel.readString(), parcel.readLong(), parcel.readString());
}

@Override
public int describeContents() {
	return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
	dest.writeString(this.getTitle());
	dest.writeString(this.getText());
	dest.writeLong(this.getID());
	dest.writeString(this.getTimeCreated().toString());
}

5.3 Activities

5.3.1 MainActivity

Classes that extend the Activity class (cf. listing 3) are used to form a link between the visual elements described by layout XML files (cf. listing 4) and the rest of the Java code. Create a new Activity subclass is similar to creating a normal Java class, but instead of one, two files will be generated: the Activity subclass under the src folder, and a layout file under the res > layout folder in your project.

We provided comments to clarify some of the code in listing 3.

public class MainActivity extends Activity {
	
	private static String TAG = "mynotes";
	private IPersistency persistency;	
    private ListView listview;
    private ArrayAdapter<MyNote> adapter;
    private EditText inputSearch;
    private List<MyNote> mynotes = new ArrayList<MyNote>();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// (auto-generated) bind the activity to the layout file
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		// Create a connection to the database
		this.persistency = new MyNotePersistency(this);
		
		// retrieve all (in the future this can be optimized with paging for example) notes :
		try {
			mynotes = persistency.selectAll();
		} catch (PersistencyException pe) {
			Log.e(TAG, pe.getLocalizedMessage());
		} finally {
			persistency.closeTransaction();
		}
		
		// retrieve the listview in the layout file
		// instantiate the adapter with given layout (id) and the list of notes
		// add the adapter to the listview
		// add an action that will be performed when an item is clicked
		listview = (ListView) findViewById(R.id.listView1);
		adapter = new ArrayAdapter<MyNote>(this, android.R.layout.simple_list_item_1, mynotes);
		listview.setAdapter(adapter);
		listview.setOnItemClickListener(new OnItemClickListenerImplementation());
		
		// find the search field
		// add an event listener that will filter the list with the given input
		inputSearch = (EditText) findViewById(R.id.inputSearch);
		inputSearch.addTextChangedListener(new EditTextWatcher());
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	public void createNewMyNote(View view) {
		// finish this activity
		finish();
		// start a new CreateNewMyNoteActivity Activity
        startActivity(new Intent(MainActivity.this, CreateNewMyNoteActivity.class));
	}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/inputSearch"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:hint="@string/search"
            android:inputType="textVisiblePassword" />

		<Button
            android:id="@+id/button1"
            style="?android:attr/buttonStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/plus"
            android:onClick="createNewMyNote" />

    </LinearLayout>	

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
    <TextView android:id="@+id/my_note_title_view"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dip"
        android:textStyle="bold"
        android:textIsSelectable="false"/> 

</LinearLayout>

In our main menu we have a button to create a new note. As you can see in the screen transition diagram, this action opens up a form where we can enter our note data. To go from one activity to another, we can create an Intent object where we provide the current context and the target Activity class. This can be seen in listing 3 in the createNewMyNote method. To link this method the button, we add the method name to the onClick event handler of the Button definition in the layout file, as can be seen in listing 4.

In listing 3, we programmatically bind an click event listener to each list item. The implementation for this event listener is provided in listing 6. Note that we add the MyNote object that was clicked by the user to the Intent object.

private class OnItemClickListenerImplementation implements OnItemClickListener {

	@Override
	public void onItemClick(AdapterView< ?> arg0, View arg1, int position, long id) {
		// create a bundle that can be added to the intent to pass it between Activity classes
		Bundle bundle = new Bundle();
		bundle.putParcelable("mynote", mynotes.get(position));
		Intent intent = new Intent(MainActivity.this, MyNoteActivity.class);
		// add the bundle to the intent
		intent.putExtras(bundle);
		
		finish();
		startActivity(intent);
	}
}

Finally listing 7 shows the implementation for the TextWatcher which will filter the list using the given character sequence.

private class EditTextWatcher implements TextWatcher {
	@Override
	public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
		MainActivity.this.adapter.getFilter().filter(cs);   
	}
	
	@Override
	public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
	
	@Override
	public void afterTextChanged(Editable arg0) {}
}

5.3.2 CreateNewMyNoteActivity

Listing 8 shows the (partial) implementation of the CreateNewMyNoteActivity class. We will zoom in on the implementation for the createMyNote operation. To avoid the user entering incomplete or wrong input, we added a check and an AlertDialog implementation to warn the user in such events. Providing meaningful feedback or avoiding bad behaviour is essential in creating a good user experience.

public class CreateNewMyNoteActivity extends Activity {
	
	public void createMyNote(View view) {
		
		EditText titleField = (EditText) findViewById(R.id.create_mynote_title);
		EditText textField = (EditText) findViewById(R.id.create_mynote_text);
		String title = titleField.getText().toString().trim();
		String text = textField.getText().toString().trim();
		
		if (title.isEmpty() || text.isEmpty()) {
			new AlertDialog.Builder(this).setTitle("Wrong input:")
				.setMessage("Please make sure both the title and text fields are filled in.")
				.setNeutralButton("Close", null).show();
		} else {
			try {
				persistency.insert(new MyNote(title, text));
			} catch (PersistencyException pe) {
				Log.e(TAG, pe.getLocalizedMessage());
			}
			finish();
	        startActivity(new Intent(this, MainActivity.class));
		}
	}
	
	// other methods and fields ...
}

5.3.3 MyNoteActivity

Our final Activity subclass is loaded when clicking an item in the list view. In this activity the user can edit or delete the note. As we saw before, when we created the Intent object to load this activity, we added a Bundle object to it, containing the (Parcelable) MyNote object. When this activity is then loaded, we have to unpack the Bundle object from the Intent object, as shown in the onCreate method in listing 9.

public class MyNoteActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_my_note);
		Bundle bundle = this.getIntent().getExtras();
		myNote = bundle.getParcelable("mynote");
		
		titleField = (EditText) findViewById(R.id.edit_mynote_title);
		textField = (EditText) findViewById(R.id.edit_mynote_text);
		titleField.setText(myNote.getTitle());
		textField.setText(myNote.getText());
		
		persistency = new MyNotePersistency(this);
	}
	
	public void deleteMyNote(View view) {
		new AlertDialog.Builder(this).setTitle("Warning")
			.setMessage("Are you sure you want to delete this note?")
			.setNegativeButton("Cancel", null)
			.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
				
	            @Override
	            public void onClick(DialogInterface dialog, int which) {
	            	try {
	        			persistency.delete(myNote);
	        		} catch (PersistencyException pe) {
	        			Log.e(TAG, pe.getLocalizedMessage());
	        		}
	        		MyNoteActivity.this.finish();
	        		MyNoteActivity.this.startActivity(new Intent(MyNoteActivity.this, MainActivity.class));
	            }
	        })
			.show();
	}
	
	// other methods and fields ...
}

We also show the delete functionality here, as the rest of the implementation is fairly similar to those of the other activities. Listing 9 shows how we load another alert dialog and bind the execution of the delete operation to the “Yes” option in our dialog. If the other button is clicked, the dialog is closed and no further actions take place.

5.4 Data persistence

We want to store our notes somehow on our mobile device or on a remote website. There are a number of ways to achieve this, for example by adding a small database to the application, using local storage of the phone, or connecting to a remote web site and sending/fetching the data to/from it [18]. We opted to use a small SQLite database.

5.4.1 From digital prototype to final implementation

Since we decided to add this database implementation later in the development cycle, allowing us to create our digital prototypes faster, we added some flexibility to our design. We created an IPersistency interface (cf. listing 10) with a TestData implementation (cf. listing 11). Since the interface doesn’t change, but only its implementation class, we were able to implement our complete application without a database, but with the complete application flow already in place. When we finally implemented the database class, we only needed to change the private IPersistency persistency fields in our activities from a TestData object to a MyNotePersistency (see further).

public interface IPersistency {

	/**
	 * Inserts a new note into the database and returns the generated id for this entry.
	 * @param myNote the note to be persisted.
	 * @return the id of the persisted note.
	 */
	public long insert(MyNote myNote) throws PersistencyException;
	
	/**
	 * Updates the given note in the database.
	 * @param myNote
	 */
	public void update(MyNote myNote) throws PersistencyException;
	
	/**
	 * Deletes the given note in the database.
	 * @param myNote
	 */
	public void delete(MyNote myNote) throws PersistencyException;
	
	/**
	 * Select the note from the database with given id.
	 * @param comparator
	 * @return the note with given id.
	 */
	public MyNote select(long id) throws PersistencyException;
	
	/**
	 * Select all the notes from the database.
	 * @param comparator
	 * @return all the notes in the database.
	 */
	public List<MyNote> selectAll() throws PersistencyException;

	/**
	 * @return the number of records in the MyNote table.
	 */
	public int recordCount();

	/**
	 * Close the current transaction.
	 */
	public void closeTransaction();
}
public class TestData implements IPersistency {

	private static final String TAG = "TestData";
	private static long ID = 0;
	
	// Database contents :
	private final Map<Long, MyNote> myNotes = new HashMap<Long, MyNote>();
	
	private static IPersistency persistency;
	
	public static IPersistency getInstance() {
		if (TestData.persistency == null) TestData.persistency = new TestData();
		return TestData.persistency;
	}
	
	private TestData() {
		super();
		this.insert(new MyNote("Example MyNote 1", "This is an example note."));
		this.insert(new MyNote("Example MyNote 2", "This is an example note."));
		this.insert(new MyNote("Example MyNote 3", "This is an example note."));
		this.insert(new MyNote("Example MyNote 4", "This is an example note."));
	}

	@Override
	public long insert(MyNote myNote) {
		myNote.setID(++ID);
		myNote.setTimeCreated(Timestamp.valueOf("2014-06-03 14:23:00.000000000"));
		this.myNotes.put(myNote.getID(), myNote);
		return myNote.getID();
	}

	@Override
	public void update(MyNote myNote) {
		this.myNotes.put(myNote.getID(), myNote);
	}

	@Override
	public void delete(MyNote myNote) {
		this.myNotes.remove(myNote.getID());
	}

	@Override
	public MyNote select(long id) {
		return this.myNotes.get(id);
	}

	@Override
	public List<MyNote> selectAll() {
		return new ArrayList<MyNote>(myNotes.values());
	}

	@Override
	public int recordCount() {
		return this.myNotes.values().size();
	}

	@Override
	public void closeTransaction() {
		// not used.
	}
}

5.4.2 The actual database implementation

Listing 12 shows the implementation for the SQL query to create the mynote table. The ID should autoincrement, meaning that the database itself will determine the ID value. The title and text values can’t be NULL, meaning that they have to contain a meaningful value. The DATE_CREATED field will also be auto-generated by the database. As can be seen in listing 13, when inserting or updating a record, the updated fields of that record are only the title and body fields, set in the getContentValues method. Alternatively, when a record is selected from the database, the resulting Cursor object is then converted into a MyNote object in the setMyNote. Before each operation on the database, we always need call either the getWritableDatabase or getReadableDatabase method of the SQLiteOpenHelper super class.

CREATE TABLE mynote (
	ID				INTEGER	PRIMARY_KEY AUTOINCREMENT NOT NULL,
	TITLE			VARCHAR(256) NOT NULL,
	BODY			TEXT NOT NULL,
	DATE_CREATED	TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
public class MyNotePersistency extends SQLiteOpenHelper implements IPersistency {
	
	private static final String CREATE_TABLE_MYNOTE = "CREATE TABLE " + TABLE_NAME + " (" +
			ID 				+ " " + INTEGER		+ " " + PRIMARY_KEY + " " + AUTOINCREMENT + " " + NOT_NULL + ", " +
    		TITLE			+ " " + VARCHAR_256 + " " + NOT_NULL + ", " +
    		BODY			+ " " + TEXT 		+ " " + NOT_NULL + ", " +
    		DATE_CREATED	+ " " + TIMESTAMP 	+ " DEFAULT CURRENT_TIMESTAMP " + NOT_NULL +
    ");";
	
	public MyNotePersistency(Context context) {
		super(context, DB_NAME, null, 1);
	}

	@Override
	public long insert(MyNote record) throws PersistencyException {
		SQLiteDatabase db = this.getWritableDatabase();
        long id = db.insert(TABLE_NAME, null, getContentValues(record));
        return id;
	}

	@Override
	public void update(MyNote record) throws PersistencyException {
		SQLiteDatabase db = this.getWritableDatabase();
	    int rowsaffected = db.update(TABLE_NAME, getContentValues(record), ID + " = ?",
	            new String[] { String.valueOf(record.getID()) });
	}

	@Override
	public void delete(MyNote myNote) throws PersistencyException {
		SQLiteDatabase db = this.getWritableDatabase();
	    db.delete(TABLE_NAME, ID + " = ?", new String[] { String.valueOf(myNote.getID()) });
	}

	@Override
	public MyNote select(long id) throws PersistencyException {
		SQLiteDatabase db = this.getReadableDatabase();
	    Cursor cursor = db.query(TABLE_NAME, new String[] { ID }, ID + "=?", new String[] { String.valueOf(id) }, null, null, null, null);
        cursor.moveToFirst();
	    MyNote data = setMyNote(cursor);
        cursor.close();
	    return data;
	}

	@Override
	public List<MyNote> selectAll() throws PersistencyException {
		List<MyNote> list = new ArrayList<MyNote>();
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);

        if (cursor.moveToFirst()) {
            do {
                list.add(setMyNote(cursor));
            } while (cursor.moveToNext());
        }
    	cursor.close();
        return list;
	}
	
	@Override
	public int recordCount() {
	    SQLiteDatabase db = this.getReadableDatabase();
	    Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
	    int count = cursor.getCount();
	    cursor.close();
	    return count;
	}
	
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(CREATE_TABLE_MYNOTE);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
		db.execSQL(CREATE_TABLE_MYNOTE);
	}
	
	@Override
	public void closeTransaction() {
		this.close();
	}
	
	private ContentValues getContentValues(MyNote record) {
		ContentValues values = new ContentValues();
        values.put(TITLE, record.getTitle());
        values.put(BODY, record.getText());
        return values;
	}

	private MyNote setMyNote(Cursor cursor) {
		MyNote data = new MyNote();
        data.setID(cursor.getLong(0));
        data.setTitle(cursor.getString(1));
        data.setText(cursor.getString(2));
        data.setTimeCreated(cursor.getString(3));
        return data;
	}
	
	public void deleteDatabase(Context context) {
		context.deleteDatabase(DB_NAME);
	}
}

5.5 Result

On figure 2 you can see some screen shots from the resulting application, run on an mobile Android device.


Figure 2 : the MyNotes application on an Android device.

6. Final thoughts

We have seen how we can design a mobile application from start to finish. Although this was a fairly simple application, we hope that the concepts provided here can be used as a stepping stone for more advanced mobile development.

The full implementation of the application can be found on our GitHub page here (link to website).

References

[1] A.I. Wasserman, 2010, “Software Engineering Issues for Mobile Application Development”

[2] A. Charland and B. LeRoux, 2011, Mobile Development, Web vs. Native

[3] M. Butler, 2011, “Android: Changing the mobile landscape”

[4] A. Holzer and J. Ondrus, 2010, “Mobile application market: A developer’s perspective”

[5] C. Snyder, 2003, “Paper Prototyping: The Fast and Easy Way to Design and Refine User Interfaces (Interactive Technologies)”, Morgan Kaufmann, Pages: 408

[6] C. Snyder, 2003, “What is Paper Prototyping”, [Online] Available at: http://www.paperprototyping.com/what.html, [Accessed: 10 February 2013]

[7] E. Duval, 2014, “CHI sessie 2: brainstorming, personas and storyboards”, [Online] Available at: http://www.slideshare.net/erik.duval/20140224-chi-sessie2 [Accessed 4 June 2014]

[8] E. Duval, 2009, “Multimedia: storyboard”, [Online] Available at: http://www.slideshare.net/erik.duval/multimedia-storyboard [Accessed 4 June 2014]

[9] E. Duval, 2010, “Paper prototyping”, [Online] Available at: http://www.slideshare.net/erik.duval/paper-prototyping-3266200 [Accessed 4 June 2014]

[10] UsabilityFirst, 2013, Usability First – Usability Glossary – user profile | Usability First, [Online] Available at: http://www.usabilityfirst.com/glossary/user-profile/ [Accessed 14 February 2013]

[11] UsabilityNet, 2006, UsabilityNet: Paper prototyping methods, URL: http://www.usabilitynet.org/tools/prototyping.htm, [Online, Accessed: 10 February 2013]

[12] UsabilityNet, 2006, UsabilityNet: International Standards, [Online] Available at: http://www.usabilitynet.org/tools/r_international.htm#9241-11, [Online, Accessed 20 February 2013]

[13] Nielsen J., 1994, Usability Engineering, Morgan Kaufmann Publishers, ISBN 0-12-518406-9, Pages: 362

[14] Nielsen J., 2012, Why You Only Need to Test with 5 Users, [Online] Available at: http://www.nngroup.com/articles/why-you-only-need-to-test-with-5-users/, Accessed 20 February 2013

[15] Nielsen J., 2012, Thinking Aloud: The #1 Usability Tool, [Online] Available at: http://www.nngroup.com/articles/thinking-aloud-the-1-usability-tool/, Accessed 20 February 2013

[16] Nielsen J., 2012, How to Conduct a Heuristic Evaluation, [Online] Available at: http://www.nngroup.com/articles/how-to-conduct-a-heuristic-evaluation/, Accessed 20 February 2013

[17] Kirakowski J., 2000, Questionnaires in Usability Engineering, [Online] Available at: http://www.ucc.ie/hfrg/resources/qfaq1.html, Accessed 20 February 2013

[18] Google, 2014, “Storage Options | Android Developers”, [Online] Available at: http://developer.android.com/guide/topics/data/data-storage.html, Accessed 6 June 2014

1. Exercise description

1.1 Learning objectives

This post builds on the previous posts on application development and user interface development. Feel free to read those before moving on with this one. In this article we will try to give the reader a notion on how to design and implement a distributed application, introducing some of the following concepts:

  • Distributed system design;
  • Service interface design;
  • Access control;
  • Remote method invocation (RMI);

1.2 Problem description

At this point Matthew’s library application allows the user to load books into the system and obtain an overview of its contents. Books can also be added and removed via the user interface.

Matthew is part of a book club and demonstrated the application to the other club members. Rachel, one of the other members, came up with an idea to extend the application to allow all members to use the application. This would enable them to create a shared repository of all their book collections and would make it much easier to find and exchange books between members.

Each member should now have his/her own collection of books and should be able to browse through the entire catalogue of books in the combined collections of the club members. Users should be able to add new books to the collection, as well as remove them as their collection changes over time. The same book could appear multiple times in the library application, on the condition that they have different owners.

The application should keep track of different users and their collections. Users can only add or remove books that they own.

2. Solving the problem : system design

2.1 Distributed systems

It should be clear from the problem description that our initial application design cannot fulfill all the requirements listed here. The library data should now be accessible from different computers that are connected via a network. To solve this problem, somehow data should be shared among them, while maintaining data consistency. The system we will design and build that meets these requirements is a distributed application.

A distributed system consists out of components that reside on different machines and communicate through message passing. This design introduces three notable challenges [1]:

  1. Lack of a global clock : This is largely resolved by coordinating operations through messages, but has its limitations.
  2. Independent failures : The nodes within this system may also fail independently and may even go undetected.
  3. Concurrency of operations : Resources may be accessed simultaneously, possibly introducing inconsistencies. To solve this, locking and other mechanisms can be used.

There are many different paradigms that can be used to design distributed systems and technologies that implement them. To understand how a distributed system works, you need to know how it relates to other layers in the overall architecture, as shown in figure 1. The lower layers are comprised of the physical network and other hardware. On top of this, network protocols operate, which are used to pass data, transported in frames and packages, between computer systems in the network. Examples of these protocols are the Internet protocol (IP) which is used to route packages between computers, and User Datagram Protocol (UDP) and Transmission Control Protocol(TCP) which is used on top of IP to pass messages between processes on these computers. On top of these protocols resides the distributed middleware. These are the technologies that enable distributed computing [1,2].

layers-and-protocols

Figure 1 : Architectural layers and protocols in a distributed environment.

In a distributed system communication between nodes is a very important aspect. The communication paradigm ultimately puts constraints on the way the system can be designed. For example for client-server applications, direct communication is very efficient as it allows clients to locate a service and perform operations on this service in a way that is very similar to a non-distributed environment.

Alternatively, indirect communication paradigms separate the client from the server through a layer of indirection. This allows spatial and/or temporal uncoupling of client and server which can make the system more flexible. For example, when a client wants to perform an action, some distributed middleware passes on this message to any server that computes the outcome. The result is then passed back to the client. The time of execution and place of the server are then irrelevant to the client.

In the next subsection we will take a closer look at one of the communication paradigms for direct communication called remote method invocation.

2.1.1 Marshalling

When a process sends a message for an operation with a number of parameters to a remote process, it marshalls the data into a format that can be send over the network. When the receiving end opens the message, it unmarshals it and uses the data to execute the operation with given parameters [1].

Different marshalling formats exist, e.g. XML, CDR, Java Object Serialization. In Java, when objects of a class have to be sent over a network, this class has to implement an interface called Serializable [1], as shown in listing 1. Serialized objects are not very readable for humans as they are a compressed representation of an object.

import java.io.Serializable;

public class Book implements Serializable {

	private final String author;
	private final String title;

	public Book (String author, String title) {
		this.author = author;
		this.title = title;
	}
}

Listing 2 shows an example of how an object of the class shown in listing 1 could be marshalled using XML. In contrast to the serialized object representation, XML is self-descriptive and hence very readable to humans.

<?xml version="1.0" encoding="UTF-8"?>
<book>
	<author>Milan Kundera</author>
	<title>The Unbearable Lightness of Being</title>
</book>

2.1.2 Remote method invocation (RMI)

Remote method invocation (RMI) allows clients to invoke methods on remote objects. Remote objects implement a remote interface which is the set of methods a client can invoke the corresponding remote object. RMI allows parameter passing by value, as input or output parameters, and also as object references. Remote invocation can then be used on these object references, instead of transmitting the complete object value across the network [1].

At the client side remote objects are represented by proxies that act as local objects, but instead of executing invocations, it forwards them in a message to the corresponding remote object after marshalling arguments [1].

To keep track of local and remote objects, their corresponding references are kept in a separate module. When a remote invocation is initiated, the relevant reference is looked up and subsequently the operation marshalled and the message passed on to the client’s communication module. Next, the message is transported via the network to the server’s communication module, which passes on the remote invocation to its dispatcher. The dispatcher passes the invocation to the correct remote object. The result of the invocation is then sent back to the client via the opposite direction [1]. The overview in figure 2 summarizes this scenario.

RMI

Figure 2 : Remote method invocation.

2.2 Requirement analysis

2.2.1 Use cases

Use cases describe the system’s functionality from the user’s point of view. Use cases are visualized in a use case diagram and elaborated in use case descriptions. In the descriptions, preconditions and postconditions are important to think clearly about what each actions tries to achieve. The main scenario allows the designer to think about the general flow of the application.

2.2.2 Domain model

A domain model gives an overview of all the concepts and how they relate to each other in a given problem description. In this case the domain model is relatively simple: a user has a number of books; books and users are part of a library.

2.2.3 Resulting analysis

The resulting requirement analysis for this project can be found here (link to pdf).

2.2.4 Deployment diagram

Figure 5 shows the deployment diagram for the library application. A deployment diagram models the physical location of deployment nodes within a system. In the distributed library application there are two distict physical classes, namely the client and the server. As remote objects are registered with a separate naming service, we’ve included an additional node, used by both client and server to connect to. On the server two components are added: one for authentication, and one for the library service itself. In an alternative design, an authentication server could be added as well.

deployment-diagram

Figure 5 : Deployment diagram for the (distributed) library application.

2.3 Basic access control

Access control is an important concept in application security. Access control models are used to authenticate users by a guard to give them the authorization to perform certain actions on a protected system [4,5]. Conceptually this can be visualized as shown in figure 5.

general access model

Figure 6 : General access control model.

Based on our specification and requirement analysis, we can determine which operations require authentication. A formal way to specify access control is through a security automaton [5]. Security automatons can be written down in a Java-like syntax, as shown in listing 3.

// NOTE : findUser(), findSession(), hash(), generateSessionkey(), show() are helper methods and are not specified here.

type Book;
type User = Tuple<username,password>;
type Session = Tuple<username,sessionkey>;

List<User> users;
List<Session> sessions;
List<Tuple<Book,User>> books;

void addBook (Book book, Session session)
	requires (Tuple<book,findUser(session.username)> not in books) && (session in sessions)
{
	books.add(Tuple<book,findUser(session.username)>);
}

void removeBook (Book book, Session session)
	requires (Tuple<book,findUser(session.username)> in books) && (session in sessions)
{
	books.remove(Tuple<book,findUser(session.username)>);
}

void overviewBooks ()
{
	show(books);
}

void login (String username, String password)
	requires (exists Tuple<username,hash(password)> in users)
{
	sessions.add(Tuple<username,generateSessionkey()>)
}

void logout (String username)
{
	sessions.remove(findSession(username))
}

3. Implementation

3.1 Project structure

As can be seen in table 1, the application is devided into three separate parts. The LibraryServer and LibraryClient projects correspond to the library server and client nodes respectively, as modeled in our deployment diagram in figure 5. The LibraryCommon project is a separate library that is included by the two other projects, so everything in this package is visible to both client and server.

Table 1 : Library application project structure.
Project (cf. node) Package
LibraryCommon
  • args
    • Book : the Book class implements the serializable interface and is passed as argument of a remote method invocation over the network.
    • Rating : the rating enumeration.
    • Session : the Session class implements the serializable interface and is passed as argument of a remote method invocation over the network.
  • exceptions
    • AuthenticationException : reports an error when trying to authenticate a user.
    • AuthorizationException : reports an error when a user tries to perform an action for which he/she is not authorized.
    • BookNotFoundException : reports an error when a particular book is not found in the library.
    • DuplicateException : reports an error when a particular book or user is already in the library and cannot be added again.
    • UserNotFoundException : reports an error when a particular user is not a member of the library.
  • remote
    • IRemoteLibraryModule : the remote interface encapsulating the library functionality.
    • IRemoteSessionModule : the remote interface encapsulating the session management functionality.
  • rmi
    • RMISettings : contains all the settings for registering and locating remote objects.
LibraryServer
  • default
    • Main : contains a main method to run the application, as well as helper methods to load some initial data from books.csv and users.csv into the library. In this class the remote objects are registered as well.
    • books.csv : contains an initial list of books that can be added to the library.
    • users.csv : contains an initial list of users that can be added to the library.
  • system
    • Library : the core of the application. This class manages the actual data of the application.
    • LibraryModule : a facade between the library and the rest of the application. Implements the IRemoteLibraryModule interface.
    • SessionModule :
    • User : a facade between the library and the rest of the application. Implements the IRemoteSessionModule interface.
LibraryClient
  • default
    • Main : contains a main method to run the application. Also contains source code to lookup the remote objects and start the user interface.
  • ui
    • ApplicationView : contains to complete user interface for the application.

The LibraryCommon library consists of a number of classes that implement the Serializable interface, namely Book and Session. Objects of these classes can be passed as arguments of remote methods invocations and thus passed in serialized format over the network, cf. remote interface declarations in section 3.2.1. Also the two remote interfaces are in this library, since client and server need to be able to access them. Also a settings class is shared between server and client as they both use the same registry to register and lookup the remote objects.

Figure 7 shows the class diagram of the most important parts of the application; things like constructors, getters and setters, exception classes, main methods et cetera have been left out. A class diagram is not the same as the domain model, but is a representation of how the domain model is implemented, although this distinction is not always made.

classdiagram

Figure 7 : Class diagram of the library project (Java RMI).

3.2 The remote service

3.2.1 Remote interfaces

To create a remote interfaces Java, the interface has to extend Remote, from the java.rmi.Remote library. The following subsections contain the listings for the remote interfaces in this project. Note that all methods in a remote interface declaration can throw RemoteException.

Session management

The IRemoteSessionModule interface, shown in listing 4, extends Remote and provides functionality to authenticate users, and verify whether or not a user is authenticated.

public interface IRemoteSessionModule extends Remote {
    
    public Session authenticate(String username, String password) throws AuthenticationException, RemoteException;
	
    public boolean isAuthenticated(Session session) throws RemoteException;
    
    public void destroySession(String username) throws RemoteException;
}
Library functionality

The IRemoteLibraryModule interface, shown in listing 5, is the second remote interface.

public interface IRemoteLibraryModule extends Remote {
    
    public void addBook (Book book, Session session) throws RemoteException, NullPointerException, DuplicateException, AuthorizationException;
    
    public void removeBook (Book book, Session session) throws RemoteException, AuthorizationException;
    
    public Book lookupBook (String isbn, String owner) throws RemoteException, BookNotFoundException;
    
    public List<Book> getBooks() throws RemoteException;
    
	// other methods ...
}

3.2.2 Implementing the remote interfaces

Once we have specified the remote interfaces, we can start implementing them. Both modules, as we’ve called them, have a library attribute to invoke operations on. We’ll assume you know how the Library class is implemented which can be found here; all in all it doesn’t differ much from the previous implementation. In this way, these modules can be understood as an additional layer between client and the core of the library system.

Listing 6 shows the implemention for the IRemoteSessionModule interface. The SessionModule implementation is stateful, as user sessions are stored during its lifetime. If this node should crash, all sessions are lost. Since authorization also occurs through this interface, the user should simply login again as soon as the service is restarted to continue with his/her work.

public class SessionModule implements IRemoteSessionModule {
    
    private final Library library;
    private final Set<Session> sessions;

    public SessionModule(Library library) {
        this.library = library;
        this.sessions = new HashSet<>();
    }

    @Override
    public Session authenticate(String username, String password) throws AuthenticationException, RemoteException {
        User user = null;
        try {
            user = this.library.lookupUser(username);
        } catch (UserNotFoundException unfe) {
            throw new AuthenticationException(username);
        }
        if (! checkPassword(user, password)) throw new AuthenticationException(username);
        Session session = new Session(username, generateSessionkey());
        this.sessions.add(session);
        return session;
    }

    @Override
    public boolean isAuthenticated(Session session) throws RemoteException {
        return this.sessions.contains(session);
    }

    @Override
    public void destroySession(String username) throws RemoteException {
        for (Session s : this.sessions) {
            if (s.getUsername().equals(username)) this.sessions.remove(s);
        }
    }

    private String generateSessionkey() {
        char[] chars = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789").toCharArray();
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 16; i++) {
            sb.append(chars[random.nextInt(chars.length)]);
        }
        return sb.toString();
    }
    
    private boolean checkPassword(User user, String password) {
        try {
            if (md5(password).equals(user.getPassword())) return true;
        } catch (UnsupportedEncodingException | NoSuchAlgorithmException ex) {
            return false;
        }
        return false;
    }
    
    private String md5 (String password) throws UnsupportedEncodingException, NoSuchAlgorithmException {
        byte[] bytes = password.getBytes("UTF-8");
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] hash = md.digest(bytes);
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < hash.length; i++) {
            if ((0xff & hash[i]) < 0x10) {
                sb.append("0").append(Integer.toHexString((0xFF & hash[i])));
            } else {
                sb.append(Integer.toHexString(0xFF & hash[i]));
            }
        }
        return sb.toString();
    }
}

Note that the SessionModule contains a number of private methods. It is often a good idea to keep the length of the method implementation within manageable bounds. In addition, if you document your methods well and give them descriptive names, source code can be read and interpreted much easier. for example, take a closer look at lines 19 and 20 of listing 6; adding all the code that is required for the functionality covered by the methods checkPassword and generateSessionkey to the authenticate method, would make this method very bloated and hard to get an overview of. By deviding the code into logical sub-parts, an outsider can much faster reason about it without even knowing the exact implementation of these sub-parts.

The IRemoteLibraryModule implementation, shown in listing 7, LibraryModule is stateless. Except for the methods that require some authorization, most method implementations simply call the corresponding method in the public interface of the Library class.

public class LibraryModule implements IRemoteLibraryModule {
    
    private final Library library;
    private final IRemoteSessionModule sessionModule;
	
    public LibraryModule(Library library, IRemoteSessionModule irsm) {
        this.library = library;
        this.sessionModule = irsm;
    }

    @Override
    public void addBook(Book book, Session session) throws RemoteException, NullPointerException, DuplicateException, AuthorizationException {
        if (! this.sessionModule.isAuthenticated(session)) throw new AuthorizationException(session);
        if (! book.getOwner().equalsIgnoreCase(session.getUsername())) throw new AuthorizationException(session);
        this.library.addBook(book);
    }
    
    @Override
    public void removeBook(Book book, Session session) throws RemoteException, AuthorizationException {
        if (! this.sessionModule.isAuthenticated(session)) throw new AuthorizationException(session);
        if (! book.getOwner().equalsIgnoreCase(session.getUsername())) throw new AuthorizationException(session);
        this.library.removeBook(book);
    }

    @Override
    public Book lookupBook(String isbn, String owner) throws RemoteException, BookNotFoundException {
        return this.library.lookupBook(isbn, owner);
    }

    @Override
    public List<Book> getBooks() throws RemoteException {
        return this.library.getBooks();
    }
	
	// other methods ...
}

Note that the implementation for both interfaces takes into account the security automaton given earlier. When a requires condition is not met, a corresponding Exception is thrown. Referring to the access control model, SessionModule operates as a guard for certain actions in LibraryModule. If a user is not authorized to perform a certain action, the relevant exception will be thrown.

3.3 Remote object registration

Once the RMI registry is running, we can register the remote objects from the server in it. In the LibraryCommon package we added a RMISettings class that contains the host and portnumber of the service, so both client and server can locate the RMI registry. Also the names by which we will register the remote objects, are found in the RMISettings class.

In listing 8 is shown how the registry is created and subsequently the remote objects are instantiated and registered at the RMI service with the given names.

// Instantiate a new library object :
Library library = new Library();

// Set the security manager :
if (System.getSecurityManager() != null) {
	System.setSecurityManager(null);
}

// Create a new registry with given port :
Registry registry = LocateRegistry.createRegistry(RMISettings.REGISTRY_PORT);

// Register the remote objects :
IRemoteSessionModule sessionModule = new SessionModule(library);
IRemoteSessionModule stubSessionModule = (IRemoteSessionModule) UnicastRemoteObject.exportObject(sessionModule, 0);
registry.rebind(RMISettings.SESSION_SERVICE_NAME, stubSessionModule);

IRemoteLibraryModule libraryModule = new LibraryModule(library, sessionModule);
IRemoteLibraryModule stubLibraryModule = (IRemoteLibraryModule) UnicastRemoteObject.exportObject(libraryModule, 0);
registry.rebind(RMISettings.LIBRARY_SERVICE_NAME, stubLibraryModule);

3.4 Client and user interface

3.4.1 Locating remote objects

Similarly to registering remote objects, at the client side we will perform a lookup operation on the RMI registry to obtain proxies for the remote objects, as shown in listing 9. Next the client can start invoking remote methods on these remote objects.

// Set the security manager :
if (System.getSecurityManager() != null) {
	System.setSecurityManager(null);
}

// Locate the registry :
Registry registry = LocateRegistry.getRegistry(RMISettings.REGISTRY_HOST, RMISettings.REGISTRY_PORT);

// Lookup both remote objects in the registry :
IRemoteSessionModule sessionModule = (IRemoteSessionModule) registry.lookup(RMISettings.SESSION_SERVICE_NAME);
IRemoteLibraryModule libraryModule = (IRemoteLibraryModule) registry.lookup(RMISettings.LIBRARY_SERVICE_NAME);

// Start the user interface for the application :
ApplicationView applicationView = new ApplicationView(sessionModule, libraryModule);

3.4.2 User interface

Listing 10 shows the implementation for the text-based user interface of our application. Some basic methods have been left out; the most important parts have been retained to understand how the flow is implemented.

public class ApplicationView {
    
    private final IRemoteLibraryModule libraryModule;
    private final IRemoteSessionModule sessionModule;
    private Session session;
    private final Scanner scanner;

    public ApplicationView(IRemoteSessionModule irsm, IRemoteLibraryModule irlm) {
        this.sessionModule = irsm;
        this.libraryModule = irlm;
        this.scanner = new Scanner(System.in);
        this.startUI();
    }
    
    private void startUI() {
        System.out.println("#######################################");
        System.out.println("# Welcome to the library application! #");
        System.out.println("#######################################");
        
        showStartMenu();
    }
    
    private void showStartMenu() {
        System.out.println("---------------------------------------");
        System.out.println("- Please select an option :           -");
        System.out.println("- 1. Login.                           -");
        System.out.println("- 2. Exit.                            -");
        System.out.println("---------------------------------------");
        
        switch (readInt(1, 2)) {
            case 1:
                showLoginMenu();
                break;
            case 2:
                showExitMessage();
                break;
        }
    }
    
    private void showLoginMenu() {
        try {
            System.out.print("Username : ");
            String name = readString();
            System.out.print("Password : ");
            String password = readString();
            
            this.session = getSessionModule().authenticate(name, password);
            showMainMenu();
        } catch (AuthenticationException ae) {
            printException(ae);
            System.err.println("You entered a wrong combination of username and password.");
            showStartMenu();
        } catch (RemoteException ex) {
            printException(ex);
            System.err.println("INFO : Something went wrong, you were redirected to the start menu.");
            showStartMenu();
        }
    }
    
    private void showMainMenu() {
        System.out.println("---------------------------------------");
        System.out.println("- Please select an option :           -");
        System.out.println("- 1. Library contents overview.       -");
        System.out.println("- 2. Add a new book.                  -");
        System.out.println("- 3. Log out.                         -");
        System.out.println("---------------------------------------");

        switch (readInt(1, 3)) {
            case 1:
                showBooksOverview();
                break;
            case 2:
                showAddBookWizard();
                break;
            case 3:
                logout();
                break;
        }
    }

    private void showBooksOverview() {
        try {
            List<Book> books = getLibraryModule().getBooks();
            for (int i = 0; i < books.size(); i++) {
                System.out.println("\t" + (i + 1) + ". " + books.get(i));
            }
        } catch (RemoteException re) {
            printException(re);
        } finally {
            showMainMenu();
        }
    }

    private void showAddBookWizard() {
        System.out.print("\tTitle : "); String title = readString();
        System.out.print("\tAuthor : "); String author = readString();
        System.out.print("\tPublication year : "); int year = readInt(0, Integer.MAX_VALUE);
        System.out.print("\tRating {1:POOR, 2:AVERAGE, 3:GOOD, 4:EXCELLENT, 5:UNKNOWN} : ");
		int value = readInt(1, 5);
        Rating[] values = Rating.values(); Rating rating = values[value - 1];
        System.out.print("\tISBN : "); String isbn = readString();
        
        try {
            Book book = new Book(title, author, year, rating, isbn, getSession().getUsername());
            this.getLibraryModule().addBook(book, getSession());
            System.out.println("INFO : The item was successfully added to the library.");
            showBooksOverview();
        } catch (IllegalArgumentException | RemoteException | NullPointerException | DuplicateException | AuthorizationException ex) {
            printException(ex);
            System.err.println("INFO : The item was not added to the library.");
            showMainMenu();
        }
    }
    
    private void logout() {
        try {
            getSessionModule().destroySession(getSession().getUsername());
            showStartMenu();
        } catch (RemoteException re) {
            printException(re);
            System.err.println("INFO : Something went wrong, you were not logged off.");
            showMainMenu();
        }
    }
}

The methods showLoginMenu, logout, showBooksOverview and showAddBookWizard all use the remote interfaces to let the user perform actions on the remote objects. It is at this point the application has come full circle.

4. Running the application

Running the application requires that you first start the server, next start the client project. If all goes well, you should see the user interface in the console!

5. Final thoughts

In this article we have covered a brief introduction to distributed systems and remote method invocation, as well as an example of requirements analysis for the given problem description, and an introduction to basic access control. Finally we have seen how to implement remote method invocation in a distributed application using Java RMI.

Of course there are many other paradigms and technologies available to implement distributed systems or a solution for the given problem description for that matter. If you are interested to read more about distributed systems, a summary of the distributed systems course at KULeuven is available under concepts/distributed-systems on this blog.

If things are not clear from the text, feel free to ask questions in the comment section below. There are of course many other tutorials available online specifically about Java RMI [6,7,8,9,10], as well as well official documentation [11].

The source code for this project can be found here. The advantage of this design is that you can relatively simply reuse the authentication part and write your own functionality in another module, e.g., an online grocery store, a multimedia retail server, et cetera.

References

[1] G. Coulouris, J. Dollimore, T. Kindberg and G. Blair, “Distributed Systems: Concepts and Design (5th Edition)”, M. Horton, Red., Addison-Wesley, 2011, p. 1063.

[2] A. S. Tanenbaum, 2010, “Computer Networks (5th Edition)”, Prentice Hall; 5 edition (October 7, 2010), 0132126958, 960 pages

[3] W. Stallings, 2005, “Operating Systems, 5th Edition”, Pearson Prentice Hall, pages: 906

[4] F. Piessens, 2005, “Developing Secure Software Applications DRAFT”, Online, avalailable at: http://people.cs.kuleuven.be/~frank.piessens/OVS/CursusOVS-2005.pdf, accessed 15/03/2014

[5] F. Piessens, 2013, “Authentication and Access control: Part I”, Online, available at: http://people.cs.kuleuven.be/~frank.piessens/OVS/slides3a.pdf, accessed 15/03/2014

[6] http://docs.oracle.com/javase/tutorial/rmi/

[7] http://www.shankysportal.com/Java_RMI_Tutorial.htm

[8] http://soft.vub.ac.be/~tvcutsem/distsys/javarmi.pdf

[9] http://download.java.net/jdk8/docs/technotes/guides/rmi/hello/hello-world.html

[10] http://www8.cs.umu.se/education/examina/Rapporter/471App.pdf

[11] http://docs.oracle.com/javase/7/docs/api/java/rmi/package-summary.html

1. Exercise description

In this tutorial we will assume you are working with the Netbeans integrated development environment (IDE). The latest version of Netbeans can be downloaded here, and the required Java Development Kit (JDK) here. If you don’t know how to compile and run Java, please read the tutorial by Oracle that can be found here; it explains in great detail how to create the “Hello world” program, as illustrated in our first post.

In what follows we will design a small administrative application, illustrating some basics about the Java language. The project is of course a simplification of real software projects, that may take months to complete with possibly large development teams working on it, after careful requirement analysis, software design, planning, et cetera. Nonetheless, it should provide you with a starting point to learn more about the Java language and software design and development in general.

1.1 Learning objectives

At the end of the tutorial you should have a notion of the following concepts:

  • Types;
  • Object-oriented design through classes;
  • Interfaces and encapsulation;
  • Inheritance;
  • Control statements such as loops and conditions;
  • Control flow alteration using exception handling;
  • Enumerations;
  • Working with collections such as lists;

1.2 Problem description

Matthew is a collector of books, comics, magazines. However, as his collection has become difficult to manage over the years, he would like to have an efficient way to index and find books and other items in his collection.

Each item has a title, author, a unique code (ISBN/ISSN), publishing year, rating, and type. The International Standard Book Number (ISBN) is a 13 digit number for books, and the ISSN is a unique eight-digit number used to identify a magazines. A rating is either excellent, good, average, poor or unknown.

He wants to be able to get an overview of his collection for each type and sort items by author or title. Items can be added, removed. Editing existing items is not supported, as it is unlikely to occur.

2. Software design: solving the problem

Once you know the problem, we could consider the questions to think about a solution:

  1. Operation and actors (functionality) : Who can perform which action on the system?
  2. Components and structure (design) : Which data is required, how is it represented?
  3. Constraints (business rules) : Under which conditions is the state of the system consistent?

In general, there is never one good possible solution to a software problem. Design decisions have tradeoffs, and an important role of a software designer is to acknowledge benefits and drawbacks from each decision and to be able to motivate the decision. These tradeoffs can be reflected in the design itself, e.g., through complexity or extensibility, or as external factors such as usability, development cost, memory usage, et cetera.

Note that the solution we’re presenting for the above problem is directed at Java application development. In reality there would probably be much more efficient solutions for this particular problem, e.g., composing a simple excel sheet of his collection would probably do the trick. But as this is an exercise in developing Java applications, we’ll take the problem description as it is. Moreover, an application like the one we will be building, can be extended to have much more interesting functionality, but we’ll leave that up to your own creativity!

2.1 Operation and actors

Use case diagrams are typically used to model the operations and actors of a system. Use case diagrams allow to identify relationships between the principal elements and processes, i.e. “actors” and “use cases” that make up a system [1]. Different types of use case diagrams exist, namely business use cases and system use cases. In this case however, it is not worth making that distinction. The following elements are part of a use case diagram [1]:

  • Actor : “An actor portrays any entity (or entities) that performs certain roles in a given system”. An actor can execute use cases that are linked to him/her. Actors reside outside the system boundary.
  • Use case : A use case corresponds to a business functionality of the system.
  • System boundary : As the system does not have infinite fuctionality, visual boundaries display the scope of the system.

For Matthew’s electronic library, the only actor that can be identified is that of administrator, as only one role exists in the current system. The following operations can be identified: “add item”, “Remove item”, “Show item overview, and “Sort item overview”. Figure 1 shows the resulting use case diagram for this system. A free and simple modeling program is dia, which can be downloaded here, but for small projects you could make sketches on paper as well.

Use case diagram

Figure 1 : Use case diagram.

2.2 Components and structure

The next step is a bit more difficult as it requires some more abstraction. Domain model diagrams and class diagrams are typically used to design the internal structure of the software.

A domain model is a “conceptual model of all the topics related to a specific problem; it describes the various entities, their attributes, roles, and relationships, plus the constraints that govern the problem domain” [2].

2.2.1 Classes

Classes can be thought of as concepts that arise in the problem description. For example, item, book, comic, ISBN, rating, collection, library, et cetera are all concepts that can be derived from the text. Not all these concepts are necessary translated into classes. Sometimes they can be seen as attributes of another class if adding a new class for the corresponding concept would introduce more complexity than required. For example, in some cases a home address is included as a separate class, and in some cases it is a simple string of text. Note that you will often create or reuse classes that are not explicitly in the description, or even in the domain model. For example, external libraries are usually not modeled in the domain model.

Relations between classes exist when one class uses another class in some way. For example, a class House could contain a list of rooms, that are modeled by a Room class, introducing a one-to-many relationship between them. This HAS-A relationship is illustrated in figure 2.

class associations

Figure 2 : Association between classes in the domain model.

Classes represent a template for parts of the data in the program as it is running. A class instantiation is called an object. For example a Person class with a name and age can be instantiated as a Person object a with a = {name: “Martha”, age: 24}, and b with b = {name: “Matthew”, age: 31}.

2.2.2 Inheritance

Classes that are semantically similar, usually display similar bevahiour as well. Instead of designing the same behaviour twice, an additional class could be introduced that encapsulates the common behaviour, which is in turn inherited by the two other classes, possibly extended with additional characteristics. This kind of paradigm is known as inheritance. It is a IS-A relationship between two classes where the parent is a generalization of the specialized, inheriting child classes.

For example, roles can be designed through inheritance. A User class has a name and a read operation associated with it. An Editor class inherits the same behaviour and attributes from the User class, but can also create and edit documents. A Manager is for example a User that can read documents and archive them, but can’t create or edit them. The corresponding diagram is shown in figure 3. Note that use case diagrams also support inheritance.

Inheritance example

Figure 3 : Inheritance example.

Result

In our example the following classes can be identified:

  • Item : an item is the superclass for Book and Periodical. This allows a certain level of abstraction over the code.
  • Book
  • Periodical : Comics and magazines are considered periodicals, and hence have ISSN codes.
  • Comic
  • Magazine
  • Library : Collection would be better, as it is advised to use wordings that are closest to the original problem description. However, there exists a Collection class in the Java util library, so to avoid unnecessary complications later on, we will use another class name.

The corresponding domain model diagram is shown in figure 4.

domain model

Figure 4 : Domain model.

2.3 Constraints

An important aspect of any piece of software is maintaining state consistency. The state of a system can be represented by the data present in the system. For example, the state of a game of chess can be represented by the positions of each piece. Sometimes there is more than one way to represent the system state. In our chess example, the system state could also be represented as the series of moves that have occurred thusfar in the game. The way data is represented in a system also has a significant impact on performance and the flexibility of a system. For example, by remembering each game state in a game of chess, allows for undoing a certain move.

Checking consistency of input/output and the contract of a service is imperative, and as a result, modeling constraints is an important aspect to avoid unexpected behaviour. For example, assuming that the input for a cash withdrawal is always positive is dangerous. As a result, checks should be implemented that impose these constraints, and otherwise return an error message.

Software tests are an important tool to check for consistency. In test driven development, tests are written out and rerun after every major change to the source code. If a test fails, the programmer knows the latest release contains a significant software bug, and debugging can begin.

In our application, for example certain numeric fields should be positive, ISSN and ISBN numbers require a certain format, title and author strings cannot be empty, et cetera. All these things can be modeled in business rules.

3. Software implementation: creating the final product

A bad initial design will have a major impact on the development time and the effectiveness of the application. As a result, carefully designing an application can save a lot of time later on. In this case, the application is relatively small, so making design mistakes won’t have as big of an impact.

3.1 The implementation language

To create the final product, some programming language is used to produce source code. The source code is in its essence a set of instructions that a computer will execute. For a computer to understand the instructions, a certain format and syntax is required. Each programming language has a set of predefined keywords that allow the programmer to specify control sequences, data types and so on.

Every language has some data type for boolean logic built into it, some numeric data type for calculations, some data type to represent characters, and a primitive data type to represent arrays. Strings are usually considered as arrays of characters. Programming languages also have some way to express conditional statements, usually using if-else structures and boolean operators, loops, commonly using while or for loops, that continue reevaluating a set of statements until some condition is no longer true, and arithmetic, using basic operators.

In this tutorial we will use Java to implement our solution. Java is an object-oriented, strongly typed programming language. It is relatively verbose and may take some time to build a fully functional program, even for small applications. Nonetheless, it is one of the most important programming languages today; it has a vast amount of libraries at its disposal, and is commonly used in teaching computer programming courses, so almost any programmer you will encounter will have some knowledge of Java.

In [3] some of the most important Java keywords are listed. The meaning of most of these keywords will hopefully become clear as we walk through the specifics of the implementation.

3.2 Implementation

The full implementation can be found here, available on GitHub. Feel free to take a look at the implementation! In what follows we’ll discuss the most important aspects of the implementation.

3.2.1 Packages

Before we start implementing classes, we’ll add a new package to distinguish better between the different components within our application. In this case, we have created two packages system and ui. This exercise focuses on the system part, which contains the library and internal data representation for the items. The ui package contains the classes that deal with visual representation and human-computer interaction, as opposed to the business logic imlpemented in the system package.

3.2.2 Classes

To define a new class, the class keyword is used, followed by the classname, as shown in listing 1. The body of the class is implemented between the curly brackets. We also added the public keyword to indicate that the class is visible throughout the application. Also, by making its constructor public, we allow that anywhere in the application an object of the class can be instantiated.

public class Library {
	
	// This is a comment.
	// Constructor for the library class. To instantiate a new Library object :
	// Library l = new Library(); 
	public Library() {}
}

The Item class is an abstract class. To declare an abstract class, simply add the abstract keyword in the class declaration header, as shown in listing 2. In this case, we’ve already added the fields title, author, year, and rating. The fields are declared private, as we don’t want anything outside to interact with them directly. The fields title and author of the type String, which represents plain text, the year is an integer, and the rating attribute is of the type Rating, which is an enumeration as defined in listing 3. The fields are instatiated on object creation, i.e., when the constructor of the class is called. To denote a field, we use the keyword this, followed by the field’s name, to distinguish from the parameter name. Alternatively, you could simply use different names in the parameter declaration of the constructor.

public abstract class Item {
    
    private String title;
    private String author;
    private int year;
    private Rating rating;
	
    public Item (String title, String author, int year, Rating rating) {
        this.author = author;
        this.rating = rating;
        this.title = title;
        this.year = year;
    }
	
	public Item(String title, String author, int year) {
        this(title, author, year, Rating.UNKNOWN);
    }
}
public enum Rating {
    POOR, AVERAGE, GOOD, EXCELLENT, UNKNOWN;
}

The source code in listing 2 also contains a second constructor, but with fewer parameters. It actually calls the other constructor using the this keyword, but with the default value for rating set to Rating.UNKNOWN.

Finally, listing 4 shows how to declare a class that extends another class, using Java’s extends keyword. In the constructor of Book, the constructor of the extended class, i.e., Item, is called using the super keyword, similar to the this keyword in listing 2. Additional fields are set after the super constructor call.

public class Book extends Item {

    private String isbn;
	
    public Book(String title, String author, int year, Rating rating, String isbn) {
        super(title, author, year, rating);
        this.isbn = isbn;
    }
}

Using this information you should be able to figure out the class declarations for the other classes.

3.2.3 Getters and setters

Getters and setters form an important paradigm to enforce encapsulation of the internal representation of a class. Getters are used by parts of the program outside the class to investigate the fields of the class. For example, the field author is accessed by a getter getAuthor(). Like any function in Java, a getter has a return type, which is in this case String, the type of the field, and a number of parameters in the function declaration, which in this case is zero. The access specification is public as we want any part of the program that can access the object to be able to inspect its fields.

Similarly, the setter setAuthor is a private method, as we don’t want to allow another object or program to change the internal state through this method. As parameters the setAuthor method receives a String, i.e., the name of the author. As the method doesn’t return anything, the return type of the method is void. The method is invoked in the constructor, and anywhere else within the class where the field is altered. The reasoning behind this is that it is much safer to put all the code that ensures the consistency of the field in one place, i.e., the setter, than repeat consistency checks anywhere that the field can be changed. In listing 5 an exception will be thrown if the input for the author field is empty. In that case the control of the flow will be redirected anywhere where the exception is caught.

private String author;
// other fields...

public Item (String author, ...) throws IllegalArgumentException {
	this.setAuthor(author);
	// ...
}

private void setAuthor(String author) throws IllegalArgumentException {
	if (author.isEmpty()) throw new IllegalArgumentException("The length of the author name must be greater than zero.");
	this.author = author;
}

public String getAuthor() {
	return this.author;
}

3.2.4 Collections

Listing 6 shows the Library class implementation with collections added. Using lists, the advantages of the polymorphic design of the Item class hierarchy can be exploited. Instead of having to maintain separate lists for comics, books, and magazines, we can create a list and add all objects of classes that extend the Item class to it.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Library {

    private final List<Item> items;
    
    public Library() {
        items = new ArrayList<>();
    }
    
    public void addItem (Item item) throws NullPointerException, DuplicateException {
        if (item == null) throw new NullPointerException("The item resloved as NULL.");
        if (this.items.contains(item)) throw new DuplicateException(item);
        this.items.add(item);
    }
    
    public void removeItem (Item item) {
        this.items.remove(item);
    }
    
    public Item lookup (String code) throws ItemNotFoundException {
        for (Item i : this.items) {
			if (i.getCode().equals(code)) return i;
		}
        throw new ItemNotFoundException(code);
    }
    
    public List<Book> getBooks() {
        List<Book> books = new ArrayList<>();
        for (Item i : this.items) {
			if (i instanceof Book) books.add((Book) i);
		}
        return books;
    }
	
    public List<Item> getItems() {
        return Collections.unmodifiableList(this.items);
    }
}

As mentioned earlier, Java also has a primitive type for arrays. In this case however, it is much easier to use the Java Collection classes from the util library. Classes that are not in the package of the current class have to be imported, and this is no different for classes of other libraries. Some classes such as String or Object don’t need to be imported. We use the List interface, ArrayList class and Collections class from the java.util library, which can be imported using the import keyword, as shown at the top of the class declaration in listing 6.

List is an interface, which means we cannot create objects directly from it, much like an abstract class. Instead, we can create an object of a class that implements the interface, for example the ArrayList class. To create a list of Item objects, use the new keyword like in any other object instantiation, and add the Item classname between the less-than and greater-than sign.

Again, to ensure our list remains consistent, we add modifier and inspector functions to it, such as addItem, removeItem, lookupItem, and getItems.

A notable keyword in this context is instanceof. Using this keyword yields a boolean value indicating whether or not an object is of the type of the given class. In the example listed in listing 6, a list of Book objects is extracted from the Item list.

3.2.5 Object equality and String representation

Every class you define extends the built-in Object class, directly, or indirectly if the class is part of a larger inhertitance tree. The object class comes with a number of methods that are essential for lot of other built-in functionality. The most commonly used ones are equals, hashcode, and toString. Typically, programmers will redefine these methods in their class implementation. In that case we say methods definitions are overriden, which is indicated by the @Override annotation before each method definition.

To determine whether or not two objects are equal, most Java programs will use the equals and hashcode methods. The default implementation simply compares the references of the two objects, but usually you’ll want a more refined condition for equality. For example, we’ll consider an Item object A equal to another object B when their references are the same, or when their codes are the same. It is usually a good idea to design your if-else structures in a way that they produce a result as fast as possible, avoiding many checks.

The toString function returns a String representation of an object. This is a handy way to generate output when testing your application.

@Override
public String toString() {
	return this.getAuthor() + ", " + this.year  + ", \"" + this.getTitle() + "\"";
}
@Override
public int hashCode() {
	return ((new Integer(this.year)).hashCode() + this.author.hashCode() + this.title.hashCode());
}
@Override
public boolean equals(Object obj) {
	if (this == obj) return true;
	if (obj == null) return false;
	if (obj instanceof Item) {
		Item i = (Item) obj;
		if (! this.getCode().equals(i.getCode())) return false;
	}
	return true;
}

3.2.6 Running the application

To run your application you’ll need a static method called main that takes an array of strings as argument. In the body of this method you can write the actual program that will be executed. Listing 8 shows an example of such a program, where first a Library object is instantiated, a couple of items are added to the Library object, and finally each object in the library is printed out on the console.

public class Main {
    
    public static void main (String[] args) {
        
        // Instantiate a new library object :
		Library library = new Library();
		
        // Create some items and add them to the library object :
		try {
			library.addItem(new Book("Learning Python, 5th Edition", "Mark Lutz", 2013, Rating.GOOD, "978-1449355739"));
			library.addItem(new Comic("A Silent Hell", "Juan Diaz Canales", 2012, Rating.EXCELLENT, "978-1595829313", 4, "Blacksad"));
			library.addItem(new Magazine("Wat is er mis met de jeugd van tegenwoordig?", "KNACK", 2014, Rating.UNKNOWN, "0772-3210 P509557", 44, "Knack", "General"));
        } catch (Exception ex) {
            System.err.println(ex.getClass().getName() + " : " + ex.getLocalizedMessage());
        }
		
		// Show the contents of the library :
        for (Item i : library.getItems()) {
            System.out.println(i);
        }
    }
}

Note the try-catch block in listing 8. When invoking a function that may throw an exception, such as the addItem method we defined in the Library class, there should be some way to avoid letting the program crash. This is done by invoking this method within a try block, followed by a catch block which will ‘catch’ the exception of the specified type. In this case we’re catching all exceptions, as all exceptions extend the Exception class, but we could also choose to only catch IllegalArgumentException objects, or even add another catch block to catch another specific exception. Once the exception is caught, the program can do something with this information. Here we’ll simply print out an error message.

4. User interface

4.1 The MVC paradigm

In software engineering, one of the most common design paradigms you will encounter is the model-view-controller pattern (MVC). “It divides a given software application into three interconnected parts, so as to separate internal representations of information from the ways that information is presented to or accepted from the user” [4]. The function of each MVC component is as follows:

  • Model : The model encapsulates the data, i.e., the state of the system.
  • View : The view forms the layer between user and the rest of the system.
  • Controller : The controller is the connector between the model and view. It translates the user input into operations on the system state, or updates the view as the internal state changes.

In the system we have designed so far, the distinction between model and controller is not very elaborated. However, we haven’t yet added any views to the system, aside from some simple print statements. In what follows we will describe the our model-controller subsystem as a service that can be plugged into a view subsystem.

4.1.1 The library service interface

The term public interface is very important in understanding many of today’s software designs. It is not to be confused with a user interface – although in an abstract sense, they are semantically similar, but let’s not make it too confusing -, which is the collection of input fields, buttons, and other visuals that allow for user input.

An interface in software design is an abstraction of a data type by defining that datatype through the set of operations that can be invoked upon it. The goal of this abstraction is separating the internal representation, i.e., the implementation, from the functionality, i.e., the interface. This idea is usually referred to as encapsulation. The set of operation definitions of an interface can be thought of as contract of that will be delivered by the service. Listing 9 gives an overview of the public interface that can be accessed by any other software component that uses the Library class. For example, this service contract promises that invoking the lookup method with a String value as parameter on a Library object will yield an Item object. Similarly, all public methods of the other classes form a public interface for each corresponding class.

public class Library {
	
    /**
     * Adds the new item to the library.
     * @param item the item to be added.
     * @throws NullPointerException when the item resolves as NULL.
     * @throws DuplicateException when the item is already present in the library.
     */
    public void addItem (Item item) throws NullPointerException, DuplicateException;
    
    /**
     * Removes the item from the library.
     * @param item the item to be removed.
     */
    public void removeItem (Item item);
    
    /**
     * Find an item in the library.
     * @param code
     * @return the item with corresponding code.
     * @throws ItemNotFoundException when the item is not present in the library.
     */
    public Item lookup (String code) throws ItemNotFoundException;
	
    /**
	 * Returns a list of the items in this library.
     * @return the list of items.
     */
    public List<Item> getItems();
}

Service interfaces are usually heavily documented to allow outsiders to get a good understanding of what to expect when using the service. In Java, classes can be documented using Javadoc [5]. Using annotations in the Javadoc comments, parameters can return values can be specified more clearly, as illustrated in listing 9.

By separating the implementation from its interface, the application becomes more loosely coupled. This means that the software components become less dependent on their respective implementations. For example, one class’s implementation can altered fundamentally, but as long as the service contract is respected, this will not be noticed by any of the other components that rely on its public interface.

4.1.2 Using the library interface

Now that we have established our public interface, we can add a view to our application that uses it. One way to implement a user interface is by creating text-based application menus. Each menu is a list of options, and the user can select an option by entering the number of the option. Of course, also text, booleans, and other types of values that can be represented in text format can be entered by a user, but these aren’t discussed further here.

Listing 10 shows the implementation of a basic text-based user interface class TBUI. It has a Scanner field which will read input from the console, and of course an instance of the Library class. This last field forms the link between the view and the rest of the system. I hope it is clear that by designing our system this way, the programmer that implements the view, can be someone else than the one who implements the library interface, as long as they respect the service contract.

package ui;

import java.util.InputMismatchException;
import java.util.Scanner;
import system.Library;

public class TBUI {
    
    private final Scanner scanner;
	private Library library;
    private final String TRY_AGAIN_MSG = "Wrong input, please try again.";
	
    public TBUI(Library library) {
        this.scanner = new Scanner(System.in);
        this.library = new Library();
    }
	
    private int readInt(int min, int max) {
        int result = min - 1;
        while (result < min || result > max) {
            try {
                result = scanner.nextInt();
                if (result < min || result > max) System.out.println(TRY_AGAIN_MSG);
            } catch (InputMismatchException ex) {
                scanner.nextLine();
                System.out.println(TRY_AGAIN_MSG);
                result = min - 1;
            }
        }
        return result;
    }
}

The source code in listing 11 implements a basic flow of the system’s functionality. In this example, the user can ask to show all items in the library, or simply exit the application. If the number of menu options is fixed, a switch forms an efficient way to implement to select the correct alternative.

public void show() {
	System.out.println("#######################################");
	System.out.println("# Welcome to the library application! #");
	System.out.println("#######################################");
	
	showMainMenu();
}

private void showMainMenu() {
	try {
		System.out.println("\nPlease select an option :");
		System.out.println("1. Show me all the items in the library.");
		System.out.println("2. Stop the application.");

		int result = readInt(1, 2);
		switch (result) {
			case 1:
				showItemsOverview();
				break;
			case 2:
				showExitMessage();
				break;
		}

	} catch (Exception ex) {
		printException(ex);
	}
}

private void showItemsOverview() {
	List<Item> items = getLibrary().getItems();
	for (int i = 0; i < items.size(); i++) {
		System.out.println("\t" + (i + 1) + ". " + items.get(i));
	}
	showMainMenu();
}

private void showExitMessage() {
	System.out.println("Goodbye!");
}

In listing 12 is shown how it all fits together in the updated main method of our Java application.

public class Main {
    
    public static void main (String[] args) {
        
        // Instantiate a new library object :
        Library library = new Library();
        
		// load some initial data ...
        
        // User interaction :
        UserInterface userInterface = new TBUI(library);
        userInterface.show();
    }
}

5. Software testing

Earlier, we highlighted the importance of testing in producing applications that ensure a certain level of software quality. This is a short introduction on how to create some basic test classes for your Java project using the JUnit test framework.

Listing 13 gives an example implementation of a JUnit test for the Book class. Of course more test cases can be added to cover the class’s complete functionality. The @Test annotation is used to indicate test methods. For your test to be successful, each of these should pass when running the test class, which can be done by right-clicking the source file in your Netbeans IDE and selecting “Run file” or “Test file”. A test fails when an assert statement, such as assertTrue, assertFalse or assertEquals, fails or the program encounters a fail method invocation.

package system;

import org.junit.Test;
import static org.junit.Assert.*;

public class BookTest {

    @Test
    public void testEquals() {
        System.out.println("testEquals");
        Book b1;
        Book b2;
        Book b3;
        try {
            b1 = new Book("SomeTitle", "Some Author", 2014, Rating.AVERAGE, "123-1234567890");
            b2 = new Book("AnotherTitle", "Another Author", 2014, Rating.POOR, "123-1234567890");
            b3 = new Book("AnotherTitle", "Another Author", 2014, Rating.POOR, "123-0123456789");
            
            assertTrue(b1.equals(b2));
            assertFalse(b2.equals(b3));
        } catch (IllegalArgumentException iae) {
            fail ("Should not throw IllegalArgumentException : " + iae.getMessage());
        }
    }
    
    @Test
    public void testToString() {
        System.out.println("testToString");
        Book b;
        try {
            b = new Book("SomeTitle", "Some Author", 2014, Rating.AVERAGE, "123-1234567890");
            
            assertEquals(b.toString(), b.getAuthor() + ", " + b.getYear() + ", \"" + b.getTitle() + "\"");
            assertEquals(b.toString(), "Some Author, 2014, \"SomeTitle\"");
        } catch (IllegalArgumentException iae) {
            fail ("Should not throw IllegalArgumentException : " + iae.getMessage());
        }
    }
}

6. Final thoughts

We now have seen how you can design a software solution for a specific problem. We saw how you can model functionality, the internal data representation and software components and talked about constraints. In the second part of the example, we’ve shown how the actual source code is created and finally run. We have also talked about the MVC paradigm and how establishing a service contract through a public interface can help you build flexible applications. Finally we gave a brief introduction to writing unit tests using the JUnit test framework.

Feel free to be critical of the design in this example. Even in such a small project, there are certainly a few design decisions to be made. For example the way in which we dealt with ISBN and ISSN numbers is just one option; can you think of an alternative? Can you identify positives and negatives of each alternative?

Also, try to think about how the application could be extended and in which way the current design supports or obstructs these extensions. As a software designer try to anticipate changes. Think about the openness, transparency, security, performance and extensibility of your solution.

We hope you found this example useful and hope you’ve learned something from it. If you have any questions or suggestions about this project or related problems feel free to ask questions in the comment section of this article.

References

[1] Chitnis M., Tiwari P., and Ananthamurthy L., 2003, Creating UML Use Case Diagrams – Developer.com – Developer.com, [Online] Available at: http://www.developer.com/design/article.php/2109801/Creating-Use-Case-Diagrams.htm, [Accessed 20 February 2013]

[2] Wikipedia, 2014, Domain Model, Online, accessed 28 february 2014, available at: http://en.wikipedia.org/wiki/Domain_model

[3] Wikibooks, 2013, Online, accessed 1 march 2014, available at: http://en.wikibooks.org/wiki/Java_Programming/Keywords

[4] Wikipedia, 2014, “Model-view-controller”, online, available at: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

[5] Oracle, 2014, “How to Write Doc Comments for the Javadoc Tool”, online, available at: http://www.oracle.com/technetwork/java/javase/documentation/index-137868.html

[6] Netbeans.org, 2013, “Writing JUnit Tests in NetBeans IDE”, online, available at: https://netbeans.org/kb/docs/java/junit-intro.html