View - KTH

66 downloads 3914 Views 738KB Size Report
Dependency injection and aspect-oriented programming (AOP) are not related ..... aspect-oriented programming with emphasize on Spring Framework. Chapter  ...
Enterprise Application Development using Dependency Injection and Aspect-Oriented Programming

STEFAN

PETTERSSON

Master of Science Thesis Stockholm, Sweden 2009

Enterprise Application Development using Dependency Injection and Aspect-Oriented Programming

STEFAN

PETTERSSON

Master’s Thesis in Computer Science (30 ECTS credits) at the School of Computer Science and Engineering Royal Institute of Technology year 2009 Supervisor at CSC was Serafim Dahl Examiner was Karl Meinke TRITA-CSC-E 2009:077 ISRN-KTH/CSC/E--09/077--SE ISSN-1653-5715

Royal Institute of Technology School of Computer Science and Communication KTH CSC SE-100 44 Stockholm, Sweden URL: www.csc.kth.se

Abstract Dependency injection and aspect-oriented programming (AOP) are not related but have the common goal of improving design, implementation and maintenance of software projects. As part of the development of an enterprise application in Java based on the Spring Framework these two technologies are considered and evaluated based how they affect the design, maintainability and testing of the project. The report concludes that the use of dependency injection is easy to adopt and promotes design of less coupled modules with higher cohesion. Dependency injection is especially beneficial in regard to unit-testing by providing simplifying the use mockup objects. Aspect-oriented programming provides an approach to develop and manage cross-cutting concerns in a more modularized way than conventional object-oriented programming. However, it requires the developer to think about the design in a new way in order to identify and design aspects, thus introducing a steep learning curve to both the developer as well as someone maintaining the project after deployment.

Referat Användandet av dependency injection och aspekt-orienterad programmering vid applikationsutveckling Dependency injection och aspect-oriented programming (AOP) är i sig inte direkt relaterade, men de har ett gemensamt mål i att underlätta och förbättra design, implementation och underhåll av mjukvaruprojekt. Som del i utvecklandet av en enterprise-application i Java baserad på Spring Framework har dessa två teknologier utvärderats baserat på hur de påverkar design, underhåll och testning under utvecklingsarbetet. Rapportens slutsats är att dependency injection är relativt lätt att införa och gynnar en design med färre och tydligare beroenden samt mer enhetlig kod. Depenceny injection fanns vara speciellt fördelaktigt i samband med enhetstestning, bland annat då införandet av mockup objekt underlättades. Aspect-oriented programming är en programmeringsmetodologi som tillhandahåller ett sätt att utveckla och hantera så kallade cross-cutting concerns, aspekter av ett program som påverkar många olika beståndsdelar, på ett mer modulariserat sätt än konventionell objektorienterad programmering. Dock innebär detta ett ganska annorlunda sätt att angripa design och identifiera lämpliga kandidater som kan implementeras som aspekter. Detta gör att tröskeln för att börja använda aspectoriented programming är relativt hög, både för utvecklaren så väl som för den som skall underhålla källkoden i efterhand.

Contents 1 Introduction 1.1 Problem Background . 1.2 Purpose . . . . . . . . 1.3 Problem definition . . 1.4 Limitations and Scope 1.5 Outline . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

1 1 1 2 2 2

2 Theory 2.1 Spring Framework . . . . . . . . . . . . . . . . . . 2.2 Dependency Injection . . . . . . . . . . . . . . . . 2.2.1 Background . . . . . . . . . . . . . . . . . . 2.2.2 Introduction . . . . . . . . . . . . . . . . . 2.2.3 Example . . . . . . . . . . . . . . . . . . . . 2.2.4 Dependency Injection in Spring Framework 2.2.5 Other Dependency Injection Frameworks . 2.3 Aspect-Oriented Programming . . . . . . . . . . . 2.3.1 Background . . . . . . . . . . . . . . . . . . 2.3.2 Introduction . . . . . . . . . . . . . . . . . 2.3.3 Terminology . . . . . . . . . . . . . . . . . 2.3.4 Example . . . . . . . . . . . . . . . . . . . . 2.3.5 Aspect-Oriented Programming Frameworks

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

. . . . . . . . . . . . .

5 5 6 6 6 7 8 9 9 10 10 11 13 15

3 Method 3.1 Overview . . . . . . . . . . . 3.1.1 Literature Study . . . 3.1.2 Requirements Analysis 3.1.3 Design . . . . . . . . . 3.1.4 Development . . . . . 3.1.5 Evaluation . . . . . . 3.2 Alternative Methods . . . . . 3.3 Tools . . . . . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

19 19 19 20 20 20 21 21 21

4 Application Overview

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

23

4.1 4.2 4.3 4.4

4.5

4.6

Background . . . . . . . . . . . . . . Goal . . . . . . . . . . . . . . . . . . Actors . . . . . . . . . . . . . . . . . Process Overview . . . . . . . . . . . 4.4.1 Advertisement . . . . . . . . 4.4.2 Receive Application . . . . . 4.4.3 Selection . . . . . . . . . . . 4.4.4 Communication . . . . . . . . 4.4.5 Interview . . . . . . . . . . . 4.4.6 Response . . . . . . . . . . . Requirements . . . . . . . . . . . . . 4.5.1 Non-Functional Requirements 4.5.2 Additional Requirements . . User Interface . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

. . . . . . . . . . . . . .

23 23 24 24 25 25 25 25 26 26 26 26 27 27

5 Architecture and Design 5.1 Architecture . . . . . . . . . . . . . . . 5.1.1 Application Server . . . . . . . 5.1.2 Data Storage . . . . . . . . . . 5.1.3 Data Access . . . . . . . . . . . 5.1.4 Business Layer . . . . . . . . . 5.1.5 Presentation Layer . . . . . . . 5.1.6 Rendering . . . . . . . . . . . . 5.1.7 Build Tool . . . . . . . . . . . . 5.2 Design . . . . . . . . . . . . . . . . . . 5.2.1 Application Design . . . . . . . 5.2.2 Dependency Injection . . . . . 5.2.3 Aspect-Oriented Programming

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

29 29 29 29 30 30 30 31 31 31 31 33 34

6 Evaluation 6.1 Dependency Injection . . . . . 6.1.1 Design . . . . . . . . . . 6.1.2 Maintainability . . . . . 6.1.3 Testing . . . . . . . . . 6.2 Aspect-Oriented Programming 6.2.1 Design . . . . . . . . . . 6.2.2 Maintainability . . . . . 6.2.3 Testing . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

35 35 35 36 37 38 38 39 41

7 Conclusion 7.1 Dependency Injection . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Aspect-Oriented Programming . . . . . . . . . . . . . . . . . . . . . 7.3 Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43 43 43 44

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

Bibliography

45

Appendices

46

A References A.1 Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2 Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47 47 48

Chapter 1

Introduction 1.1

Problem Background

Dependency injection and aspect-oriented programming are two technologies that have gained popularity lately, partly because they are important parts of wide spread application frameworks like the Spring Framework. Although the two technologies are not related to each other they both have a common goal of improving design, implementation and maintenance of software development projects. The approach to achieve this is to provide a framework that reduces coupling between objects and, respectively, allow modularization of so-called cross-cutting concerns.

1.2

Purpose

However, what are the consequences of using dependency injection and aspectoriented programming in a real-world project? How does it affect the design and architecture of a system? How is development and testing impacted? Are the technologies fire-and-forget silver bullets or is there more to it? It is along those lines the purpose of this Master’s thesis is formulated. The purpose of this Master’s thesis is to evaluate the consequences of introducing and using dependency injection and aspect-oriented programming from a practical perspective in a real-world project. A real-world project in this context is a time-boxed project with limited resources and a real customer including unclear specifications and changing demands. The background of this real-world project is to develop a prototype of a webbased enterprise application that supports the Human Resource department at the company Avalanche Studios during the recruitment process of new employees. The purpose of the system is to automate parts the recruitment process or make them more effective by supporting the recruiters from a job application is received until a decision is made about a candidate. The system is Java based and developed using the Spring Framework. The project is divided into two major parts. The first part consists of requirement 1

CHAPTER 1. INTRODUCTION

analysis and producing an architecture and design for the system. The second part implements the design using agile methods. The project is then evaluated according to the problem definition.

1.3

Problem definition

