Before You Begin
Purpose
This tutorial shows you to create and deploy a simple standalone web application using Servlets, Java Server Pages (JSP), Bootstrap and Tomcat embedded so that you can deploy it to Oracle Application Container Cloud Service.
Time to Complete
60 minutes
Background
A web application is a software program that runs on a web server. Usually a web application is packaged in a WAR file and deployed in a server container like Tomcat, JBOSS, or GlassFish. However, embedded servers provide an interesting alternative. Instead of deploying your app in a server, the server is embedded in your application. The result is a standalone application packaged in a JAR file that can be executed from a command line.
A web application is usually divided in two parts: front-end and back-end. The back end (server side), of the application defines how the site works and is responsible for things like calculations, business logic, database interactions, and performance. The front-end is the part of the application that web users interact with. It's usually a mixture of HTML, CSS, and JavaScript, all controlled by the browser.
Most modern web projects use some kind of standard framework to make web development faster, easier, and more standards compliant. Currently, Bootstrap is the most popular and widely used framework for creating web applications.
Bootstrap is a front-end framework that contains a collection of tools designed to allow developers to quickly create web applications. It includes design templates based on HTML and CSS for common user interface components like forms, typography, buttons, navigations, tables, tabs, dropdowns, alerts, modals, accordion, carousel and many other optional JavaScript extensions.
Bootstrap is simple to use and it's compatible with modern browsers such as Mozilla Firefox, Google Chrome, Safari, Internet Explorer, and Opera.
Scenario
In this tutorial you build a simple Java web application of employees that implements the CRUD (Create, Read, Update, and Delete) operations. The application uses Bootstrap to add User Interface styles, Tomcat as embedded server and Maven to compile and package the dependencies needed to run a standalone web application.
Context
This tutorial covers developing and packaging your application. To learn how to deploy your application see: Deploying an Application to Oracle Application Container Cloud Service.
What Do You Need?
- Java Development Kit 8 (JDK 8)
- Maven 3.0+
- Bootstrap v3.3.4
- A text editor or integrated development environment (IDE).
- employees-app.zip (This is the Maven project with source code. Download it to view the completed project.)
Creating a Maven Project
In this section, you create a basic Maven project from an archetype.
The first time that you execute any Maven command, Maven downloads all the plugins and related dependencies to fulfill the command. From a clean installation of Maven, this can take quite a while based on the internet connection. If you create another project, Maven won't need to download anything new and can execute the command much faster.
-
Open a command-line window (or terminal in Linux).
-
Go to the directory where you want to store the project.
-
Execute the Maven command to generate the project:
mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DarchetypeGroupId=org.apache.maven.archetypes -DinteractiveMode=false -DgroupId=com.example.employees -DartifactId=employees-app -DarchetypeVersion=1.0
Ignore any warnings that you see during project creation. They won't affect the correct execution of the command.
This command creates a standard Maven structure.
When execution completes, you should see the new employees-app
project directory in your current location.
You can change the groupId
and artifactId
of this command for your particular project.
You can open the project in NetBeans (File -> Open project) or in Eclipse (File -> Import -> Maven-> Existing Maven Project) but you must configure Maven in the IDE. Make sure that the IDE supports java 8. Version 7.4 (or later) of NetBeans includes support for JDK 8.
Configuring the pom.xml File
In this section, you configure the dependencies required to run Tomcat in an embedded mode and the plugins to compile and package the project in a single uber JAR.
Java web applications require a web container, such as Tomcat, to run on. Installing and configuring a web container on each development machine can be time-consuming. Furthermore, other developers must manage the dependencies manually if they want to run the web application.
Maven has a Tomcat plugin that allows you to run an embedded Tomcat instance without installing a local Tomcat server.
Adding Maven Dependencies
-
Open the
pom.xml
located in the project root directory. -
Add the
<properties>
tags with the tomcat version property before the<dependencies>
tags.<properties> <tomcat.version>7.0.57</tomcat.version> </properties>
-
Add the following dependencies in the
<dependencies>
tags:<dependencies> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-logging-juli</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jasper</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jasper-el</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jsp-api</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies>
JSTL (JSP Standard Tag Library) is a JSP-based standard tag library that offers a collection of tags to control the flow in the JSP page, date/number formatting and internationalization facilities, and several utility EL functions.
Adding Build Plugins
In this section, you add code to configure the following plugins:
- The Maven Compiler Plugin (
maven-compiler-plugin
) is used to compile the sources of the project. - The Maven Assembly Plugin (
maven-assembly-plugin
) allows users to aggregate the project output along with its dependencies, modules, site documentation, and other files into a single distributable archive.
Build plugins are executed during the build.
You configure the plugins in the <build>
element of the POM.
-
To add the
<build>
element containing the<plugins>
section, copy and paste the following code into thepom.xml
file:<build> <finalName>employees-app</finalName> <resources> <resource> <directory>src/main/webapp</directory> <targetPath>META-INF/resources</targetPath> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <inherited>true</inherited> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <finalName>${project.build.finalName}-${project.version}</finalName> <archive> <manifest> <mainClass>com.example.employees.Main</mainClass> </manifest> </archive> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Usually Maven ignores all but .java
files in src/main/java
when tasks
such as compile and package are executed. The <resource>
tag is used to include the jsp
, css
,
js
and fonts files in the JAR like
resources.
Creating an Employee Class Model
To model the Employee representation, create a plain old Java class with fields, constructors, and accessors for the fields.
- Create the package
java.com.example.employees
in themain
directory. - Create the
Employee.java
class by copying and pasting the following code into the package: -
Create the
EmployeeList
class in the same package by copying and pasting the following code:/* Copyright © 2015 Oracle and/or its affiliates. All rights reserved. */ package com.example.employees; import java.util.ArrayList; import java.util.List; public class EmployeeList { private static final List<Employee> employeeList = new ArrayList(); private EmployeeList(){ } static{ employeeList.add(new Employee("John","Smith","12-12-1980","Manager","Sales","john.smith@abc.com")); employeeList.add(new Employee("Laura","Adams","02-11-1979","Manager","IT","laura.adams@abc.com")); employeeList.add(new Employee("Peter","Williams","22-10-1966","Coordinator","HR","peter.williams@abc.com")); employeeList.add(new Employee("Joana","Sanders","11-11-1976","Manager","Marketing","joana.sanders@abc.com")); employeeList.add(new Employee("John","Drake","18-08-1988","Coordinator","Finance","john.drake@abc.com")); employeeList.add(new Employee("Samuel","Williams","22-03-1985","Coordinator","Finance","samuel.williams@abc.com")); } public static List <Employee> getInstance(){ return employeeList; } }
Because this is a simple example, this tutorial doesn't use a database to store
Employee
objects. Instead, you use anArrayList
, and store theEmployee
objects in memory.This class contains two constructors. The first constructor receives all the fields including the
id
. The second constructor creates a new ID for the employee. You use this to create a new employee.The preceding code added six employees to the list so that you have some data to retrieve for testing purposes.
/* Copyright © 2015 Oracle and/or its affiliates. All rights reserved. */
package com.example.employees;
import java.util.concurrent.atomic.AtomicLong;
public class Employee {
private long id;
private String name;
private String lastName;
private String birthDate;
private String role;
private String department;
private String email;
private static final AtomicLong counter = new AtomicLong(100);
public Employee(String name, String lastName, String birthDate, String role, String department, String email, long id) {
this.name = name;
this.lastName = lastName;
this.birthDate = birthDate;
this.role = role;
this.department = department;
this.email = email;
this.id = id;
}
public Employee(String name, String lastName, String birthDate, String role, String department, String email) {
this.name = name;
this.lastName = lastName;
this.birthDate = birthDate;
this.role = role;
this.department = department;
this.email = email;
this.id = counter.incrementAndGet();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String birthDate) {
this.birthDate = birthDate;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Employee{" + "id=" + id + ", name=" + name +
", lastName=" + lastName + ", birthDate=" + birthDate +
", role=" + role + ", department=" + department +
", email=" + email + '}';
}
}
Building a Search Page
In this section, you create a user interface to search for employees that match name or last name.
-
Create the
EmployeeService.java
class in thecom.example.employees
package by copying and pasting the following code:/* Copyright © 2015 Oracle and/or its affiliates. All rights reserved. */ package com.example.employees; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; public class EmployeeService { List<Employee> employeeList = EmployeeList.getInstance(); public List<Employee> getAllEmployees() { return employeeList; } public List<Employee> searchEmployeesByName(String name) { Comparator<Employee> groupByComparator = Comparator.comparing(Employee::getName) .thenComparing(Employee::getLastName); List<Employee> result = employeeList .stream() .filter(e -> e.getName().equalsIgnoreCase(name) || e.getLastName().equalsIgnoreCase(name)) .sorted(groupByComparator) .collect(Collectors.toList()); return result; } public Employee getEmployee(long id) throws Exception { Optional<Employee> match = employeeList.stream() .filter(e -> e.getId() == id) .findFirst(); if (match.isPresent()) { return match.get(); } else { throw new Exception("The Employee id " + id + " not found"); } } }
This class contains an instance of
EmployeeList
and three methods to retrieve the employees using Lambda expressions. -
Create the
EmployeeServlet.java
class in thejava.com.example.employees
package:@WebServlet( name = "EmployeeServlet", urlPatterns = {"/employee"} ) public class EmployeeServlet extends HttpServlet { EmployeeService employeeService = new EmployeeService();
The
@WebServlet
annotation is used to declare a servlet instead of using XML in the web deployment descriptor (web.xml
) . The annotation is processed by the container at deployment time. -
Override the
doGet
method:@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("searchAction"); if (action!=null){ switch (action) { case "searchById": searchEmployeeById(req, resp); break; case "searchByName": searchEmployeeByName(req, resp); break; } }else{ List<Employee> result = employeeService.getAllEmployees(); forwardListEmployees(req, resp, result); } }
The
doGet
method is used to process the HTTP GET request. In this it's implemented to process three kinds of searches: by Id, by Name, and by default the complete list of employees.The
searchAction
parameter is used to determine what kind of search it will be executed. -
Create the
searchEmployeeById
,searchEmployeeByName,
andforwardListEmployees
methods:private void searchEmployeeById(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { long idEmployee = Integer.valueOf(req.getParameter("idEmployee")); Employee employee = null; try { employee = employeeService.getEmployee(idEmployee); } catch (Exception ex) { Logger.getLogger(EmployeeServlet.class.getName()).log(Level.SEVERE, null, ex); } req.setAttribute("employee", employee); req.setAttribute("action", "edit"); String nextJSP = "/jsp/new-employee.jsp"; RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(nextJSP); dispatcher.forward(req, resp); } private void searchEmployeeByName(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String employeeName = req.getParameter("employeeName"); List<Employee> result = employeeService.searchEmployeesByName(employeeName); forwardListEmployees(req, resp, result); } private void forwardListEmployees(HttpServletRequest req, HttpServletResponse resp, List employeeList) throws ServletException, IOException { String nextJSP = "/jsp/list-employees.jsp"; RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(nextJSP); req.setAttribute("employeeList", employeeList); dispatcher.forward(req, resp); }
The
forwardListEmployees
method is a helper method to dispatch the employee list tolist-employees.jsp
. It's in a separate method because the code is reused in other operations.
Creating the User Interface
In this section, you build a user interface to search employees by name or last name and show the results that match in a table. You use Bootstrap to add some styles like the light-blue button and shaded rows you see in the following figure.
There are two ways to start using Bootstrap on your website:
- Download Bootstrap from
getbootstrap.com
. - Include Bootstrap from a Content Delivery Network (CDN).
In this tutorial you download the precompiled and
minified versions of Bootstrap CSS, JavaScript and
fonts from getbootstrap.com
and add
them to your project.
-
Copy the directories
js
,fonts,
andcss
from the Bootstrapzip
to thewebapp
folder. -
Delete the non-minified versions of the CSS and JS files.
The minified files are compressed versions of the CSS and JavaScript so they download faster.
-
Create the
list-employees.jsp
file in a new folder namedjsp
under thewebapp
folder. -
Add the
bootstrap.min.js
andbootstrap.min.css
files to thejsp
folder:<link rel="stylesheet" href="../css/bootstrap.min.css"> <script src="../js/bootstrap.min.js"></script>
-
Add
taglib
for JSTL support:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
-
Add the Search form:
list-employees.jsp - Fragment
<div class="container"> <h2>Employees</h2> <!--Search Form --> <form action="/employee" method="get" id="seachEmployeeForm" role="form" > <input type="hidden" id="searchAction" name="searchAction" value="searchByName"/> <div class="form-group col-xs-5"> <input type="text" name="employeeName" id="employeeName" class="form-control" required="true" placeholder="Type the Name or Last Name of the employee"/> </div> <button type="submit" class="btn btn-info"> <span class="glyphicon glyphicon-search"></span> Search </button> <br></br> <br></br> </form> </div>
Individual form controls (
input
,textarea
andselect
) automatically receive some global styling with Bootstrap.Bootstrap requires a containing element to wrap site contents. The
container
class is used for a responsive fixed-width container.The
required
attribute introduced in HTML5 specifies that the user must enter a value before submitting the form. Bootstrap validates the required fields before submitting the form and shows a predefined message if a field is missing. You can override this behavior by coding your specific rules and messages using jQuery.The
placeholder
is another attribute introduced in HTML5 that describes the expected value of theinput
field.The class
.btn
provides the default look of a gray button with rounded corners. Thebtn-info
class gives the button the light-blue color that's typically used for informational alert messages.Bootstrap includes 200 glyphs from the Glyphicons Halflings set. Glyphicons Halflings aren't usually available for free, although some basic icons are available for Bootstrap free of cost. For example, the
btn-info
class provides the magnifying glass icon. -
Add the table to display the result of the search. Make sure the new form is enclosed in
<div class="container">
tags.list-employees.jsp - Fragment
<form action="/employee" method="post" id="employeeForm" role="form" > <c:choose> <c:when test="${not empty employeeList}"> <table class="table table-striped"> <thead> <tr> <td>#</td> <td>Name</td> <td>Last name</td> <td>Birth date</td> <td>Role</td> <td>Department</td> <td>E-mail</td> </tr> </thead> <c:forEach var="employee" items="${employeeList}"> <c:set var="classSuccess" value=""/> <c:if test ="${idEmployee == employee.id}"> <c:set var="classSuccess" value="info"/> </c:if> <tr class="${classSuccess}"> <td>${employee.id}</td> <td>${employee.name}</td> <td>${employee.lastName}</td> <td>${employee.birthDate}</td> <td>${employee.role}</td> <td>${employee.department}</td> <td>${employee.email}</td> </tr> </c:forEach> </table> </c:when> <c:otherwise> <br> </br> <div class="alert alert-info"> No people found matching your search criteria </div> </c:otherwise> </c:choose> </form>
employeeList
is the attribute that contains the employee search results. This fragment of code iterates theemployeeList
to display the employee information. If no results match the search, then an alert message is displayed.The
.table-striped
class adds the zebra-striping style to any table row.Bootstrap has four contextual classes (
.alert-success, .alert-info, .alert-warning, .alert-danger
) that provide a way to style messages to the user.<div class="alert alert-success">Success! Well done it's submitted.</div> <div class="alert alert-info">Info! take this info.</div> <div class="alert alert-warning">Warning ! Don't submit this.</div> <div class="alert alert-danger">Error ! Change a few things.</div>
-
Review the code. Your code should look like the following:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <link rel="stylesheet" href="../css/bootstrap.min.css"/> <script src="../js/bootstrap.min.js"></script> </head> <body> <div class="container"> <h2>Employees</h2> <!--Search Form --> <form action="/employee" method="get" id="seachEmployeeForm" role="form"> <input type="hidden" id="searchAction" name="searchAction" value="searchByName"> <div class="form-group col-xs-5"> <input type="text" name="employeeName" id="employeeName" class="form-control" required="true" placeholder="Type the Name or Last Name of the employee"/> </div> <button type="submit" class="btn btn-info"> <span class="glyphicon glyphicon-search"></span> Search </button> <br></br> <br></br> </form> <!--Employees List--> <form action="/employee" method="post" id="employeeForm" role="form" > <c:choose> <c:when test="${not empty employeeList}"> <table class="table table-striped"> <thead> <tr> <td>#</td> <td>Name</td> <td>Last name</td> <td>Birth date</td> <td>Role</td> <td>Department</td> <td>E-mail</td> </tr> </thead> <c:forEach var="employee" items="${employeeList}"> <c:set var="classSucess" value=""/> <c:if test ="${idEmployee == employee.id}"> <c:set var="classSucess" value="info"/> </c:if> <tr class="${classSucess}"> <td>${employee.id}</td> <td>${employee.name}</td> <td>${employee.lastName}</td> <td>${employee.birthDate}</td> <td>${employee.role}</td> <td>${employee.department}</td> <td>${employee.email}</td> </tr> </c:forEach> </table> </c:when> <c:otherwise> <br> <div class="alert alert-info"> No people found matching your search criteria </div> </c:otherwise> </c:choose> </form> </div> </body> </html>
-
Modify the
index.jsp
file to forward the content to the servlet:<jsp:forward page="/employee" />
Creating a Launcher Class
In this section, you create a class with a main
method that creates an instance of the Tomcat
server. This method is executed when you run the JAR
file.
Create the Main
class in the com.example.employees
package:
/* Copyright © 2015 Oracle and/or its affiliates. All rights reserved. */
package com.example.employees;
import java.util.Optional;
import org.apache.catalina.startup.Tomcat;
public class Main {
public static final Optional<String> port = Optional.ofNullable(System.getenv("PORT"));
public static void main(String[] args) throws Exception {
String contextPath = "/";
String appBase = ".";
Tomcat tomcat = new Tomcat();
tomcat.setPort(Integer.valueOf(port.orElse("8080") ));
tomcat.getHost().setAppBase(appBase);
tomcat.addWebapp(contextPath, appBase);
tomcat.start();
tomcat.getServer().await();
}
}
With the org.apache.catalina.startup.Tomcat
class, you can configure the port using the setPort
method, and the context path using the addWebapp
method.
Running the Web Application
Compiling and Running the Web Application
- Open a command-line window or terminal
- Go to the root directory of the project (
employees-app
), wherepom.xml
resides. - Compile the project:
mvn clean compile.
- Package the application:
mvn package
. - Look in the target directory. You should see
a file with the following or a similar name:
employees-app-1.0-SNAPSHOT-jar-with-dependencies.jar
- Change to the target directory.
-
Execute the JAR:
java -jar employees-app-1.0-SNAPSHOT-jar-with-dependencies.jar
.The output indicates that
employees-app
is running onhttp://localhost:8080
Testing the Web Application
-
Open the Firefox or Chrome web browser.
-
Enter the following URL into the browser address window:
http://localhost:8080
-
Enter a Name or Last Name (for example, Williams) and click Search.
-
Search an employee name that doesn't exist in the preload list (for example, Ivan ).
This is how the Bootstrap's class
alert-info
looks for alert messages.
Adding Editing Operations
In this section, three new operations: Create, Remove, and Edit an employee.
Editing the Service
Open the EmployeeService.java
class
and add the new three operations:
EmployeeService.java - Fragment
public long addEmployee(Employee employee) {
employeeList.add(employee);
return employee.getId();
}
public boolean updateEmployee(Employee customer) {
int matchIdx = 0;
Optional<Employee> match = employeeList.stream()
.filter(c -> c.getId() == customer.getId())
.findFirst();
if (match.isPresent()) {
matchIdx = employeeList.indexOf(match.get());
employeeList.set(matchIdx, customer);
return true;
} else {
return false;
}
}
public boolean deleteEmployee(long id) {
Predicate<Employee> employee = e -> e.getId() == id;
if (employeeList.removeIf(employee)) {
return true;
} else {
return false;
}
}
Editing the Servlet
-
Open the
EmployeeServlet.java
class and override thedoPost
method:EmployeeServlet.java - Fragment
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action"); switch (action) { case "add": addEmployeeAction(req, resp); break; case "edit": editEmployeeAction(req, resp); break; case "remove": removeEmployeeByName(req, resp); break; } }
-
Add the
addEmployeeAction
,editEmployeeAction
, andremoveEmployeeByName
methods:private void addEmployeeAction(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); String lastName = req.getParameter("lastName"); String birthday = req.getParameter("birthDate"); String role = req.getParameter("role"); String department = req.getParameter("department"); String email = req.getParameter("email"); Employee employee = new Employee(name, lastName, birthday, role, department, email); long idEmployee = employeeService.addEmployee(employee); List<Employee> employeeList = employeeService.getAllEmployees(); req.setAttribute("idEmployee", idEmployee); String message = "The new employee has been successfully created."; req.setAttribute("message", message); forwardListEmployees(req, resp, employeeList); } private void editEmployeeAction(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String name = req.getParameter("name"); String lastName = req.getParameter("lastName"); String birthday = req.getParameter("birthDate"); String role = req.getParameter("role"); String department = req.getParameter("department"); String email = req.getParameter("email"); long idEmployee = Integer.valueOf(req.getParameter("idEmployee")); Employee employee = new Employee(name, lastName, birthday, role, department, email, idEmployee); employee.setId(idEmployee); boolean success = employeeService.updateEmployee(employee); String message = null; if (success) { message = "The employee has been successfully updated."; } List<Employee> employeeList = employeeService.getAllEmployees(); req.setAttribute("idEmployee", idEmployee); req.setAttribute("message", message); forwardListEmployees(req, resp, employeeList); } private void removeEmployeeByName(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { long idEmployee = Integer.valueOf(req.getParameter("idEmployee")); boolean confirm = employeeService.deleteEmployee(idEmployee); if (confirm){ String message = "The employee has been successfully removed."; req.setAttribute("message", message); } List<Employee> employeeList = employeeService.getAllEmployees(); forwardListEmployees(req, resp, employeeList); }
Editing the View
- Open the
list-employee.jsp
file. -
Add two hidden input elemments in the
employeeForm
form:<input type="hidden" id="idEmployee" name="idEmployee"> <input type="hidden" id="action" name="action">
These inputs are used to pass the values of
idEmployee
and type of action to the servlet and execute the corresponding action. -
Add a
div
above theemployeeForm
form to display an alert message:<c:if test="${not empty message}"> <div class="alert alert-success"> ${message} </div> </c:if>
-
Add a link to the first column (#):
<td> <a href="/employee?idEmployee=${employee.id}&searchAction=searchById">${employee.id}</a> </td>
Use this link to display the values for a particular employee on an edit page.
-
Add a new column next to the E-mail column for the remove button:
<td> <a href="#" id="remove" onclick="document.getElementById('idEmployee').value='${employee.id}'; document.getElementById('action').value='remove'; document.getElementById('employeeForm').submit();"> <span class="glyphicon glyphicon-trash"/> </a> </td>
The
glyphicon-trash
provides a trash can icon. -
Add a new form below the
employeeForm
form with a button for creating a new employee. Make sure that the new form is enclosed in<div class="container">
tags.<form action ="jsp/new-employee.jsp"> <br></br> <button type="submit" class="btn btn-primary btn-md">New employee</button> </form>
-
Review the code. Your code should look like the following:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <link rel="stylesheet" href="../css/bootstrap.min.css"> <script src="../js/bootstrap.min.js"></script> </head> <body> <div class="container"> <h2>Employees</h2> <!--Search Form --> <form action="/employee" method="get" id="seachEmployeeForm" role="form"> <input type="hidden" id="searchAction" name="searchAction" value="searchByName"> <div class="form-group col-xs-5"> <input type="text" name="employeeName" id="employeeName" class="form-control" required="true" placeholder="Type the Name or Last Name of the employee"/> </div> <button type="submit" class="btn btn-info"> <span class="glyphicon glyphicon-search"></span> Search </button> <br></br> <br></br> </form> <!--Employees List--> <c:if test="${not empty message}"> <div class="alert alert-success"> ${message} </div> </c:if> <form action="/employee" method="post" id="employeeForm" role="form" > <input type="hidden" id="idEmployee" name="idEmployee"> <input type="hidden" id="action" name="action"> <c:choose> <c:when test="${not empty employeeList}"> <table class="table table-striped"> <thead> <tr> <td>#</td> <td>Name</td> <td>Last name</td> <td>Birth date</td> <td>Role</td> <td>Department</td> <td>E-mail</td> <td></td> </tr> </thead> <c:forEach var="employee" items="${employeeList}"> <c:set var="classSucess" value=""/> <c:if test ="${idEmployee == employee.id}"> <c:set var="classSucess" value="info"/> </c:if> <tr class="${classSucess}"> <td> <a href="/employee?idEmployee=${employee.id}&searchAction=searchById">${employee.id}</a> </td> <td>${employee.name}</td> <td>${employee.lastName}</td> <td>${employee.birthDate}</td> <td>${employee.role}</td> <td>${employee.department}</td> <td>${employee.email}</td> <td><a href="#" id="remove" onclick="document.getElementById('action').value = 'remove';document.getElementById('idEmployee').value = '${employee.id}'; document.getElementById('employeeForm').submit();"> <span class="glyphicon glyphicon-trash"/> </a> </td> </tr> </c:forEach> </table> </c:when> <c:otherwise> <br> <div class="alert alert-info"> No people found matching your search criteria </div> </c:otherwise> </c:choose> </form> <form action ="jsp/new-employee.jsp"> <br></br> <button type="submit" class="btn btn-primary btn-md">New employee</button> </form> </div> </body> </html>
-
Create the
new-employee.jsp
file in thejsp
directory:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <head> <link rel="stylesheet" href="../css/bootstrap.min.css"/> <script src="../js/bootstrap.min.js"></script> </head> <body> <div class="container"> <form action="/employee" method="post" role="form" data-toggle="validator" > <c:if test ="${empty action}"> <c:set var="action" value="add"/> </c:if> <input type="hidden" id="action" name="action" value="${action}"/> <input type="hidden" id="idEmployee" name="idEmployee" value="${employee.id}"/> <h2>Employee</h2> <div class="form-group col-xs-4"> <label for="name" class="control-label col-xs-4">Name:</label> <input type="text" name="name" id="name" class="form-control" value="${employee.name}" required="true"/> <label for="lastName" class="control-label col-xs-4">Last name:</label> <input type="text" name="lastName" id="lastName" class="form-control" value="${employee.lastName}" required="true"/> <label for="birthdate" class="control-label col-xs-4">Birth date</label> <input type="text" pattern="^\d{2}-\d{2}-\d{4}$" name="birthDate" id="birthdate" class="form-control" value="${employee.birthDate}" maxlength="10" placeholder="dd-MM-yyyy" required="true"/> <label for="role" class="control-label col-xs-4">Role:</label> <input type="text" name="role" id="role" class="form-control" value="${employee.role}" required="true"/> <label for="department" class="control-label col-xs-4">Department:</label> <input type="text" name="department" id="department" class="form-control" value="${employee.department}" required="true"/> <label for="department" class="control-label col-xs-4">E-mail:</label> <input type="text" name="email" id="email" class="form-control" value="${employee.email}" placeholder="smith@aol.com" required="true"/> <br></br> <button type="submit" class="btn btn-primary btn-md">Accept</button> </div> </form> </div> </body> </html>
This
jsp
contains a form to add a new employee or edit an existing one. All fields are marked as required.The
data-toggle="validator"
attribute automatically enables form validation for the form element.The
pattern
attribute introduced in HTML5 specifies a JavaScript regular expression for the field’s value to be checked against. This form is used in thebirthdate
field to validate the format (dd-MM-yyyy).
Testing the New Operations: Create, Edit, and Remove
Running the Web Application
- Stop the application (if it'd running).
- Reexecute the steps in Compiling and Running the Web Application.
-
Enter the following URL into the browser address window:
http://localhost:8080
Testing the New Employee Feature
-
Click New employee
-
Fill in all the fields.
Note: Try submitting the form leaving one inputbox empty, and see what happens.
-
Click Accept.
Testing the Remove Feature
-
Click the trash can icon to delete the employee (for example the 102 employee).
Testing the Edit Feature
-
Click the ID number.
-
Edit some fields.
-
Click Accept.
As you can see, Bootstrap is a powerful front-end framework. This tutorial provides the basics to get you started and show you how easy it is to use.
Developing an Application for Cloud Deployment
To properly package, deploy, and execute your application in Oracle Application Container Cloud Service you must understand the concepts in this section.
Container Considerations
Oracle Application Container Cloud Service applications are run in Docker containers. Docker is an open-source software container project. Think of a container as very lightweight virtual machine. Your application runs in its own isolated execution space that has its own memory, file system, and network access. Access to these operating system resources without the cost of having to implement an entire virtual operating system.
When you deploy your application to Oracle Application Container Cloud Service, a container is dynamically generated for your application. The implication of this is that all the typical configuration information, like host name and port number, are also dynamically generated. Therefore, any application that runs in this environment must be read key configuration information from the environment before the application starts.
Additionally, containers for your application can be dynamically allocated to scale up or down as is required by the application load. This dynamic scaling feature is made possible by the load balancers provided by Oracle Application Container Cloud Service. The balancer routes traffic to the application instances thus "balancing" the load.
Given the setup of the service, this has implications for your applications.
Application Considerations for Running in a Container
For an application to run properly on Oracle Application Container Cloud Service, there are a few requirements that must be met.
- The applications must be stateless: Because the service can run multiple instances of the same application, applications can't share state. For example, items added to a shopping cart application on one instance would not be available to other instances of the application. Because application load is balanced between instances, there is no guarantee that the next request will be handled by the same instance as the last request. Thus trying to store application state could result in possible data loss. Therefore, state information should be stored outside your application in a database. Modern databases are designed to handle connections from multiple concurrent clients.
- The applications must communicate through a network port: The only way to communicate to a running application is through its assigned network port.
- Application must configurable at runtime: Applications must be able to start and execute based on values set by the container. Values are passed to an application through environment variables.
- All dependencies must be included with the application: If your application requires a library to execute, that library must be included with the application when it is deployed. This can be accomplished two ways.
- Create an Uber JAR (Java SE only): When you create your application, you include all dependent librariesin the JAR file with the application.
- Archived Libaries: Include dependent
libraries in the application archive you create.
For Java, you can separate the JAR files by
using the
CLASSPATH
command-line option in the application launch command.
For more information, See the Developer Guide.
Configuring Your Application to Run in the Cloud
For you application run on Oracle Application
Container Cloud Service, you must let the container
assign the host name and port number for your
application.The container sets these values using
environment variables. Your application must read
the HOSTNAME
and PORT
environment variables, then start your application
using those values.
In Java 8, the new Optional
class
makes it fairly easy to read these values, check for
null, and then return the values from the
environment variables or use default values.
What follows is a new version of the Main
class that checks for the PORT
and HOSTNAME
environment variables at runtime. If the values are
set, then they are used to launch the server. If the
values aren't set, default values are used instead.
This allows your application to run stand alone
locally or in the cloud using Oracle Application
Container Cloud Service.
Main.java
package com.example.rest;
import java.util.Optional;
public class Main{
public static final String BASE_URI;
public static final String protocol;
public static final String path;
public static final Optional<String> host;
public static final Optional<String> port;
static{
protocol = "http://";
host = Optional.ofNullable(System.getenv("HOSTNAME"));
port = Optional.ofNullable(System.getenv("PORT"));
path = "myapp";
BASE_URI = protocol + host.orElse("localhost") + ":" + port.orElse("8080") + "/" + path + "/";
}
public static void main(String[] args) throws IOException {
// Start your app here ...
}
}
A couple of key points.
- At the top of the class notice two
Optional<String>
variables are declared. Theport
andhost
fields will store the result of your environment variable lookup. - In the
static
initialization block, theSystem.getenv
method is used to get the environment variable. Notice that theOptional.ofNullable
method is called. This method will return either the value stored in the environment variable or an emptyOptional
if no value is returned. - The
BASE_URI
field now uses theOptional
variables to create the URI. TheorElse
method sets a default value if theOptional
is empty.
With these improvements, you can set the host name or port using environment variables. The additional code is clear and concise.
Download Suggested Solution
If you want to see the complete set of project files, download the source files and Maven project from the following link:
Creating a Cloud-Ready Package Using manifest.json File
The final step for packaging your application, is
to combine your application archive with the manifest.json
file.
The manifest.json File
The manifest.json
file provides
metadata about your Java application. This
information includes the version of Java and the
command used to execute the application. It may
also include notes about your application along
with release information.
{
"runtime": {
"majorVersion": "7"
},
"command": "sh target\/bin\/start",
"release": {
"build": "150520.1154",
"commit": "d8c2596364d9584050461",
"version": "15.1.0"
},
"notes": "notes related to release"
}
JSON Fields Defined
runtime:
majorVersion:
The version of the runtime e.g. for Java it would be 7 or 8.command:
The command to execute after deploying the application.release
build:
A user-specified text value identifying this build.commit:
A user-specified text value related to this commit.version:
The version text string maintained by user.
Here is the manifest.json
file
used in the sample application.
{
"runtime":{
"majorVersion": "8"
},
"command": "java -jar employees-app-1.0-SNAPSHOT-jar-with-dependencies.jar",
"release": {
"build": "20150813Build",
"commit": "commitString",
"version": "20150813Version"
},
"notes": "Web application for testing"
}
Note: From the simplicity of the command used to launch the application, you can see that this is an uber JAR deployment. This application uses Java 8. If your application needs any special command-line switches to run, this is where you would specify here.
Creating the Final Archive
As a final step, you create an archive
containing the application archive and manifest.json
files. Both files are stored at the root level
of the archive file.
For example, create an archive using zip
.
zip employees-sample-app.zip
manifest.json
employees-app-1.0-SNAPSHOT-jar-with-dependencies.jar
Here is an example using tar
.
tar cvfz employees-sample-app.tgz
manifest.json
employees-app-1.0-SNAPSHOT-jar-with-dependencies.jar
When you have created the final archive, your application is ready for deployment.
Packaging an Application Archive Without an Uber JAR File
As an alternative to using an uber JAR, an
application can be started by specifying libraries
on the java
command line or by using a
bash
shell script. Oracle Application
Container Cloud Service uses a Linux container for
the execution of applications, so most of the rules
that apply to running a command in Linux will apply.
Assumptions
The two examples that follow are based on the following assumptions:
- The home directory for the application is
/u01/app
. - The application execution code is stored in a
JAR named
app.jar
. - All required Java libraries are stored in the
lib
directory. Thelib
directory is included in the final archive as a subdirectory from the archive root directory. - The
lib
directory contains the following JAR libraries:web.jar, rest.jar, media.jar
.
Executing the Application with Java and Classpath
Given the preceding assumptions, and assuming the lib
directory is included as described, the following
command could be used to launch the application:
java -cp '/u01/app/lib/*' -jar app.jar
When, the application is deployed, the libraries in
the lib
directory are copied under the
application's home directory. The above command
executes the java
command and loads
all of the libraries in the /u01/app/lib
directory. This should provide the JVM with all the
necessary classes to run the application.
Executing the Application with a Shell Script
As an alternative you could execute your application using a shell script. The command line for execution would be something like this:
bash -l ./applicationScript.sh
The CLASSPATH
environment variable
can also be used to set the path. In this example,
the script could contain the follow two lines.
export
CLASSPATH=/u01/app/lib/web.jar;/u01/app/lib/rest.jar;/u01/app/lib/media.jar;
java -jar app.jar
Cloud Deployment Summary
To properly package your application, you must go through the following steps:
- Create an application that is configurable at runtime.
- Package your application as an uber jar or as separate library jars.
- Create an application archive that contains your
application and a
manifest.json
in a single file. Themanifest.json
file must be stored in the root directory of your archive. - Deploy your application to Oracle Application Container Cloud Service.
Note: The manifest.json
file
is optional, as the information contained in the
file can also be configured through the user
interface.
Deploying Your Applicaiton to Oracle Application Container
Now that you have created an application and packaged it for deployment to Oracle Application Container Cloud Service, you are ready to deploy it. The steps for deploying your application are described in a separate tutorial: Deploying an Application to Oracle Application Container Cloud Service.
Want to Learn More?
-
Bootstrap website getbootstrap.com
-
Maven plugin for Apache Tomcat website tomcat.apache.org/maven-plugin.html
-
Deploying an Application to Oracle Application Container Cloud Service OBE
Credits
- Curriculum Developer: Luz Elena Peralta Ayala
- QA: Veerabhadra Rao Putrevu