In Project 2, we will develop an online markdown editor using Apache Tomcat. Through this process we will learn how to develop a Web application using a “traditional” stack, in particular, MySQL and Apache Tomcat (Java servlet).
In later projects, we will use more “modern” Web development stack, such as MongoDB, Node.js, and Angular, but we want to make sure that everyone has an experience with developing Web applications on a more “traditional” stack. This will help students understand and appreciate why modern frameworks are structured as they are. All development for Project 2 will be done on the “tomcat” container that you created from “junghoo/cs144-tomcat” image in Project 1. Make sure that the container still starts and works fine by issuing the following command in a terminal window:
$ docker start -i tomcat
Before starting to code, first learn how we develop a Web application using Apache Tomcat by going over our tutorial:
The tutorial teaches how you can develop a Web application using Java Servlet and JSP, and how you can package the set of files needed for your app into a single Web Application Archive (WAR) file for easy deployment on Tomcat.
In order to access MySQL data from a Java program, you will need to use JDBC (Java DataBase Connectivity) API. Go over the following tutorial to learn how to use JDBC to access MySQL in a Java program.
Now that you have learned how to develop applications on Tomcat and MySQL, it is time to get your real work done. In Part C, you will need to implement an “online markdown editor” that allows users to save and edit blog posts written in markdown.
Markdown is a lightweight and intuitive markup language originally proposed by John Gruber in 2004 to help people “write using an easy-to-read, easy-to-write plain text format, and optionally convert it to structurally valid XHTML (or HTML)”. Due to its simplicity, markdown has become the de-facto standard for writing readme files on many web sites (e.g., GitHub and BitBucket). In fact, this page was originally written in markdown and has been converted to HTML using Pandoc! Since we will use an “off-the-shelf” markdown parsing library in Java for this project, you don’t have to learn markdown syntax, but in case you are interested, here is a 2-minute introduction to markdown syntax. For your reference, John Gruber’s description of markdown syntax is available here (highly recommended if you want to learn more precise syntax) and the result of recent standardization efforts, referred to as CommonMark, is available here (extremely detailed and precise, yet boring to read).
Your Web site should allows users to create a new post (written in markdown), preview the post (rendered in HTML), and manage existing posts. These tasks are supported through three main pages on your Web site: edit, preview, and list pages
The “edit page” allows editing the title and body of a post.
The page should contain two input boxes
<input>
box of text
type. This text input element must have the ID attribute with value “title”.<textarea>
. This textarea element must have the ID attribute with value “body”.The page should contain four buttons: save, close, preview, and delete. Once pressed,
The “preview page” shows the HTML rendering of a post written in markdown. The page must have a “close” button. Once pressed, close button goes back to the “edit page” of the post.
The “list page” shows the list of all blog posts saved by the user. The posts in the list should be sorted by their “postid” (a unique integer assigned to a post) in the ascending order. Each item in the list must show:
title, creation, and modification dates of the post, and
two buttons: open and delete. Once pressed,
The list page should also contain a “new post” button to allow users to create a new post. Once pressed, the button should lead to the “edit page” for a new post.
To help you better understand the function of the three pages on your site and their interaction model, we made an example demo site available at http://oak.cs.ucla.edu/editor/post?action=list&username=junghoo. To encourage you to create your own CSS styles for your site, we removed all CSS styling tags in the demo, so it looks ugly. But you can still get an idea of how the three pages should interact and function. For security concerns, we do not perform any update operation to our database. Even if you “delete” a post, for instance, it won’t disappear from the list.
Note: The three “pages” that we described above may not necessarily be three separate HTML pages. Depending on your implementation, they may be implemented as one or more HTML pages, Java servlets and/or JSP pages. You may consider “three pages” as three classes of page layouts that your application should use.
In developing your application, you should follow the following REST API. First, you should make your application available at the following path:
/editor/post?action=type&username=name&postid=num&title=title&body=body
The path is case sensitive. You must use exactly the same case as above both for the the initial path prefix and the parameters. The parameter “action” specifies one of five “actions” that your site has to take: open, save, delete, preview, and list. The other four parameters, username, postid, title, and body are (optional) parameters that the actions may need. Here is detailed descriptions of the five actions – their functions and required parameters:
NOTE:
HttpServletResponse
API to learn how to set the HTTP status code in Servlet.NOTE: During our grading, we won’t be doing extensive check for security related issues, but there are two things that we may test:
Your implementation should do the basic input validation. For example, we do expect that students make sure that postid is integer.
Your implementation should use preparedStatement
in your SQL queries as a basic protection against SQL injection attack. As we mentioned in our JDBC tutorial, we expect students to follow this industry-wise convention, which cannot be overemphasized as a minimal security measure.
Whatever the user input, it will be important that your implementation won’t flake out (like throwing Java exception messages as the output). All requests need to be handled gracefully, however malformed they may be.
Note on Multi-Threading:
Tomcat creates one thread per each request and concurrent data manipulation can be taken care of by MySQL. This makes server-side programming easy since the programmer doesn’t have to worry about concurrency. But in order to do things this way, you need to keep two things in mind:
doGet()
and close it before you return from the handler.doGet()
must not reference a class instance variable. It must reference only local variables.Note on Connection Pooling:
You may think that creating one DB connection per request will lead to too much overhead and wonder whether there is any way to “share” the same DB connection among multiple requests. The proper way to do it is to use connection pooling, but this is beyond the scope of this project. Connection pooling is something that must be used in a production environment, but you MUST NOT use connection pooling in this project since it won’t work on the grader’s machine.
All blog posts that are saved by the users must be stored as a row a MySQL table with the following schema:
Posts(
username VARCHAR(40),
postid INTEGER,
title VARCHAR(100),
body TEXT,
modified TIMESTAMP DEFAULT '2000-01-01 00:00:00',
created TIMESTAMP DEFAULT '2000-01-01 00:00:00',
PRIMARY KEY(username, postid)
)
Hopefully, the meaning of each column would be clear from our earlier discussion. MySQL schema definition is case sensitive, so be careful with the case of your schema definition.
Create the table within “CS144” database as the user “cs144”. The first blog post of every user must start with postid=1 and their subsequent posts must be assigned to a linearly increasing postid. Therefore, blog posts by different users may share the same postid, but postid is unique within a single user. Populate the Posts
table with the following six initial tuples:
('user_XYRSAF', 1, 'Post 1 by XYRSAF', 'Article1 written by XYRSAF', '2020-01-03 10:00:00', '2020-01-03 09:10:00')
('user_XYRSAF', 2, 'Post 2 by XYRSAF', 'Article2 written by XYRSAF', '2020-01-04 10:00:00', '2020-01-04 09:10:00')
('user_XYRSAF', 3, 'Post 3 by XYRSAF', 'Article3 written by XYRSAF', '2020-01-05 10:00:00', '2020-01-05 09:10:00')
('user_XYRSAF', 4, 'Post 4 by XYRSAF', 'Article4 written by XYRSAF', '2020-01-06 10:00:00', '2020-01-06 09:10:00')
('user_ACHERW', 1, 'Post 1 by ACHERW', 'Article1 written by ACHERW', '2020-01-03 10:00:00', '2020-01-03 09:10:00')
('user_ACHERW', 2, 'Post 2 by ACHERW', 'Article2 written by ACHERW', '2020-01-04 10:00:00', '2020-01-04 09:10:00')
In case you find it helpful, here is a text file that contains the six tuples in a format that you can simply “cut-and-paste” into a SQL INSERT statement.
In implementing your app, you may need to create tables other than the “Posts” table to store other information needed for your app persistently.
To help you get started, we provide skeleton sample code for this project in project2.zip, which has the following set of files:
project2.zip
|
+- build.gradle
+- create.sql
+- deploy.sh
+- src
+- main
+- java
| +- Editor.java
|
+- webapp
+- edit.jsp
+- WEB-INF
+- web.xml
Editor
class, such as connecting to, retrieving from, and updating MySQL database server, and dispatching the user’s request to an appropriate JSP page./editor
to the Java class Editor
.As you learned from our tutorial on Tomcat, a Web application needs many files that should be carefully prepared and packaged. To help you focus on coding, not packaging, we included a “gradle build script” that takes care of compilation and packaging. To see how it works, unzip project2.zip into a folder, cd
to the folder and run the following command:
$ gradle assemble
This will compile all your Java source files in the src/main/java/
directory (currently, just “Editor.java”), package them together with everything under src/main/webapp
directory according to the spec, and create a war file at build/libs/editor.war
. Once created, you can copy the war file to $CATALINA_BASE/webapps
:
$ cp build/libs/editor.war $CATALINA_BASE/webapps
to deploy it to the Tomcat server. Copy the file, wait for a few seconds (so that Tomcat detects the new war file and sets it up), and access http://localhost:8888/editor/post?action=list&username=test from a browser on your host. You will get a (non-functional) version of the “edit page” that has been generated from the edit.jsp
page.
As long as you can use our provided gradle build script to compile your code and create the war file, it is okay for you to know nothing about how the gradle build script works. But if you want to learn more, read one of many online tutorials on Gradle.
Your job now is to write Java code in Editor.java
and (optionally) add new JSP, HTML, CSS pages to src/main/webapp
in order to implement Online Markdown Editor. If you are not sure how the code in Editor.java
works or how to write a JSP page, go over our Tomcat Application Development Tutorial. If you are not sure how to access a MySQL database with a Java code, go over our JDBC tutorial.
Note: You may find it helpful that what is written to System.out/err
in Tomcat servlet is (1) printed on console and (2) written to the catalina.out
log filelocated in $CATALINA_BASE/logs/
. To access the directory, you will need the root permission. You can start a bash shell under the root user by the “sudo bash” command, for example.
To implement the “preview page”, you need to “compile” a markdown-formatted input into an HTML-formatted output. For this, you can use the commonmark Java library. We have already downloaded the library and made it available in our “tomcat” container. Go over the the README.me file on the library page to learn how to use the library. Roughly, the following Java code
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
...
Parser parser = Parser.builder().build();
HtmlRenderer renderer = HtmlRenderer.builder().build();
String markdown = "This is *CS144*";
String html = renderer.render(parser.parse(markdown));
/* html has the string "<p>This is <em>CS144</em></p>\n" */
will “compile” the markdown text This is *CS144*
into HTML text <p>This is <em>CS144</em></p>
.
deploy.sh
In project2.zip, we also included our “deployment bash script”, deployment.sh, that automates the entire application deployment process. In particular, it (1) creates tables for our application in MySQL (2) build the “editor.war” file and (3) deploy it to the Tomcat server. Once you finish developing your code, make sure that simply running
$ ./deploy.sh
properly sets up your application. This script will be used to deploy your application during our grading, so it is extremely important to make sure that this script works without any error.
Your project must be submitted electronically before the deadline through our CCLE Course Website. Navigate to Sections on left of the page, and click on the Project 2 submission section. If you submit multiple times, we will grade only the latest submission.
The zip file that you submit must be named project2.zip
, created using a zip compression utility (like using “zip -r project2.zip *
” command in the container). You should submit this single file project2.zip that has the following packaging structure.
project2.zip
|
+- README.txt (optional)
+- TEAM.txt
+- deploy.sh
+- build.gradle
+- create.sql
+- edit.png, preview.png, list.png
+- src
+- main
+- java
| +- Editor.java (and other java files that you wrote)
|
+- webapp
+- edit.jsp (and other jsp, css, and html files that you added)
+- WEB-INF
+- web.xml
We have already explained what most of the above files are earlier, but your submission should include the following additional files:
create.sql
file inserts the six initial tuples into Posts
table.Please ensure that your submission is packaged correctly with all required files. Make sure that each file is correctly named (including its case) and project2.zip contains all files directly, not within a subdirectory. Please do not include complied Java class files or WAR file.
Perhaps, the most important requirement of your submission is that the grader should be able to deploy a fully functional version of your Web site just by running ./deploy.sh
after unzipping your submission. You may get as small as zero points if the grader encounters an error due to incorrect packaging, missing files, and failure to follow our exact spec.
Grading is a difficult and time-consuming process, and file naming and packaging convention is very important to test your submission without any error. In order to help you ensure the correct packaging of your submission, we have made a “testing script” p2_test available. In essence, the testing script unzips your submission to a temporary directory and deploy your files to Tomcat to test whether they are likely to run OK on the grader’s machine. Download the testing script and execute it inside the tomcat container:
$ ./p2_test project2.zip
(if your project2.zip file is not located in the current directory, you need to add the path to the zip file before project2.zip. You may need to use chmod +x p2_test
if there is a permission error.)
You MUST test your submission using the script before your final submission to minimize the chance of an unexpected error during grading. Again, significant points may be deducted if the grader encounters an error during grading. When everything runs properly, you will see an output similar to the following from the testing script:
$ ./p2_test project2.zip
dropping all tables in CS144 database
building and deploying your application...
:clean UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.86 secs
:compileJava
:processResources NO-SOURCE
:classes
:war
:assemble
BUILD SUCCESSFUL
Total time: 0.894 secs
Finished deploying your app. Sleeping for 30 seconds for Tomcat to pick it up...
Requesting http://localhost:8080/editor/post?action=open&username=user_XYRSAF&postid=1
This is the response from Tomcat. Make sure that it is what you expect
<!DOCTYPE html>
<html>
...
</html>
Requesting http://localhost:8080/editor/post?action=open&username=user_XYRSAF&postid=10
This is the response from Tomcat. Make sure that it is what you expect
<!DOCTYPE html>
<html>
...
</html>
SUCCESS!