|
|
Exam App Technology and Architecture
The technologies we integrated with Terracotta to implement the Examinator reference application are listed and explained below. Then we show how these technologies were integrated to create Examinator.
Technology Stack
We used the following technology stack to build the reference implementation. The selection process was carried out carefully and with deliberation. We were looking for a stack that was being used by a significant portion of our customer base and also in the industry. We sought to build with unintrusive frameworks that wouldn't hide how Terracotta is used. Lastly, we wanted frameworks we actually find tolerable to use 
This does not mean that frameworks we didn't choose aren't good. In many cases (for example, web framework), we had to choose among many viable alternatives. We may, in the future, expand the reference implementation to include some of these alternatives. Of course, if you want to contribute an augmentation of the reference implementation using your favorite technology, we welcome your initiative.
| Web Frameworks and Tools |
| Spring Web Flow |
To keep track of a user's progress through an exam, we use Spring Web Flow. Since the exam is essentially a workflow through a linear series of questions, this seemed like the best choice. The current state of a user's flow is stored in the HTTP Session and clustered with Terracotta so that, if a web container instance fails, the user can be seamlessly routed by the load balancer to another web container instance without losing any state (i.e., the answers the user has already submitted). |
| Spring MVC |
To handle the administrative functions of creating an exam, user account management, and test initiation and reporting, we used the Spring MVC component of the Spring Framework. |
| jQuery |
The administrative functionality of creating and editing exams is implemented in large part with JavaScript, using jQuery. |
| SOJO |
For parsing JSON within Java, we use SOJO. |
| SiteMesh |
For page layout, we used SiteMesh. |
| Platform and Tools |
| Spring |
For dependency injection and a host of other handy tools, we used Spring. |
| Spring Security |
To manage user login and access control, we used Spring Security (formerly Acegi Security System for Spring). |
| FreeMarker |
To manage the layout of email messages, we used the FreeMarker template engine. |
| Domain Persistence |
| JPA |
For the high-level domain persistence layer, we used the Java Persistence API (JPA). |
| Hibernate |
For the actual object-relational persistence, we used Hibernate. |
| Apache DBCP |
For database connection pooling, we used Apache DBCP. |
| MySQL |
For relational database persistence, we used MySQL. |
| Web Infrastructure |
| Apache mod_proxy |
For load balancing during testing, we used Apache mod_proxy, although this will not be the final load balancing technology used. |
| Tomcat |
For deployment and performance testing we used the Apache Tomcat web container. |
| Jetty |
For development we used Webtide's Jetty web server. |
| Development, Testing, and Build Tools |
| Maven |
To manage dependency acquisition and build and test task management, we used Maven. |
| Eclipse (+WTP) |
We used the Eclipse IDE and the Web Tools Platform (WTP) to actually write the code. |
| m2eclipse Maven Eclipse Plugin |
The m2eclipse plugin allows Eclipse to build Examinator using only the pom.xml file to resolve build dependencies. |
| Cargo |
To launch the web container(s) for scripted/automated testing, we used Cargo. |
| JUnit |
For unit testing, we used JUnit. |
| HttpUnit |
For functional testing of the web application, we used HttpUnit. |
| Selenium |
We also used Selenium and Selenium RC for functional testing of the web application. |
| Crosscheck |
For unit testing JavaScript, we used Crosscheck. |