Considering the purpose of this Master’s project a number of questions have been formulated that are pivotal during the project and of interest to address in the resulting evaluation section. What does the use of dependency injection and aspect-oriented programming mean for application design? • In what areas are dependency injection and aspect-oriented programming used? • How is the design affected in terms of modularization and overall code architecture? What does the use of dependency injection and aspect-oriented programming mean for application development? • How is refactoring and maintainability affected? • How is testing, especially automated unit testing, affected?

1.4

Limitations and Scope

Although dependency injection and aspect-oriented programming are techniques applicable to many different environments, applications and programming languages the focus is on Java, the Spring Framework and the particular application developed as part of this Master’s project. One of the most important aspects in development of enterprise applications are how they are maintained during time after initial deployment, especially by other developers than the original. Due to practical reasons this is not covered in this Master’s project even if related areas such as design are discussed. The requirements gathering phase of the project does result in far more functionality than what is possible to implement in the prototype application. However, in order to achieve a real-world project the design and architecture is developed with all requirements in mind, although only a subset will actually be implemented.

1.5

Outline

The outline of the thesis is as follows:

2

1.5. OUTLINE

Chapter 1: Introduction (this chapter) - provides background of the thesis and a problem definition. Chapter 2: Theory - describes the theory behind dependency injection and aspect-oriented programming with emphasize on Spring Framework. Chapter 3: Method - describes how the thesis work is structured into different phases involving research, design, implementation and evaluation. Chapter 4: Application Overview - presents an overview including background and requirements of the application that is developed. Chapter 5: Architecture and Design - describes the architecture behind the application and how it is designed and implemented. Chapter 6: Evaluation - evaluates the design and implementation in regard to the use of dependency injection and aspect-oriented programming. Chapter 7: Conclusion - draws conclusion based on the previous evaluation and suggests possible future work.

3

Chapter 2

Theory This chapter first introduces the Spring Framework which is the framework used by the application developed as part of this thesis, next dependency injection and aspect-oriented programming will be described in detail. The reader is assumed to have basic knowledge of design and architecture in regard to software development.

2.1

Spring Framework

The application developed as part of this Master’s project will be built using the Spring Framework. Spring Framework is an open source application framework for Java originally developed by Rod Johnson and commercially supported by the company SpringSource. The fundamental mission statement of the Spring Framework is to make enterprise application development easier and less complex, especially in comparison to J2EE [10]. The aim of the Spring Framework is that it should be largely non-invasive and applications developed should not depend on the Spring API. In line with this aim Spring Framework does support integration with a range of other solutions, such as Hibernate and other object-relational mapping solutions as well as web application frameworks like Struts. This makes it easy to use only parts of the Spring Framework together with other existing solutions. The Spring Framework provides a lot of functionality divided into modules which may be seen as smaller frameworks by themselves. Modules include frameworks for data access, transaction management and security to support for remote management, messaging and testing. What is most interesting among these modules are the inversion of control container that provides configuration of application components using dependency injection and the aspect-oriented programming framework. The support for dependency injection and aspect-oriented programming in the Spring Framework will be 5

CHAPTER 2. THEORY

described further in the corresponding sections about these topics below.

2.2

Dependency Injection

Dependency injection is introduced in this section including examples. How dependency injection is supported by the Spring Framework is explained followed by a brief overview of other dependency injection frameworks.

2.2.1

Background

Dependency injection originates from the concept of inversion of control. The point of inversion of control is to achieve looser coupling and make modules more reusable by letting a container or framework take responsibility of the flow of control of an application [9]. That is, inverting the flow of control compared to traditional architecture, hence the name. Dependency injection is a special form of inversion of control where it is the acquisition of dependencies that is inverted. The term dependency injection was coined by Martin Fowler in 2004 [5].

2.2.2

Introduction

Using a dependency injection framework the responsibilities of construction, wiring and assembly of objects are removed from the clients or services themselves and instead provided by the framework, thus inverting the flow of control. Instead of making an object being responsible of getting hold of a service using a factory pattern [6], service locator or creating it the object may instead simply provide a property that holds the reference that gets automatically set by the framework when the object is created. There are three different types of dependency injection styles; constructor injection, setter injection and interface injection. The difference between these types is how they provide an object with references to dependencies. • Constructor injection: references to dependencies are provided through the class constructor. • Setter injection: the object exposes a setter method that the dependency injection framework uses to inject the dependency. • Interface injection: the object implements an interface that contains a method that allows the object receive a reference to the dependency. Next an example is given to illustrate the use of dependency injection. 6

2.2. DEPENDENCY INJECTION

2.2.3

Example