Architecture
The Examinator application follows a very traditional web application architecture that divides the logic into several distinct layers. The web pages are built in a traditional MVC (Model View Controller) structure. Below that is a Service layer, where the bulk of the business logic is implemented. At the bottom are the domain objects for the application and a DAO (Data Access Object) layer for moving domain objects to and from the database.
Domain
The domain objects represent the core entities in the application:
| User |
A user existing in the system. |
| UserRole |
A role a User has in the system, such as Administrator. |
| Exam |
The exam definition, which has exactly one main section. |
| Section |
An exam section, which may contain either other Sections or Questions. |
| Question |
An exam Question, which contains at least two Choices. |
| Choice |
A single answer to a Question. A Choice can be either the wrong or right (correct) answer to its containing question. Exactly one and only one choice per question is designated as correct. |
| ExamFacade |
A helper object that can assemble all the objects in an Exam and provide additional functionality, such as iteration through the exam questions |
| ExamSession |
The in-process state of taking an exam (exam start time, chosen answers, questions marked for review) |
| ExamResult |
The result of completing an exam - a score, pass/fail information, and statistics about the exam |
The relationships and cardinality of the domain objects are illustrated as follows:
All of these objects are stored in the database with the exception of the ExamFacade, which is used only as a short-term in-memory helper class and ExamSession, which is clustered in Terracotta.
Data Access Objects (DAO)
The Data Access Object (DAO) pattern is used to encapsulate how data moves between the database and the domain objects. Several technologies are used to bridge the divide between the database and the application code.
| Database (MySQL) |
Stores the data in relational tables |
| ORM (JPA and Hibernate) |
Defines and implements the mapping of domain objects to database tables |
| DAO Templates (Spring) |
Framework for easily building DAO objects |
JPA specifies a standard set of annotations and configuration to map domain objects to database tables. Hibernate implements the JPA specification and provides additional functionality for providing object-relational mapping. The Examinator DAOs are implemented using the Spring JPA Template which takes care of getting and using the JPA EntityManager and simplifies JPA exception handling.
The DAO pattern encapsulates common operations around a core domain class such as:
- save a new instance
- find all records of a given type
- find records by some criteria
- update an existing instance
- delete all records of a given type
- delete records by some criteria
The class hierarchy for the DAO layer is as follows:
DAO class relationships: org.terracotta.reference.exam.dao
The domain classes themselves (org.terracotta.reference.exam.domain) are simple beans with fields and accompanying accessor/mutator methods that are annotated to describe their relationship to columns in the database. A domain class is annotated with @Entity to flag it as a class whose instances also live in the database. The further class annotation @Table binds the class to a particular table in the database.
@Entity
@Table(name = "EXAM")
public class Exam implements Serializable {
}
org.terracotta.reference.exam.domain.Exam annotated class declaration.
Each field that corresponds to a column in the given table is annotated with the appropriate column name:
@Id
@GeneratedValue
@Column(name = "ID")
private Long id;
@Column(name = "NAME", unique = true)
private String name;
org.terracotta.reference.exam.domain.Exam annotated field declarations.
The GenericDAO class encapsulates the SQL predicates for the generic load, save, and delete operations on entity objects. It uses an injected Spring JpaTemplate instance to invoke those operations on the underlying data service. The Spring JPA Template infrastructure handles the actual plumbing of those operations to the appropriate calls through Hibernate to the database, leaving the DAO code free of such implementation details. For example, the method in GenericDAO to load a set of domain objects by a particular attribute is as follows:
public <T> List<T> findByAttribute(final Class<T> entityClass, final String attributeName, final String attributeValue) {
return getJpaTemplate().find(
"select e from " + entityClass.getSimpleName() + " e where e." + attributeName
+ " = ?1", attributeValue);
}
org.terracotta.reference.exam.dao.impl.GenericDAO
The concrete DAO implementations have an instance of GenericDAO that they use to delegate interaction with the JpaTemplate. For example, the ExamDaoImpl class delegates to GenericDAO.findByAttribute in order to load an Exam domain object by name:
public Exam findByName(final String examName) {
final List<Exam> list = generic.findByAttribute(Exam.class, "name", examName);
if (null == list || 0 == list.size()) return null;
assert list.size() == 1;
return list.get(0);
}
org.terracotta.reference.exam.dao.impl.ExamDaoImpl
Service
The Service layer classes encapsulate the business logic provided by the bottom half of the application stack. This is also how transactionality to the database is specified. For example, the DaoExamService exposes methods for manipulating Exam objects.
@Service
public class DaoExamService implements ExamService {
@Autowired
private ExamDao examDao;
@Autowired
private ExamResultDao examResultDao;
@Autowired
private UserService userService;
@Transactional(readOnly = false)
public Exam updateExam(final Exam exam){
logger.debug("Updating exam: " + exam);
examDao.saveOrUpdate(exam);
return exam;
}
@Transactional(readOnly = true)
public List<ExamResult> getAllExamResultsForUser(final String userName) {
final User user = userService.findByUserName(userName);
assert user != null;
return examResultDao.getAllExamResultsForUser(user);
}
}
org.terracotta.reference.exam.service.impl.DaoExamService
The references to other services and to required DAO objects are autowired in by Spring. As you can see from this code snippet, the updateExam method is declared via annotations as a transactional write method and the relevant Exam object is delegated to the ExamDAO object for updating. In getAllExamResultsForUser, the actions of retrieving a User domain object via the UserService and then loading that user's exam results is declared an atomic transactional read operation.
MVC
The top three layers implement an MVC architecture where the view is implemented in JSP (Java Server Pages) fed by Spring MVC models produced by Spring MVC controllers. The portion of the Examinator application concerned with taking exams is implemented using Spring Web Flow.
Spring Web Flow provides support for modeling and implementing a "flow". The flow consists of a set of states and events that can occur in each state to move to a new state. Generally, the movement from one state to another causes a new view page to be rendered. Web Flows are defined using a simple XML-based declarative configuration and controller engine. This support makes the building of "conversational" portions of the web application very easy.
Pages are rendered using JSP pages in conjunction with SiteMesh templates. SiteMesh is a page layout framework that lets you factor out common parts of your web pages and put them in a template. For example, headers, footers, and other common elements can be placed in the template and automatically standardized across all web pages.
Terracotta
Terracotta is used to cluster application and user session state as well as cached Hibernate entities. The next section shows how Terracotta is used by describing in detail the functions of the Examinator application.
|