An example will be given using a conventional approach followed by examples using dependency injection. The examples are unrealistically small but concise enough to illustrate what is happening. The example is a class representing a car. Characteristics (data) of the car like weight, dimensions, engine type and so on are read from an external source and assigned to the object. The classes reading the data all implements the DataReader interface. The car object may look like this (only the relevant code is included): public class Car { private DataReader reader; ... // other variables representing the car public Car() { DataReader reader = new DataFileReader("cardata.txt"); } }

It is the car object that creates the data reader explicitly using new. A somewhat more flexible approach would be to use the factory design pattern but the main problem that the car itself is responsible for the type of data reader still remains. Refactoring the car class to use dependency injection using the constructor it would look like: public class Car { private DataReader reader; ... // other variables representing the car public Car(DataReader reader) { this.reader = reader; } }

The significant difference with this solution is that the dependency is injected into the Car class; the Car class does not know, and should not know, what kind of data reader it gets. Using setter injection the data reader property is set using a setter method after the object is created instead of as a constructor parameter. public class Car { private DataReader reader; ... // other variables representing the car public Car() { } public void setReader(DataReader reader) { this.reader = reader; } }

Using dependency injection neither class (Car or DataReader) need to be aware of or explicitly ask for each other. The Car class simply needs to accept a data 7

CHAPTER 2. THEORY

reader object and does not need any logic of how to create a data reader class, allowing the remaining code to focus on the ”business logic” instead of including boilerplate infrastructure code for wiring and construction of dependencies [19]. What is left out of these examples is how the dependency injection is configured. This depends on the dependency injection framework and may be done using configuration files, annotations or programmatically. In the next section examples are given for using dependency injection in Spring Framework.

2.2.4

Dependency Injection in Spring Framework

Continuing the example in the previous section the next thing to do is configuring the wiring of the objects. In the Spring Framework the most common way to do this is using XML-files [11]. As of version 2.5 of the Spring Framework it is also possible to use annotations to configure dependency injection. In the example a DataReader class and a Car class needs to be configured. An example of the XML-configuration (with XML header omitted for clarity) follows below.

The id is used to identify the object (bean) and the class field specifies what class to instantiate. In the example a DataFileReader instance is created using constructor injection to set the source file. The Car object is using setter injection to automatically set a reference upon creation to the file reader object. This is done by referencing the previously specified id filereader. The following code example shows how to get a reference to the Car instance using a BeanFactory. package se.kth.spett.di; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.code.io.FileSystemResources; public class Example { public static void main(String[] args) throws Exception { BeanFactory factory = new XmlBeanFactory(new FileSystemResource("config.xml")); Car car = (Car) factory.getBean("car"); car.doStuff(); } }

8

2.3. ASPECT-ORIENTED PROGRAMMING

Using the getBean() method of the BeanFactory object with the previously defined id as an argument a reference to the instance of the Car object is retrieved. When an object is defined the default scope is set to singleton. That means that there will be a single shared object instance for all subsequent requests and references. The opposite is the prototype scope where a new instance is created each and every time the prototype is referenced. Additional types exist where it is possible to scope an object to the life-cycle of a HTTP request or session.

Auto-wiring The Spring container does also support auto-wiring between defined objects. Using auto-wiring it is possible to automatically let Spring resolve how object should be wired by expecting the class itself using either property name or property type. With auto-wiring it is possible to significantly reduce or eliminate the XMLbased wiring configuration. The disadvantage is that it is harder to see the relationships between objects since there is no explicit configuration anywhere. It is possible to combine auto-wiring with explicit wiring.

2.2.5

Other Dependency Injection Frameworks

Some of the other most popular Java dependency injection frameworks are briefly mentioned here. Guice 1 is an open source dependency injection framework for Java 5 developed by Google. Guice exclusively uses annotations for wiring dependencies. Another framework is PicoContainer 2 that instead of annotations or XML-files provides an API to programmatically configure the wiring of objects. EJB 3 does support dependency injection of two types; field injection and setter injection [17]. Field injection allows the container to inject a reference to a private variable without any public setter method.

2.3

Aspect-Oriented Programming

This section gives an overview of aspect-oriented programming (AOP), starting with some background information and an introduction of what kind of problems it attempts to solve. Next the terminology used is explained followed by a concrete example of aspect-oriented programming applied to provide a solution for a typical cross-cutting concern. Finally, AOP frameworks are described in general, with a particular focus on the aspect-oriented programming in the Spring Framework. 1 2

Google Guice: http://code.google.com/p/google-guice/ PicoContainer: http://www.picocontainer.org/

9

CHAPTER 2. THEORY

2.3.1

Background

Methodologies for programming computers have evolved to make developing software more efficient. The introduction of procedural programming is a big step forward compared to using machine-level languages. Procedural programming evolved and object-oriented programming is now the de facto standard for most new software development projects [15]. Object-oriented programming introduced object abstraction and makes it easier to model common behavior and encapsulate it in a modular way. Modularization makes a system more flexible, easier to maintain and quicker to develop [18]. However, many programming problems are of such type that neither object-oriented programming nor procedural programming does provide a way to sufficiently design and implement these cross-cutting concerns [13]. This often leads to compromises where code for the particular concern is being scattered through-out several modules. This result in code tangling that makes not only the implementation of the concern itself, but all modules affected by it, more difficult to develop and maintain [13]. These types of problems are called cross-cutting concerns, since they cut across different modules, and that is what aspect-oriented programming attempts to solve by providing a way to separate these concerns into modularized aspects.

2.3.2

Introduction

An example featuring a simplified hotel management system will be used to give an introduction to aspect-oriented programming and what it attempts to solve. The hotel management system is used by the hotel staff to book rooms for guests, reserve tables at the dinner restaurant and so on. At first glance the module (or class) responsible for booking a room seems pretty straight forward. First the availability of a room is checked, if it is vacant during the period the guest would like to stay then go ahead and reserve the room for that particular period. However, not anyone in the staff should be able to book a room and hence a security check is needed to verify that the current user actually has the authorization to perform this task. In addition, every task needs to be logged in a database so it is possible to see who did what and when, as well as statistics for the hotel management. Inserting method calls to provide security and logging in the room booking method is simple, but it adds external dependencies to other modules and thus making the room booking module coupling tighter. In the same way it lowers the module cohesion since the room booking module must now also be responsible for user authorization and to trigger logging of the task. Figure 2.1 illustrates how the room module invokes the API of the logging and security modules. Now imagine all other modules in the hotel management system, from booking 10

2.3. ASPECT-ORIENTED PROGRAMMING

API calls Security module

bookRoom(...)

Logging module

Room module

Figure 2.1. Logging and security implemented in an non aspect-oriented way. The room module is responsible for invoking the API of the logging and security modules.

the sauna to reserving dinner tables and managing staff vacations. It is easy to see that concerns such as security and logging must be adhered to in many modules, scattering calls to security and logging functionality all over the system. Security and logging are typical examples of cross-cutting concerns. These types of cross-cutting concerns are what aspect-oriented programming attempts to solve. In aspect-oriented programming cross-cutting concerns such as security and logging may be modularized into aspects. An aspect may be configured to automatically be executed based on a simple set of rules to remove the responsibility from each module, class or method it will be applied to. As an example a security aspect may be automatically executed before every call to a method with a name that starts with book (e.g. bookRoom(...)). It then only executes the invoked method if the current user is authorized. Figure 2.2 illustrates the room booking implemented using aspect-oriented programming. Using aspect-oriented programming code scattering and code tangling is reduced since the booking modules now only contain their core logic for booking and not calls to facilitate security and logging. This results in lower coupling and higher cohesion thus improving modularization.

2.3.3

Terminology

To be able to discuss aspect-oriented programming some of the most fundamental parts of the vocabulary need to be described more in detail. Even though the terms may be implemented in different ways and supported to various degrees depending on framework, they are general to aspect-oriented 11

CHAPTER 2. THEORY

Automatically woven calls

API calls

bookRoom(...)

Security aspect

Security module

Logging aspect

Logging module

Room module

Figure 2.2. Logging and security implemented using aspect-oriented programming. The aspects define when and how to invoke the logging and security API without the need for any calls from the room module.

programming as a whole. Advice An advice describes what to do when an aspect is applied. It contains the code that performs the actual logic that should be carried out. As an example, if the purpose of the aspect is to log events that are executed in an application the advice will contain the code that results in the appropriate string being written to the log file. An advice can be executed before, after or around join points (which is described next). It may replace the code of a join point, or even bypass it. Join point Join points are all possible places in the code where an advice may be executed. They are called join points because they represent points in code where an advice may be joined (or plugged in). Possible join points may be method calls, construction of objects, an exception being thrown or when a field is modified. What different types of join points that are supported vary between different aspect-oriented programming frameworks. In the logging example used in the advice description, join points would be all possible locations in the code where the logging advice may be triggered. Pointcut A pointcut answer the question of where an advice should be executed by specifying a set of join points. Pointcuts specifies join points usually through class or method 12

2.3. ASPECT-ORIENTED PROGRAMMING

names, either explicitly or by using regular expression patterns. Exactly how pointcuts are expressed vary between different framework implementations [22]. Some does also allow the creation of dynamic pointcuts that depend on runtime decisions; such if an advice should be applied depending on the value of a method parameter. Continuing the logging example a pointcut decides where logging should be done (out of all possible join points), for example when methods starting with the string book are called. The most common types of pointcuts include before, after and around. The first two are triggered just before and after a method is invoked, while the around advice encapsulates a method call and therefore allows code to be executed both before and after, including skipping the call completely. Introduction An introduction enables the ability to add new methods or attributes to an already existing object. This means it is possible to give existing classes new behavior without changing them. The concept of introductions is analogous with mixins [3] popular in languages like Python and Ruby. Aspect The central unit where advice, pointcuts and introductions are combined is called an aspect. Beside these aspect-oriented specific details it may also contain variables, methods and nested class members, just like a normal class in Java. An aspect in the logging example defines everything there is to know about it, including where logging should be applied (pointcuts) and when and how the logging should be done (advice). Target The target is the object that the aspect is applied to. Weaving Weaving is the process of combining aspects and the target objects into a complete system. Aspects are woven into objects at the specified join points. The weaving itself may be done in several ways; at compile time, at classload or during runtime.

2.3.4

Example

The following examples are extremely simplified and contain no business logic so the execution flow is easy to see. The purpose is to give a basic example of how 13

CHAPTER 2. THEORY

aspect-oriented programming looks like and how it is different compared to pure object-oriented programming. The main class is Room that contains the bookRoom() method that takes an integer that identifies what room to book. The main method simply makes two calls to illustrate the execution flow when invoking the bookRoom() method. package se.kth.spett.aspectj; public class Room { public void bookRoom(int id) { System.out.println("Room " + id + " booking logic performed"); } public static void main(String[] args) { Room room = new Room(); room.bookRoom(1); room.bookRoom(0); } }

Next is the Logging aspect that performs logging before and after a call to the bookRoom() method in the Room class. The pointcut definition logging applies to all public methods in the Room class starting with ”book”, no matter of return value and accepted parameters. Two join points are defined to be executed before and after the logging pointcut is triggered. package se.kth.spett.aspectj; public aspect Logging { public pointcut logging(): call(public* se.kth.spett.aspectj.Room.book*(*)); before(): logging() { System.out.println( "Entering: " + thisJoinPointStaticPart.getSignature().getName() ); } after(): logging() { System.out.println( "Exiting: " + thisJoinPointStaticPart.getSignature().getName() ); } }

The Security aspect has a similar pointcut but applies an around advice and only conditionally proceeds with the original call. The unrealistically simplified code in the example may be replaced with logic that verifies if the current user has permission to book the particular room. If not, the call will not proceed (as illustrated in the example when i is equal to zero). package se.kth.spett.aspectj; public aspect Security { public pointcut access(): call(public* se.kth.spett.aspectj.Room.book*(int));

14

2.3. ASPECT-ORIENTED PROGRAMMING

void around(int i): access() && args(i) { if (i == 0) { System.out.println("Blocked call when i == " + i); return; } else { proceed(i); } } }

Running this trivial example compiled with AspectJ produces the following output: Entering: bookRoom Room 1 booking logic performed Exiting: bookRoom Entering: bookRoom Blocked call when i == 0 Exiting: bookRoom

Each call is logged both before and after a call by the Logging aspect and in the case when bookRoom() is called with 0 (zero) as argument the called is blocked by the Security aspect.

2.3.5

Aspect-Oriented Programming Frameworks

Depending on the framework used to enable aspect-oriented programming the features supported may be different. Differences include what kind of join points that are exposed, how pointcuts are defined, and how and when weaving take place. The Spring Framework is the foundation of the application developed as part of this Master’s project and the built-in support for aspect-oriented programming in the framework will be used. AspectJ is the most widely adopted framework for aspect-oriented programming [12]. These frameworks are briefly described below with an emphasis on how aspectoriented programming in Spring is different from AspectJ. Spring AOP The purpose of the support for aspect-oriented programming in the Spring Framework is to allow users to implement their own aspects in a simple and easy way and to provide declarative enterprise services (most importantly transaction management) in the framework itself [11]. Therefore Spring AOP is not, and does not intend to be, a full-blown AOP framework such as AspectJ. Advice in Spring is written as a standard Java class and pointcuts are specified in a XML configuration file or by using annotations. The weaving in Spring is done in runtime by wrapping the target object with a proxy class. When a call is made the proxy class poses as the target and intercepts calls where advice should be applied. 15

CHAPTER 2. THEORY

Because of this there is no need for a special compilation process, everything is pure Java and applications may be used directly in a J2EE web container or application server. The most apparent limitation of Spring AOP is that it does only support method join points. The possible types of advice available are: • Before advice: Advice is executed before a method but may not prevent execution of the method unless an exception is thrown. • After returning advice: Advice is executed after a method returns normally. • After throwing advice: Advice is executed if a method exits by throwing an exception. • After (finally) advice: Always executed after a method has executed. • Around advice: Surrounds a join point and may perform custom behavior before and after a method invocation, it may also decide to skip executing the called method altogether. If more fine-grained advice than what is possible with method join points is needed it is also possible to use AspectJ within the Spring Framework. AspectJ AspectJ is the most widely used standard for aspect-oriented programming and was a product of the research led by Gregor Kiczales at Xerox PARC [13]. The first public version of AspectJ was released in 2001. AspectJ is now an open-source project in the Eclipse Foundation [1]. AspectJ is an extension to Java where the AspectJ language is used to specify pointcuts and how they should be applied and Java is used to implement the core concerns. Every Java program is a valid AspectJ program and the AspectJ compiler produces standard Java class files that may be executed by any compliant Java virtual machine. AspectJ supports compile-time weaving, post-compile weaving (weaving aspects into already compiled classes) and load-time weaving (LTW). With loadtime weaving the weaving takes place when Java virtual machine uses the class loader to load the class. EJB 3 EJB 3 (Enterprise JavaBeans), part of JavaEE 5, supports aspect-oriented programming in terms of interceptors. The AOP features supported are limited; however, they don’t require the complexity related to introducing a full-fledged AOP framework like AspectJ [17]. Interceptors are objects that are automatically executed when a method is invoked in an EJB object. The type of pointcut supported is the around advice, 16

2.3. ASPECT-ORIENTED PROGRAMMING

that allow custom behavior to be executed at the beginning of a method, as well as after the method with the possibility to react to both return value and thrown exceptions.

17

Chapter 3

Method This chapter describes the different stages of how the project was carried out followed by a short reflection on the method used with possible alternative methods. The main purpose of this Master’s project is to evaluate the consequences of using dependency injection and aspect-oriented programming in a real-world project. Hence, the method resembles very much the process of a software development project at a company. Following the development phase an evaluation is conducted to recapitulate the questions put forward in the problem definition.

3.1

Overview

The sections below give an overview of the different stages of the project.

3.1.1

Literature Study

Initially a literature study was done with three goals in mind, although somewhat overlapping. The first goal was to gather a basic knowledge of the fundamental technologies and tools used in the development of the prototype application. Most notable this involved reading books about the Spring Framework [23], Hibernate [2] and Java Enterprise design and development in general [10]. The second goal was to study the principles of dependency injection and aspectoriented programming in general. Since they both are fundamental parts of the Spring Framework this was partly already covered. In order to get more general knowledge of application of the techniques outside the Spring Framework the literature study was complemented with journal articles [24] and further reading about AspectJ [15]. The third and final goal was to gather knowledge about related academic work about the use and consequences of dependency injections and aspect-oriented programming. This part included reading the original paper published about aspectoriented programming by Kiczales et al [13]. 19

CHAPTER 3. METHOD

Worth noting is that there are a fairly large amount of published articles and papers about aspect-oriented programming while those about dependency injection (and inversion of control) is much more rare. This is no surprise since the use of dependency injection is more about introducing a design pattern [5] compared to the introduction of a new programming paradigm [8], where the latter has received much more interest from the academic world.

3.1.2

Requirements Analysis

A requirements analysis of the application to be developed, the recruitment system, was done by meetings, discussions and interviews with users and stakeholders of the system. The focus of this phase was to understand the recruitment process and identify where in the process the most benefit of a software system may be achieved. This approach was chosen, instead of asking for a set of feature specifications, because users usually don’t understand what they want or have a clear idea of their requirements [16]. The gathered requirements were put together and presented to verify their validity.

3.1.3

Design

Based on the identified features that are deemed as feasible to be part of the prototype an overall architecture and design of the application was done. The goal of the design was to achieve best practices using the Spring Framework [23] and software design in general. What is important to note is that design decisions were made based on factors involving requirements, feature scope, time constraints, previous knowledge and expertise. Although the use of dependency injection and aspectoriented programming was always in mind, these principles was only used where it made sense rather than being forcefully applied everywhere just because they are the topic of this Master’s thesis.

3.1.4

Development

The development phase of the project was divided into four sprints (time periods) of about two weeks realized in what might resemble a one-man Scrum project [14]. What this basically means is that a product backlog consisting of the identified features is created. For each sprint the features with the highest priority (from a business perspective) is picked to be included in the backlog for the upcoming sprint (sprint backlog). When new requirements arise, both in terms of identified new features or fixes/additions to existing features, they are added to the product backlog. Although the Scrum methodology is much more than that [20] it served as a good way to prioritize what should be done during the development phase. Actual implementation was done focusing on getting a feature to work in the most basic way and then refactoring the code as needed. Adequate testing code 20

3.2. ALTERNATIVE METHODS

for unit testing and automatic integration testing were implemented as part of each feature.

3.1.5

Evaluation

Finally the evaluation phase addresses the questions presented in the problem definition. Although the evaluation step is the last, it does permeate the whole process from requirements gathering to development and deployment.

3.2

Alternative Methods

The method described that will be used is based on a subjective criterion and has a qualitative rather than quantitative approach. This is something that needs to be considered in the evaluation phase since background and experience of the author in combination with the subjective nature of the evaluation is something that will be a factor even if the goal is to minimize it as much as possible. The choice of method is largely the result of time and resource constraints and the circumstances around this particular Master’s thesis work. Alternative methods would include developing two systems according to the same requirements, one using aspect-oriented programming and dependency injection and another only using an object-oriented approach. Using such a method it may be easier to identify the differences and consequences of each approach. Building on this alternative method a more quantitative and empirical method would involve several developers assigned to develop the different approaches. To empirically evaluate aspects like readability, understandability and maintainability of the different approaches a set of other developers may be tasked to perform changes on the different systems. Also defined measures like CBM (coupling between modules), LCO (lack of cohesion in operations) and RFM (response for a module) may be used for comparison [4] together with metrics to indicate system size like non-comment lines of code and number of modules.

3.3

Tools

Several tools, all free open source, were used during the development of the prototype application1 . As a development environment NetBeans IDE 6.1 was used with Maven 2 for build management. Unit testing was done using JUnit while Canoo WebTest was used for automated integration testing. Subversion was used for version control and Hudson served as an automated continuous integration tool. Primarily GlassFish was used as Java application server. Also Jetty, which is a lightweight servlet container, was used for testing. 1

See appendix A for references and web links to the different tools mentioned here.

21

Chapter 4

Application Overview The real-world project the questions in the problem definition are applied to is the development of a web based recruitment system. The purpose of the system is to support the Human Resource department during the recruitment process, from receiving an application until a decision is made to hire or decline a candidate. The purpose of this chapter is to describe the background, requirements, scope and interface of the system to give a better understanding of the project and what is developed.

4.1

Background

The recruitment process at Avalanche Studios is managed more or less manually using a simple Excel document to track status and keep information related to a candidate. With more than one recruiter and several technical specialists involved in the process this is getting out of hand in regard to communication (internal and with the candidate), managing data and files like portfolios and work examples, keeping relevant information in one place and so on. Avalanche Studios is one of the largest game developers in Sweden developing games primarily for Xbox 360, Playstation 3 and PC. As of July 2008 there are about 160 employees at Avalanche Studios. Being a game developer means there are several categories of employees and candidates with slightly different requirements from a recruitment process perspective. A programmer, 3D artist and producer need to be evaluated differently in the recruitment process because their discipline requires different skills (technical, aesthetical, administrative, leadership, etc).

4.2

Goal

The main goal of the application is, in regard to the recruitment process, to support the Human Resource staff (the recruiters) during the whole recruitment process and make their work more effective. This is achieved by automating as many tasks as 23

CHAPTER 4. APPLICATION OVERVIEW

Advertisement

Receive Application

Selection

Interview

Response

Communication

Figure 4.1. After an application is received the candidate is evaluated. The evaluation including selection, communication and interview is iterative, where a candidate usually is interviewed more than one.

possible in the recruitment process and by providing support for other activities by allowing the system to manage and distribute relevant information.

4.3

Actors

There are persons with different roles that will interact with the system. They are referred to in this document as candidates, recruiters and specialists. • Candidate: The person applying for a position by sending in a resume and other related information like a personal letter and portfolio. • Recruiter: The person doing the recruitment including selecting candidates, conducting interviews, and so on. There are multiple recruiters using the system and multiple recruiters may be involved in the recruitment process of a single candidate. The terms recruiter and user are used interchangeably in this document since the primary user of the application is a recruiter. • Specialist: A person in the company with specialist knowledge (like an art director or lead programmer) or in a management position that is involved in the recruitment process, primarily by giving feedback on the selection of candidates and conducting interviews. There are several specialists and they are different depending on candidate and position.

4.4

Process Overview

Figure 4.1 illustrates the different steps in the recruitment process. Although the process is generalized it does serve well as the starting point for a brief discussion about the result of the requirements gathering phase prior to development. The intent is to give an understanding of how the application may support each step in the process rather than to specify individual requirements. 24

4.4. PROCESS OVERVIEW

4.4.1

Advertisement

Open positions are advertised on the company website. Publishing and editing positions are managed by recruiters using an administrative interface of the application. The published positions are displayed on public pages that require no authentication. In order to integrate well with the existing company website the public pages must support a custom look and style.

4.4.2

Receive Application

A candidate applies for a position by submitting a form on a web page associated with the desired position. The candidate must fill in all mandatory questions like name and email address. Next the candidate uploads related files to the application like a curriculum vitae, personal letter and examples of previous work (if applicable). Once successfully submitted a candidate profile is created in the application. The candidate profile is the central place where all related information of the candidate is located. A candidate profile may also be created from within the application itself by a recruiter in case when the recruitment process is initiated without an application through the web page. An example of this situation may be when there is a recommendation of a person who have not applied herself.

4.4.3

Selection

All candidates in the application may be sorted or searched based on different criteria to support the selection of candidates in the recruitment process. To allow for useful search queries a candidate is associated with a status and additional fields such as type of position as well as notes and reminders. In the selection process other people in the company may be involved (specialists). In order to facilitate their involvement and avoid duplicating the candidate profile (to send in an email as an example) a recruiter may give limited access to a specialist so the specialist may review information about a specific candidate and enter feedback.

4.4.4

Communication

All communication and discussions with a candidate should be logged and available for others as well as for archiving in case of later review. A candidate profile in the application does allow for notes in terms of log entries to be stored. A log entry may be created to associate information with a candidate together with a date and information about who created the log entry. This information is then visible to other recruiters reviewing the candidate. Ultimately all communication would automatically be associated with a candidate, like an email conversation. This is possible by having the application interact 25

CHAPTER 4. APPLICATION OVERVIEW

with an email server. However, this feature is considered out of scope due to the time constraints involved with a Master’s project.

4.4.5

Interview

The interview phase of the recruitment process is a way to get more information about a candidate to make a better selection of which candidates to proceed with and which to reject. The application does support the interviewing in two ways. It does provide an easy way to review and print all information about a candidate using the candidate profile and after an interview is finished an evaluation and summary of the candidate based on the interview may be entered.

4.4.6

Response

The application may send out scheduled or on-demand emails to a candidate, making it possible for a recruiter to easily communicate a decision to the candidate in the recruitment process. This specific feature was asked for when there are many unqualified candidates applying for a position and they need to be rejected. Instead of rejecting them initially an email may be scheduled to communicate the decision at the same time as the period when applications are accepted has ended.

4.5

Requirements

The result of the requirements analysis includes far more functionality than what is possible to realize within the scope of a Master’s project. Only a subset of the identified features will be implemented as part of the prototype developed. The main goal is not to include as many features as possible in the implementation as long as the scope of the application is substantial enough to provide a foundation for evaluation and addressing the questions asked in the problem definition. The previous section discussing the recruitment process and how the application may support it is the result of discussions with staff from the Human Resource department over several meetings. In addition to those features there are some additional requirements and non-functional requirements discussed below.

4.5.1

Non-Functional Requirements

Some non-functional requirements were identified during the requirement analysis. • When designing the application the top priority should be to keep it simple to the user. Rather fewer and more generic features than a lot of specialized functionality. 26

4.6. USER INTERFACE

• No prior knowledge, expect basic web browser usage, on behalf of the users of the application may be assumed. • All components, including framework, libraries, development platform and hosting services (database, application server, operating system, etc) must be free and require no license cost. • The application should require no or little additional system maintenance beside regular duties common to all hosting (backup, security patching).

4.5.2

Additional Requirements

User authentication and authorization is needed since recruiters, specialists and candidates are all somehow interacting with the system but should have fundamentally different permissions in terms of what they may do and what information they may see. To manage users of the application user administration functionality must be provided that allows creating, editing, disabling and removing users.

4.6

User Interface

The diagram in figure 4.2 illustrates a simplified overview of the different parts of the application from a user interface perspective. The diagram is divided into two major sections. The internal (left) section contain the different views available to recruiters and specialists; hence users must be authenticated to access the internal pages of the application. Authentication is done using username and password. To the right is the public section which represents the pages accessible by candidates when applying for a position.

27

CHAPTER 4. APPLICATION OVERVIEW

Internal

Overview

Positions: List

Candidates: List

Public

Statistics

User Administration

Positions: List

User View / Edit

Info

Search Position View / Edit

Filter

Roles

Candidate View / Edit

Apply

Log Reminders Attachments

Figure 4.2. User interface overview.

28

Chapter 5

Architecture and Design This chapter describes the general architecture of the application and the different technologies used followed by a more detailed design of the implementation. The intention is to give an overview of how the application has been implemented without dwelling on specific solutions only relevant to requirements applicable only in this particular case. In the same way, specific information about the use of third-party and framework modules is only briefly mentioned, instead of duplicating already existing framework reference documentation.

5.1

Architecture

The intention is to give an overview of which components are used with a brief description explaining what they are and their purpose in the application. Figure 5.1 shows the general architecture with what components are used for the different layers to develop the application.

5.1.1

Application Server

To run the application GlassFish v2 is used. GlassFish is an open source application server for the Java Enterprise Edition (Java EE) platform headed by Sun Microsystems. It is also called Sun Java System Application Server 9. Although GlassFish is a fully compliant Java EE 5 application server, not all features are required considering the feature set of the application that is within the scope of the prototype. A servlet container like Apache Tomcat or Jetty would also be possible to use.

5.1.2

Data Storage

Data is stored using MySQL which is a relational database management system. Due to the design and the data access layer of the application MySQL is not a requirement by itself and may be substituted by most other popular SQL database systems. 29

CHAPTER 5. ARCHITECTURE AND DESIGN

Rendering : JSP

Build : Maven 2

Presentation Layer : Spring MVC

Business Layer : Spring Framework 2.5

Data Access (ORM) : Hibernate 3

Data Storage : MySQL 5

Application Server : GlassFish v2

Figure 5.1. Overview of components in application architecture.

5.1.3

Data Access

Persistence of objects and management of relational data is done using the Java Persistence API (JPA) with Hibernate as the Java persistence framework. Hibernate is an object-relational mapping (ORM) framework for Java that makes it possible to map classes in Java to database tables.

5.1.4

Business Layer

At the core of the application is the Spring Framework that, among many other things, provides modules that facilitates dependency injection and aspect-oriented programming.

5.1.5

Presentation Layer

Spring MVC, the Model-View-Controller framework of the Spring Framework is used for web related logic and the presentation layer. 30

5.2. DESIGN

5.1.6

Rendering

JavaServer Pages (JSP) is used to render the actual view of the Model-ViewController architecture provided by Spring MVC. Also a number of custom made and publicly available tag libraries was used, most notable those part of the Spring Framework. Additionally, Display Tag was used to enhance the display of tables in the application.

5.1.7

Build Tool

Apache Maven is a build automation tool somewhat similar in functionality to the more well-known Apache Ant. One key difference is that Maven may automatically download component and library dependencies from the Internet, making it easier to maintain a complex project.

5.2

Design

This section describes the overall design of the application in terms of how it is implemented. The general design is described followed by a discussion about the particular use of dependency injection and aspect-oriented programming in the application.

5.2.1

Application Design

The implementation design of the application is illustrated in figure 5.2. The blocks with text in italic font are non-custom components (Spring MVC and Spring Security is part of the Spring Framework) and Hibernate is a component by itself. The fact that the implementation uses the Spring Framework may be imagined as another box encapsulating the whole figure. As illustrated in the figure the security module of the Spring Framework called Spring Security is leveraged to provide authentication and security throughout the application. What is described is the general implementation design of the application rather than details that only relates to specific functionality.

DAO Data Access Objects (DAO) are used to abstract and encapsulate all access to the underlying persistence system and data base. All Hibernate specific code is located in Data Access Objects. Typically there is a DAO for each object in the domain model. 31

CHAPTER 5. ARCHITECTURE AND DESIGN

Spring MVC View Model Controller Spring Security

ServiceManager

DAO

Hibernate Model DB

Figure 5.2. Cross-section diagram of application implementation design.

ServiceManager It is in the service managers where all business logic code resides. Controllers use one or more service manager objects and each service manager object use one or more DAOs. Model-View-Controller The Model-View-Controller (MVC) architectural pattern is used to separate business logic from presentation in the web interface. This is implemented using the Spring MVC module that is part of the Spring Framework. The model represents the actual content that is manipulated or displayed and the controller manages and determines what is to be displayed. The view is the actual rendering of the model. 32

5.2. DESIGN

In this application the model often resembles a persistent java bean. A controller is a servlet and the view is a JSP page. A typical scenario is when a user clicks a link on a web page of the application. Then a HTTP GET request is made that is managed by a dispatch servlet. Depending on the URL the dispatch servlet forwards the request to the appropriate controller that populates a model. The model is rendered using a view and ultimately displayed as a HTML page. Design by Interface To achieve decoupling, classes interact with each other using interfaces rather than specific implementations in form of classes. This technique is called design by interface [21]. The purpose with design by interface is to have looser coupling between different parts of the application and have less dependencies on specific implementations. The technique is applicable on both class part of the standard Java library as well as application specific classes. As an example, by accepting a java.util.Map (which is an interface) as a method parameter instead of a java.util.HashMap (which is an implementation) it is possible to replace the implementation of the map without breaking anything. This is possible since the dependencies exist only to the interface, and not the actual implementation. This technique together with dependency injection is especially powerful. When performing unit testing mockup objects are inserted instead of full implementations. As long as they implement the correct interface the object that is tested will not know the difference.

5.2.2

Dependency Injection

Dependency injection, and the Inversion of Control container in the Spring Framework providing the dependency injection, is such a central part so it is used more or less throughout the whole application. It is used to create objects, everything from DAOs to services and controllers, and configure them by using injection dependencies either using setters or in the constructor. Here are some examples of how it is used. All DAO classes are instantiated and injected with a reference to the Hibernate session factory which enables persistence. Service implementations in the service layer are injected with the needed DAO instances and controllers are injected with the requested services. Beside the injection of references to other objects it is used for property configuration of objects. As an example, a controller object managing a web form has a property called successView that decides what view to render if the submitted form was successfully validated. This property value is specified in an XML-file and set upon object instantiation via dependency injection. 33

CHAPTER 5. ARCHITECTURE AND DESIGN

5.2.3

Aspect-Oriented Programming

Aspect-oriented programming is applied in two different ways, both related to security. In the service layer an around advice has been applied to methods that ensure only users authenticated as administrators may access those specific methods. If the user is not an administrator the invoked methods are not executed and an exception is thrown resulting in an error page being displayed to the user. The other advice is responsible for enforcing security and only allowing administrators to edit a user, except when users edit themselves. This is also done in the service layer by explicitly naming the saveUser() method of the UserManager class in the pointcut definition. The advice makes use of both the types before and after.

34

Chapter 6

Evaluation In this chapter the results of using dependency injection and aspect-oriented programming in the development of the aforementioned application are evaluated and discussed with the problem definition in mind. The chapter is divided into two sections, one discussing dependency injection and one discussing aspect-oriented programming. In each section the most significant findings are presented divided into sub sections after the original questions posed in the problem definition. The sub sections are design, maintainability and testing. Design is about how the application architecture and design of implementation is affected by the use of the technology in question. Maintainability deals with the consequence of how easy the implementation is to maintain (for the original developer but more importantly for someone else not familiar with the code) as a result of the use of dependency injection and aspectoriented programming. Maintainability includes properties such as readability, impact on refactoring and overall understandability of the code. Finally testing includes testing of the application as a whole as well as features introduces by the technology itself. These three aspects are not discrete and may often overlap, as an example, something that improves the design and architecture of an implementation may often also make the implementation easier to maintain. The evaluation, discussion and possible conclusions in this chapter are all made by the author based on the experience gathered from the design and implementation of the application described in this master thesis.

6.1 6.1.1

Dependency Injection Design

Easy to implement Dependency injection is such an integral part of the Spring Framework that it is more or less not possible to be without it unless just single modules of the Spring 35

CHAPTER 6. EVALUATION

Framework are used. Classes in the Spring Framework are all designed to have dependencies injected where possible. Hence there is no question if dependency injection should be used or not if the power of the Spring Framework is leveraged, instead it is more interesting to look at the impact of design using dependency injection on objects not coupled with the framework itself. The use of dependency injection does not affect any business logic or objectoriented design of objects. What it does is it to provide an alternative way to get references to dependent objects. Because of this little or no modifications to the overall design of a system is required to change in order to use dependency injection. Framework or not If using the Spring Framework or another framework where dependency injection is included there is no reason not to use it. However, if for various reasons a framework that provides dependency injection may not be used the concept and pattern of dependency injection is still useful when designing classes. By implementing the classes with interfaces designed for setter- and constructor injection and creating them using a factory like pattern much of the benefit of dependency injection remains without the need to introduce a complete framework.

6.1.2

Maintainability

Less boilerplate code In general less boilerplate code is required when relying on dependency injection instead of conventional lookup or creation of dependent objects. This is the case because dependencies are injected typically using a setter method or in the constructor instead of being created and instantiated, retrieved through lookup methods code or using factories. Less boilerplate code results in higher object cohesion since the code focuses less on infrastructure issues such as construction and retrieval dependent objects and more on performing the application logic. Exposed dependencies By consequently using dependency injection all dependencies of an object are retrieved using constructor or setter injection. This means that it is possible to identify all external dependencies by only looking at the signature of the constructor and the public setter methods. Another approach is to consider the configuration of dependency injection to see what references are injected into each object. Using Spring the dependency injection is typically in XML. Worth noting is that if auto-wiring is used the ability to identify dependencies by looking at the configuration is significantly reduced. 36

6.1. DEPENDENCY INJECTION

The conventional way without using dependency injection would require reading all method implementations line by line. The risk is when conventional dependency lookup is used together with dependency injection. Then it is easy to miss dependencies only visible in code of method implementations if only looking at the method signatures or dependency injection configuration.

Refactoring Dependency injection promotes the design of less coupled code since an object’s dependencies are injected rather than retrieved using infrastructure code in the object itself. This by itself often makes refactoring easier. An example of when refactoring is needed is when dependencies change. For example a collection of data changes from one data type to another. The refactoring is potentially made significantly easier with a system using dependency injection compared to conventional design. This is especially the case when dependency injection is used together with the concept of design by interface (see section 5.2.1). The reason why refactoring becomes easier in this case is because dependencies are injected and the only ”contract” between the dependency and the object performing logic on the dependency is the interface. If a dependency changes but still implements the same interface little or no changes need to be done to the object.

6.1.3

Testing

Following the discussion about refactoring it is easy to see that unit testing benefits as well from dependency injection. In general less coupled objects are easier to unit test, but when there are dependencies they sometimes need to be replaced by mock objects. A mock object is an object that simulates the behavior of a real object when the real object is impractical to use in a unit test. There are several reasons why mock objects may be used, examples are objects with special states that are hard to reproduce (like network errors) or too slow (certain database queries). In unit testing of web applications it is common to use a mock object representing an HTTP request. Dependency injection allow easy replacement of objects with mock counterparts where needed. If dependencies were not as easy exposed this might potentially be a very troublesome operation. 37

CHAPTER 6. EVALUATION

6.2 6.2.1

Aspect-Oriented Programming Design

Separation of concerns Compared to dependency injection aspect-oriented programming has big impact on the design and implementation of an application. With the concept of separating scattered concerns into aspects a solution would be designed significantly different when using aspect-oriented programming instead of only conventional objectoriented programming. With successful use of aspect-oriented programming, cross-cutting concerns previously scattered throughout a system may be largely put into an aspect that is applied where necessary. This has two immediate effects. Code invoking crosscutting concerns may be removed from objects, thus letting the objects focus on their application logic and thereby receive higher cohesion. The flip side of this is that the implementation and triggering of the cross-cutting concerns are centralized into aspects, hence making changes now only requires modification in the aspects instead of (in worst case) all places in the application invoking the cross-cutting concern. A typical example of such cross-cutting concern is logging.

Identifying aspects Aspect-oriented programming introduces a new dimension to design by allowing a code flow different compared to object-oriented programming. This requires a somewhat new way of thinking to take advantage of aspect-oriented programming. Since aspect-oriented programming is a complement to object-oriented programming and not a replacement there is nothing that forces a design to use aspects. Hence the developer must identify what concerns are suitable to be implemented as aspects or not. From designing the application in this Master’s project two approaches to identifying aspects emerged. The first approach is to identify cross-cutting concerns during requirements analysis and the initial stage of designing the architecture before starting implementation. This approach involves identifying business aspects that cross-cut several modules. The second approach is to implement modules and objects in a conventional way and then refactor concerns into aspects once it is obvious that they are crosscutting. A typical example of this is when the same type of code is repeated in several places; the objects are refactored to use an aspect instead of repeating the same type of code. 38

6.2. ASPECT-ORIENTED PROGRAMMING

Aspects or not What became apparent during the development of the application is that it is fairly easy to use aspects for the typical solutions used in many papers as examples; security, logging and transaction management. However, looking beyond these typical applications of aspect-oriented programming the choice often becomes a question of solving something using aspect-oriented programming or the way it was done before. More often than not the latter was chosen. There are several likely reasons for this. First of all it is always easier to do something "the usual way" compared to introducing something new that the developer is likely less familiar with. Secondly most design problems are not new, and therefore there already exist accepted proven solutions for many of these. This is especially the case when using the Spring Framework. Although the Spring Framework has its own Spring AOP support, there are already provided many convenient ways of solving problems that otherwise might have been good candidates to be implemented using aspect-oriented programming. In the same way using design patterns solves several common design problems. Where applicable it is often more convenient to use a proven design pattern instead of introducing an aspect-oriented solution. Worth noting is a quantitative study where the 23 Gang-of-Four [6] patterns were implemented both using object-oriented solutions and aspect-based and then compared. Although it was concluded that most aspect-oriented solutions improved the separation of pattern-related concerns, only four of the 23 patterns exhibited significant reuse in projects [7]. Another factor that must be taken into account is that although a better separation of concerns may be achieved using an aspect-oriented solution it may, in some cases, also result in more lines of code and more complicated design. In such case a more conventional approach may be desirable. Many of these issues are related to that aspect-oriented programming is a relatively new way of designing applications compared to object-oriented programming. When a developer become more accustomed with aspect-oriented solutions the desire to instead use conventional solutions because they are more familiar will gradually be reduced.

6.2.2

Maintainability

Understanding aspects With regard to maintaining an application using aspect-oriented programming the fundamental issue is whether or not the person changing the code (given it is someone not familiar with the code) is used to aspect-oriented programming. Since aspect-oriented programming is complementing object-oriented programming the code flow may not be as straight forward to understand. This is especially true since simply looking at an object and dissecting the code in each method may not reveal that the execution flow may be altered by aspects when methods are 39

CHAPTER 6. EVALUATION

invoked. Trying to understand or debug such as system without a firm grasp of exactly what aspects are applied is troublesome. This is where it is important that the IDE does support aspect-oriented programming and thus may provide hints or symbols to the reader that denotes that an aspect is applied. Even for someone well-versed with aspect-oriented programming this may be of great help when interpreting the result of complex conditional pointcuts. Higher cohesion Using an aspect-oriented approach code that performs the logic for cross-cutting concerns may be removed from each and every method and instead modularized into an aspect. This was discussed in the design section previously. This naturally leads to higher cohesion of the objects and thus makes refactoring easier. Both because there are less and more focused code, but also because the responsibility of invoking the cross-cutting concern logic is moved from the object methods to the aspect. Refactoring cross-cutting concerns Making it easier to refactor cross-cutting concerns is one of the most important advantages of an aspect-oriented solution. Use logging as an example. Calls to log state changes and actions are not uncommonly scattered across many methods and objects. If the logging module is to be refactored changes may in worst case be needed in the scattered invoking code across the whole application. This naturally makes refactoring more error prone and troublesome. With an aspect-oriented solution such changes are isolated to the logging aspect that encapsulates both the logic as well as determine how and when it should be invoked using pointcuts. Object refactoring impact on aspects There is a risk that the intent of an aspect may break when objects which the aspect is applied to are refactored. Pointcuts may be defined using method names and thus the developer refactoring a target object must follow this naming convention if the aspect should be executed as expected. This means that it is necessary for the developer doing the refactoring to know about the applied aspects even if the refactoring itself has nothing to do with the cross-cutting concerns of the aspect. Neglecting this may result in hard to find bugs since the developer changing or adding a method may not know about the relevant aspects that may or may not be triggered. 40

6.2. ASPECT-ORIENTED PROGRAMMING

6.2.3

Testing

In a conventional design cross cutting concerns cannot be unit tested, since they are scattered and therefore not in a unit. In an aspect-oriented design the cross cutting concern is modularized into an aspect, so now there is a unit. However, unit testing aspects is not as straight forward as testing plain objects since the logic an aspect performs is always applied to a target object. During development of the application three different testing approaches were identified. Integration test Probably the simplest way is to perform unit testing on objects where aspects are applied. This becomes a sort of integration test since both the non-aspect-oriented objects are tested together with the aspect itself that is applied to the object. This is easy to setup since the unit test is not any different from any other unit tests. However, extra care should be taken so that the test will also fail if not only the object logic itself fails but also if the aspect behaves in an unexpected way. Mockup target object The downside with the previous approach of integration test is that the testing of the aspect is not isolated. So if the target object fails then the aspect test will also fail even if it is correct. A possible solution to this is to create a mockup target object that the aspect is applied to. From the perspective of the aspect the object is no different than any other target object. The purpose of the mockup target object is that it should only include code that help verifying the code flow and that the advice are correctly triggered at the right join points. It should not include any other logic from the typical target objects (since they are separately unit tested). The benefit of using a mockup target object is that the aspect logic may be tested in isolation. The downside is that potentially many mockup target objects must be created if the pointcut definitions span many different kind of join points. Delegation to objects The third approach is to delegate all aspect logic into separate objects and then do unit testing on those objects. This approach verifies the actual logic performed within an aspect but it fails to verify when the advises are triggered by the pointcut definition. Because of that this approach may very well be combined with using mockup target objects. Another disadvantage is that in some cases it is hard or inconvenient to extract the logic into separate objects.

41

Chapter 7

Conclusion This chapter concludes the evaluation in two sections; discussing dependency injection and aspect-oriented programming. Then a possible continuation of this Master’s thesis is briefly covered in the section about future work.

7.1

Dependency Injection

Dependency injection is easy to understand and does not require substantial changes of an application class architecture, still it promotes a design pattern which results is less coupled modules and less boilerplate code. The use of dependency injection is especially beneficial when it comes to testing and unit-testing in particular. When an object is no longer responsible itself for looking up or creating its dependent objects and resources it becomes much easier to unit test objects. Partly because they are less coupled but most importantly because dependencies are so easily replaced with mockup objects. Many of the particular frameworks, like the Spring Framework as used in the project of this Master’s thesis, include support for dependency injection. Then the use of dependency injection is easy and straightforward. If the use of a dependency injection framework is not possible by any reason some of the benefits may still be leveraged by following the inversion of control principle.

7.2

Aspect-Oriented Programming

The main purpose of aspect-oriented programming is to develop and manage crosscutting concerns in a more modularized way than what is the result of conventional object-oriented programming. This is done by introducing the concept of aspects that are applied to target objects. A pointcut language is used to determine where, when and how changes in variables or invocations of methods of the target object are intercepted with code from the aspect. This is realized through weaving. In case of AspectJ weaving is most commonly done using the AspectJ compiler. In the case of Spring AOP (the 43

CHAPTER 7. CONCLUSION

aspect-oriented programming support of the Spring Framework) the weaving is done at runtime and therefore does not require a special compiler, making it potentially easier to adopt aspect-oriented programming. To benefit from aspect-oriented programming a more or less new way of thinking is required in order to identify concerns as candidates to be implemented as aspects, in contrast to using a conventional object-oriented approach. Although there are many situations where the benefit of aspect-oriented programming is clear, it introduce a complexity because the code flow of the target objects may be changed without it being visible in the code. The introduction of aspects does also require changes to how testing, and especially unit-testing, is conducted. Three different testing approaches were identified; integration testing where unit-testing is performed on the target objects, the use of mockup-target objects to more easily isolate and verify the functionality of the aspect and delegation to objects where the logic of an aspect is split into one or more objects that may be unit-tested separately as any other object.

7.3

Future Work

The most immediate continuation of the work presented in this Master’s thesis is to look more closely into maintainability of the code in a more quantitative approach. Having the two systems developed after the same requirements, one in a pure objectoriented way and the other using aspect-oriented programming. Then task equally experienced programmers where half performs changes on the object-oriented developed system and the other half on the on the aspect-oriented system. The result may be evaluated based the quality of the changes (according to a predefined, but unknown to the developers, set of unit tests), the time it took and the complexity of the solution. Possibly more in-depth interviews with the programmers may be conducted afterwards to get an understanding of their approach.

44

Bibliography [1]

The Aspectj Project at http://www.eclipse.org/aspectj/.

the

Eclipse

Foundation.

[2]

Christian Bauer and Gavin King. Java Persistence with Hibernate. Manning Publications Co., Greenwich, CT, USA, 2006.

[3]

Jonas Boner. Aspectwerkz - dynamic AOP for Java. In Proceeding of the 3rd International Conference on Aspect-oriented software development, New York, NY, USA, 2004. ACM.

[4]

M. Ceccato and P. Tonella. Measuring the effects of software aspectization. In 1st Workshop on Aspect Reverse Engineering, 2004.

[5]

Martin Fowler. Inversion of Control Containers and the Dependency Injection pattern. http://martinfowler.com/articles/injection.html, 2004.

[6]

Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns. Addison-Wesley Professional, January 1995.

[7]

Alessandro Garcia, Cláudio Sant’Anna, Eduardo Figueiredo, Uirá Kulesza, Carlos Lucena, and Arndt von Staa. Modularizing design patterns with aspects: a quantitative study. In AOSD ’05: Proceedings of the 4th international conference on Aspect-oriented software development, pages 3–14, New York, NY, USA, 2005. ACM.

[8]

Timothy Highley, Michael Lack, and Perry Myers. Aspect oriented programming: A critical analysis of a new programming paradigm. Technical report, Charlottesville, VA, USA, 1999.

[9]

Ralph E. Johnson and Brian Foote. Designing reusable classes. Journal of Object-Oriented Programming, 1(2):22–35, 1988.

[10] Rod Johnson. Expert One-on-One J2EE Design and Development. Wrox Press Ltd., Birmingham, UK, UK, 2002. [11] Rod Johnson et al. The Spring Framework - Reference Documentation. http://static.springframework.org/spring/docs/2.5.x/reference/. 45

BIBLIOGRAPHY

[12] Gregor Kiczales, Erik Hilsdale, Jim Hugunin, Mik Kersten, Jeffrey Palm, and William G. Griswold. An overview of aspectj. In ECOOP, volume 2072, pages 327–353. Springer, 2001. [13] Gregor Kiczales, John Lamping, Anurag Menhdhekar, Chris Maeda, Cristina Lopes, Jean-Marc Loingtier, and John Irwin. Aspect-oriented programming. In Proceedings European Conference on Object-Oriented Programming, volume 1241, pages 220–242. Springer-Verlag, Berlin, Heidelberg, and New York, 1997. [14] Henrik Kniberg. Scrum and XP from the Trenches. InfoQ, 2007. [15] Ramnivas Laddad. AspectJ in Action: Practical Aspect-Oriented Programming. Manning Publications Co., Greenwich, CT, USA, 2003. [16] Steve McConnell. Rapid Development: Taming Wild Software Schedules. Microsoft Press, Redmond, WA, USA, 1996. [17] Debu Panda, Reza Rahman, and Derek Lane. Ejb 3 in Action. Manning Publications Co., Greenwich, CT, USA, 2007. [18] D. L. Parnas. On the criteria to be used in decomposing systems into modules. pages 139–150. Yourdon Press, Upper Saddle River, NJ, USA, 1979. [19] Dhanji R. Prasanna. Dependency Injection. Manning Publications Co., Greenwich, CT, USA, 2008. [20] Ken Schwaber. Agile Project Management With Scrum. Microsoft Press, Redmond, WA, USA, 2004. [21] Robb Shecter. Design by interface. Dr. Dobb’s Journal, pages 96–101, february 1999. [22] Maximilian Storzer and Stefan Hanenberg. A classification of pointcut language constructs. In Software-engineering Properties of Languages and Aspect Technologies, March 2005. [23] Craig Walls. Spring in Action. Manning Publications Co., Greenwich, CT, USA, 2007. [24] Jeremy Weiskotten. Dependency injection: Designing loosely coupled and testable objects. Dr. Dobb’s Journal, pages 10, 12, 14–15, may 2006.

46

Appendix A

References This appendix contains references to tools, components, libraries and other technologies used in the development of the application.

A.1

Tools

• Canoo WebTest - automated web testing http://webtest.canoo.com/ • DbUnit - a JUnit extension used for database population when testing http://www.dbunit.org/ • GlassFish - Java application server http://glassfish.dev.java.net/ • Hudson - continuous integration server https://hudson.dev.java.net/ • Jetty - lightweight servlet container for testing http://jetty.mortbay.com/ • JUnit - Unit testing http://junit.org/ • Maven 2 - build management http://maven.apache.org/ • NetBeans - Integrated Development Environment (IDE) http://www.netbeans.org/ • Subversion - version control system http://subversion.tigris.org/ 47

APPENDIX A. REFERENCES

A.2

Components

• Apache Commons - library of many useful and reusable components http://commons.apache.org/ • Display Tag - Java tag library with support functionality for displaying tables http://displaytag.sourceforge.net/ • Hibernate - Java object-relational mapping (ORM) library http://www.hibernate.org/ • Log4j - used for logging http://logging.apache.org/log4j/ • Spring Framework - open source Java application framework developed by SpringSource, led by Rod Johnson http://www.springframework.org/

48

TRITA-CSC-E 2009: 077 ISRN-KTH/CSC/E--09/077--SE ISSN-1653-5715

www.kth.se