Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package net.coderodde.roddenotes.config;
- /**
- * This class contains the general application parameters.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class Config {
- /**
- * This class contains form parameter names.
- */
- public static final class PARAMETERS {
- /**
- * The name of the parameter holding the document ID.
- */
- public static final String DOCUMENT_ID = "documentId";
- /**
- * The name of the parameter holding the edit token.
- */
- public static final String EDIT_TOKEN = "editToken";
- /**
- * The name of the parameter holding the document text.
- */
- public static final String DOCUMENT_TEXT = "documentText";
- }
- /**
- * This class contains attribute names.
- */
- public static final class ATTRIBUTES {
- /**
- * Used for communicating the actual document text.
- */
- public static final String DOCUMENT_TEXT = "documentText";
- /**
- * Used for communicating publish links.
- */
- public static final String PUBLISH_LINK = "publishLink";
- /**
- * Used for communicating document IDs.
- */
- public static final String DOCUMENT_ID = "documentId";
- /**
- * Used for communicating edit tokens.
- */
- public static final String EDIT_TOKEN = "editToken";
- }
- public static final class PAGES {
- /**
- * The name of the JSP file for viewing a (non-editable) document.
- */
- public static final String VIEW_PAGE = "view.jsp";
- /**
- * The name of the JSP file for editing documents.
- */
- public static final String EDITOR_PAGE = "edit.jsp";
- /**
- * The name of the JSP file rendered upon missing document.
- */
- public static final String NO_DOCUMENT_PAGE = "viewDocNotFound.jsp";
- /**
- * The name of the HTML file rendered upon requesting a view without the
- * document ID parameter.
- */
- public static final String NO_ID_VIEW_PAGE = "viewIdNotGiven.html";
- /**
- * The name of the HTML file rendered whenever receiving a request where
- * the document ID and the edit token do not match.
- */
- public static final String DONT_HACK_US_PAGE = "dontHackUs.html";
- }
- /**
- * This class contains all the error messages in the application.
- */
- public static final class ERROR_MESSAGES {
- /**
- * The name of the CSS class used for rendering error messages.
- */
- public static final String ERROR_MESSAGE_CSS_CLASS = "error";
- /**
- * The opening span tag.
- */
- public static final String SPAN_BEGIN =
- "<span class='" +
- ERROR_MESSAGE_CSS_CLASS +
- "'>";
- /**
- * The closing span tag.
- */
- public static final String SPAN_END = "</span>";
- /**
- * The text rendered whenever the document with given ID does not exist.
- */
- public static final String NO_SUCH_DOCUMENT_TEXT_FORMAT =
- SPAN_BEGIN + "(Document with ID %s does not exist.)" + SPAN_END;
- /**
- * The text rendered whenever the user accesses the view page without a any
- * document ID.
- */
- public static final String NO_GIVEN_ID_TEXT =
- SPAN_BEGIN +
- "(Cannot find a document without an ID.)" +
- SPAN_END;
- }
- public static final class STATUS_MESSAGES {
- public static final String SUCCESS = "success";
- public static final String FAILURE = "failure";
- }
- }
- package net.coderodde.roddenotes.controllers;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.sql.SQLException;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import net.coderodde.roddenotes.config.Config;
- import net.coderodde.roddenotes.model.Document;
- import net.coderodde.roddenotes.sql.support.MySQLDataAccessObject;
- /**
- * This servlet is responsible for deleting documents from the database.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 16, 2017)
- */
- @WebServlet(name = "DeleteDocumentServlet", urlPatterns = {"/deleteDocument"})
- public class DeleteDocumentServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- try (PrintWriter out = response.getWriter()) {
- String documentId =
- request.getParameter(Config.PARAMETERS.DOCUMENT_ID);
- String editToken =
- request.getParameter(Config.PARAMETERS.EDIT_TOKEN);
- if (documentId == null || editToken == null) {
- out.print(Config.STATUS_MESSAGES.FAILURE);
- return;
- }
- Document document = null;
- try {
- document = MySQLDataAccessObject.INSTANCE.getDocument(documentId);
- } catch (SQLException ex) {
- out.print(Config.STATUS_MESSAGES.FAILURE);
- throw new RuntimeException(ex);
- }
- if (document == null) {
- out.print(Config.STATUS_MESSAGES.FAILURE);
- return;
- }
- if (!document.getEditToken().equals(editToken)) {
- out.print(Config.STATUS_MESSAGES.FAILURE);
- return;
- }
- try {
- MySQLDataAccessObject.INSTANCE.deleteDocument(documentId);
- out.print(Config.STATUS_MESSAGES.SUCCESS);
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- }
- }
- }
- package net.coderodde.roddenotes.controllers;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.sql.SQLException;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import net.coderodde.roddenotes.config.Config;
- import net.coderodde.roddenotes.config.Config.ATTRIBUTES;
- import net.coderodde.roddenotes.model.Document;
- import net.coderodde.roddenotes.sql.support.MySQLDataAccessObject;
- import static net.coderodde.roddenotes.util.MiscellaneousUtilities.getServerURL;
- /**
- * This servlet handles the edit requests. If the servlet receives parameters
- * defining the document ID and its edit token, and both are valid, this servlet
- * prepares an editor view for the document. Otherwise, a new document is
- * created and is presented to the user.
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- @WebServlet(name = "EditServlet", urlPatterns = {"/edit"})
- public class EditServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- String id = request.getParameter(Config.PARAMETERS.DOCUMENT_ID);
- String editToken = request.getParameter(Config.PARAMETERS.EDIT_TOKEN);
- if (id == null || editToken == null) {
- serveFreshEmptyDocument(request, response);
- return;
- }
- Document document = null;
- try {
- document = MySQLDataAccessObject.INSTANCE.getDocument(id);
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- if (document == null) {
- serveFreshEmptyDocument(request, response);
- return;
- }
- if (!document.getEditToken().equals(editToken)) {
- serveFreshEmptyDocument(request, response);
- return;
- }
- request.setAttribute(ATTRIBUTES.DOCUMENT_ID, document.getId());
- request.setAttribute(ATTRIBUTES.EDIT_TOKEN, document.getEditToken());
- request.setAttribute(ATTRIBUTES.DOCUMENT_TEXT, document.getText());
- request.setAttribute(ATTRIBUTES.PUBLISH_LINK,
- getPublishLink(request, document));
- request.getRequestDispatcher(Config.PAGES.EDITOR_PAGE)
- .forward(request, response);
- }
- @Override
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- try (PrintWriter out = response.getWriter()) {
- out.println("This servlet does not serve POST requests.");
- }
- }
- private String getPublishLink(HttpServletRequest request,
- Document document) {
- return new StringBuilder(getServerURL(request))
- .append("/view?")
- .append(Config.PARAMETERS.DOCUMENT_ID)
- .append('=')
- .append(document.getId())
- .toString();
- }
- private void serveFreshEmptyDocument(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- Document document = null;
- try {
- document = MySQLDataAccessObject.INSTANCE.createNewDocument();
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- String path = getPath(request, document);
- request.getRequestDispatcher(path).forward(request, response);
- }
- private String getPath(HttpServletRequest request, Document document) {
- return new StringBuilder().append(request.getPathInfo())
- .append('?')
- .append(Config.PARAMETERS.DOCUMENT_ID)
- .append('=')
- .append(document.getId())
- .append('&')
- .append(Config.PARAMETERS.EDIT_TOKEN)
- .append('=')
- .append(document.getEditToken())
- .toString();
- }
- }
- package net.coderodde.roddenotes.controllers;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.sql.SQLException;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import net.coderodde.roddenotes.config.Config;
- import net.coderodde.roddenotes.model.Document;
- import net.coderodde.roddenotes.sql.support.MySQLDataAccessObject;
- /**
- * This servlet listens to the root resource of this application, creates a new
- * document and redirects to the document's edit view.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- @WebServlet(name = "HomeServlet", urlPatterns = {""})
- public class HomeServlet extends HttpServlet {
- private static final String EDIT_SERVLET_NAME = "edit";
- @Override
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- Document document = null;
- try {
- document = MySQLDataAccessObject.INSTANCE.createNewDocument();
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- if (document == null) {
- throw new NullPointerException("Creating a document failed.");
- }
- response.sendRedirect(getEditPageAddress(document));
- }
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- try (PrintWriter out = response.getWriter()) {
- out.print("Please access this resource via GET method.");
- }
- }
- /**
- * Constructs the address for the edit page.
- *
- * @param document the document to prepare for editing.
- * @return a page address relative to the web application.
- */
- private String getEditPageAddress(Document document) {
- return new StringBuilder()
- .append(EDIT_SERVLET_NAME)
- .append('?')
- .append(Config.PARAMETERS.DOCUMENT_ID)
- .append('=')
- .append(document.getId())
- .append('&')
- .append(Config.PARAMETERS.EDIT_TOKEN)
- .append('=')
- .append(document.getEditToken())
- .toString();
- }
- }
- package net.coderodde.roddenotes.controllers;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.sql.SQLException;
- import java.util.regex.Pattern;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import net.coderodde.roddenotes.config.Config;
- import net.coderodde.roddenotes.config.Config.PAGES;
- import net.coderodde.roddenotes.config.Config.PARAMETERS;
- import net.coderodde.roddenotes.model.Document;
- import net.coderodde.roddenotes.sql.support.MySQLDataAccessObject;
- /**
- * This servlet is responsible for updating an existing document. If the
- * incoming document is not yet in the database, it is put there.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- @WebServlet(name = "UpdateDocumentServlet", urlPatterns = {"/update"})
- public class UpdateDocumentServlet extends HttpServlet {
- /**
- * The regular expression for the begin script tag.
- */
- private static final String SCRIPT_TAG_BEGIN_REGEX = "<\s*script\s*>";
- /**
- * The regular expression for the end script tag.
- */
- private static final String SCRIPT_TAG_END_REGEX = "<\s*/\s*script\s*>";
- private static final String SCRIPT_TAG_BEGIN_SUBSTITUTE = "<script>";
- private static final String SCRIPT_TAG_END_SUBSTITUTE = "</script>";
- @Override
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- try (PrintWriter out = response.getWriter()) {
- out.println("This servlet does not work via GET method.");
- }
- }
- @Override
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- String documentId = request.getParameter(PARAMETERS.DOCUMENT_ID);
- String documentText = request.getParameter(PARAMETERS.DOCUMENT_TEXT);
- String editToken = request.getParameter(PARAMETERS.EDIT_TOKEN);
- documentText = sanitizeText(documentText);
- try (PrintWriter out = response.getWriter()) {
- if (documentId == null
- || editToken == null
- || documentText == null) {
- out.print(Config.STATUS_MESSAGES.FAILURE);
- return;
- }
- Document document = new Document();
- document.setId(documentId);
- document.setEditToken(editToken);
- document.setText(documentText);
- try {
- boolean validUpdate =
- MySQLDataAccessObject.INSTANCE.updateDocument(document);
- if (!validUpdate) {
- request.getRequestDispatcher(PAGES.DONT_HACK_US_PAGE)
- .forward(request, response);
- return;
- }
- out.print(Config.STATUS_MESSAGES.SUCCESS);
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- }
- }
- private String sanitizeText(String text) {
- Pattern patternBeginTag =
- Pattern.compile(SCRIPT_TAG_BEGIN_REGEX,
- Pattern.CASE_INSENSITIVE);
- Pattern patternEndTag =
- Pattern.compile(SCRIPT_TAG_END_REGEX,
- Pattern.CASE_INSENSITIVE);
- text = patternBeginTag.matcher(text)
- .replaceAll(SCRIPT_TAG_BEGIN_SUBSTITUTE);
- return patternEndTag.matcher(text)
- .replaceAll(SCRIPT_TAG_END_SUBSTITUTE);
- }
- }
- package net.coderodde.roddenotes.controllers;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.sql.SQLException;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import net.coderodde.roddenotes.config.Config;
- import net.coderodde.roddenotes.model.Document;
- import net.coderodde.roddenotes.sql.support.MySQLDataAccessObject;
- /**
- * This servlet is responsible for showing the documents via their ID.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- @WebServlet(name = "ViewServlet", urlPatterns = {"/view"})
- public class ViewServlet extends HttpServlet {
- @Override
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- String documentId =
- request.getParameter(Config.PARAMETERS.DOCUMENT_ID);
- if (documentId == null) {
- request.getRequestDispatcher(Config.PAGES.NO_ID_VIEW_PAGE)
- .forward(request, response);
- return;
- }
- Document document = null;
- try {
- document = MySQLDataAccessObject
- .INSTANCE
- .getDocument(documentId);
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- if (document == null) {
- request.setAttribute(Config.ATTRIBUTES.DOCUMENT_ID,
- documentId);
- request.getRequestDispatcher(Config.PAGES.NO_DOCUMENT_PAGE)
- .forward(request, response);
- return;
- }
- request.setAttribute(Config.ATTRIBUTES.DOCUMENT_TEXT,
- document.getText());
- request.getRequestDispatcher(Config.PAGES.VIEW_PAGE)
- .forward(request, response);
- }
- @Override
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
- try (PrintWriter out = response.getWriter()) {
- out.println("This servlet is not accessible via POST method.");
- }
- }
- }
- package net.coderodde.roddenotes.model;
- /**
- * This class implements a document.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class Document {
- private String id;
- private String editToken;
- private String text;
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public String getEditToken() {
- return editToken;
- }
- public void setEditToken(String editToken) {
- this.editToken = editToken;
- }
- public String getText() {
- return text;
- }
- public void setText(String text) {
- this.text = text;
- }
- }
- package net.coderodde.roddenotes.sql;
- import java.sql.SQLException;
- import net.coderodde.roddenotes.model.Document;
- /**
- * This interface lists all the methods a data access object should implement in
- * order to integrate with rodde-notes.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public interface DataAccessObject {
- /**
- * Creates a new document with unique ID, random edit token and empty text.
- *
- * @return a document.
- * @throws SQLException if the SQL layer fails.
- */
- public Document createNewDocument() throws SQLException;
- /**
- * Deletes a document with given ID.
- *
- * @param id the ID of the document to delete.
- * @throws SQLException if the SQL layer fails.
- */
- public void deleteDocument(String id) throws SQLException;
- /**
- * Reads a document with given ID.
- *
- * @param id the ID of the desired document.
- * @return the document with the given ID or {@code null} if there is no
- * such.
- * @throws SQLException if the SQL layer fails.
- */
- public Document getDocument(String id) throws SQLException;
- /**
- * Saves the document. If the document is not yet present in the database,
- * it is inserted. Otherwise, its state is updated.
- *
- * @param document the document to update.
- * @return {@code true} if the ID and editToken match each other.
- * {@code false} otherwise.
- * @throws SQLException if the SQL layer fails.
- */
- public boolean updateDocument(Document document) throws SQLException;
- /**
- * Makes sure all the tables are created in the database.
- *
- * @throws SQLException if the SQL layer fails.
- */
- public void initializeDatabaseTables() throws SQLException;
- }
- package net.coderodde.roddenotes.sql.support;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import net.coderodde.roddenotes.model.Document;
- import net.coderodde.roddenotes.sql.DataAccessObject;
- import net.coderodde.roddenotes.sql.support.MySQLDefinitions.DELETE;
- import net.coderodde.roddenotes.sql.support.MySQLDefinitions.DOCUMENT_TABLE;
- import net.coderodde.roddenotes.sql.support.MySQLDefinitions.SELECT;
- import net.coderodde.roddenotes.sql.support.MySQLDefinitions.UPDATE;
- import net.coderodde.roddenotes.util.RandomUtilities;
- /**
- * This class implements a data access object over a MySQL database.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class MySQLDataAccessObject implements DataAccessObject {
- /**
- * The name of the environment variable holding the connection URI for the
- * MySQL database server.
- */
- private static final String DATABASE_URI_ENVIRONMENT_VARIABLE =
- "RODDE_NOTES_DB_URI";
- /**
- * The only instance of this class.
- */
- public static final MySQLDataAccessObject INSTANCE =
- new MySQLDataAccessObject();
- static {
- try {
- // Attempts to load the driver.
- Class.forName("com.mysql.jdbc.Driver");
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException("Cannot load the JDBC driver for MySQL.",
- ex);
- }
- }
- private MySQLDataAccessObject() {}
- /**
- * {@inheritDoc }
- */
- @Override
- public Document createNewDocument() throws SQLException {
- String id = null;
- String editToken = RandomUtilities.generateRandomEditToken();
- try (Connection connection = getConnection()) {
- connection.setAutoCommit(false);
- try (PreparedStatement statement =
- connection.prepareStatement(MySQLDefinitions.SELECT.DOCUMENT.VIA_DOCUMENT_ID)) {
- while (true) {
- id = RandomUtilities.generateRandomDocumentId();
- statement.setString(1, id);
- try (ResultSet resultSet = statement.executeQuery()) {
- if (!resultSet.next()) {
- break;
- }
- }
- }
- }
- try (PreparedStatement statement =
- connection.prepareStatement(
- MySQLDefinitions.INSERT.DOCUMENT)) {
- statement.setString(1, id);
- statement.setString(2, editToken);
- statement.setString(3, ""); // Note the empty text.
- statement.executeUpdate();
- }
- connection.commit();
- }
- Document document = new Document();
- document.setId(id);
- document.setEditToken(editToken);
- document.setText(""); // Note the empty text.
- return document;
- }
- /**
- * {@inheritDoc }
- */
- @Override
- public Document getDocument(String id) throws SQLException {
- try (Connection connection = getConnection()) {
- try (PreparedStatement statement =
- connection.prepareStatement(MySQLDefinitions
- .SELECT
- .DOCUMENT
- .VIA_DOCUMENT_ID)) {
- statement.setString(1, id);
- try (ResultSet resultSet = statement.executeQuery()) {
- if (!resultSet.next()) {
- return null;
- }
- Document document = new Document();
- document.setId(
- resultSet.getString(DOCUMENT_TABLE.ID_COLUMN.NAME));
- document.setEditToken(
- resultSet.getString(
- DOCUMENT_TABLE.EDIT_TOKEN_COLUMN.NAME));
- document.setText(
- resultSet.getString(
- DOCUMENT_TABLE.TEXT_COLUMN.NAME));
- return document;
- }
- }
- }
- }
- private Connection getConnection() throws SQLException {
- URI dbUri = null;
- try {
- dbUri = new URI(System.getenv(DATABASE_URI_ENVIRONMENT_VARIABLE));
- } catch (URISyntaxException ex) {
- throw new RuntimeException("Bad URI syntax.", ex);
- }
- String[] tokens = dbUri.getUserInfo().split(":");
- String username = tokens[0];
- String password = tokens[1];
- String dbUrl = "jdbc:mysql://" + dbUri.getHost() + dbUri.getPath();
- return DriverManager.getConnection(dbUrl, username, password);
- }
- /**
- * {@inheritDoc }
- */
- @Override
- public void initializeDatabaseTables() throws SQLException {
- try (Connection connection = getConnection()) {
- try (Statement statement = connection.createStatement()) {
- statement.executeUpdate(DOCUMENT_TABLE.CREATE_STATEMENT);
- }
- }
- }
- /**
- * {@inheritDoc }
- */
- @Override
- public boolean updateDocument(Document document) throws SQLException {
- try (Connection connection = getConnection()) {
- connection.setAutoCommit(false);
- try (PreparedStatement statement =
- connection.prepareStatement(
- SELECT.DOCUMENT.VIA_DOCUMENT_ID)) {
- statement.setString(1, document.getId());
- try (ResultSet resultSet = statement.executeQuery()) {
- if (!resultSet.next()) {
- return false;
- }
- String editToken =
- resultSet.getString(
- DOCUMENT_TABLE.EDIT_TOKEN_COLUMN.NAME);
- if (!editToken.equals(document.getEditToken())) {
- return false;
- }
- }
- }
- try (PreparedStatement statement =
- connection.prepareStatement(UPDATE.DOCUMENT.VIA_DOCUMENT_ID)) {
- statement.setString(1, document.getText());
- statement.setString(2, document.getId());
- statement.executeUpdate();
- }
- connection.commit();
- }
- return true;
- }
- @Override
- public void deleteDocument(String id) throws SQLException {
- try (Connection connection = getConnection()) {
- try (PreparedStatement statement =
- connection.prepareStatement(DELETE.DOCUMENT)) {
- statement.setString(1, id);
- statement.executeUpdate();
- }
- }
- }
- }
- package net.coderodde.roddenotes.sql.support;
- /**
- * This class defines all the data regarding the database schema for the
- * rodde-notes app.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class MySQLDefinitions {
- /**
- * Defines the structure of the database table holding the note entries.
- */
- public static final class DOCUMENT_TABLE {
- /**
- * The name of the notes table.
- */
- public static final String TABLE_NAME = "rodde_notes_documents";
- /**
- * Describes the note ID column.
- */
- public static final class ID_COLUMN {
- /**
- * The name of the note ID column.
- */
- public static final String NAME = "document_id";
- /**
- * The length of IDs in characters.
- */
- public static final int LENGTH = 10;
- /**
- * The data type of the column.
- */
- public static final String TYPE =
- "CHAR(" + LENGTH + ") NOT NULL";
- }
- /**
- * Describes the edit token column.
- */
- public static final class EDIT_TOKEN_COLUMN {
- /**
- * The name of the edit token column.
- */
- public static final String NAME = "edit_token";
- /**
- * The length of edit tokens in characters.
- */
- public static final int LENGTH = 12;
- /**
- * The data type of the column.
- */
- public static final String TYPE = "CHAR(" + LENGTH + ") NOT NULL";
- }
- /**
- * Describes the text column.
- */
- public static final class TEXT_COLUMN {
- /**
- * The name of the text column.
- */
- public static final String NAME = "text";
- /**
- * The data type of the column.
- */
- public static final String TYPE = "TEXT NOT NULL";
- }
- /**
- * The SQL statement for creating the note table.
- */
- public static final String CREATE_STATEMENT =
- "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (n" +
- " " + ID_COLUMN.NAME + " " + ID_COLUMN.TYPE + ",n" +
- " " + EDIT_TOKEN_COLUMN.NAME + " " +
- EDIT_TOKEN_COLUMN.TYPE + ",n" +
- " " + TEXT_COLUMN.NAME + " " + TEXT_COLUMN.TYPE + ",n" +
- " PRIMARY KEY(" + ID_COLUMN.NAME + "));";
- }
- /**
- * Contains all the delete statements.
- */
- public static final class DELETE {
- /**
- * Deletes the document from the database.
- */
- public static final String DOCUMENT =
- "DELETE FROM " + DOCUMENT_TABLE.TABLE_NAME + " WHERE " +
- DOCUMENT_TABLE.ID_COLUMN.NAME + " = ?;";
- }
- /**
- * Contains all the insert statements.
- */
- public static final class INSERT {
- /**
- * Inserts a document.
- */
- public static final String DOCUMENT =
- "INSERT INTO " + DOCUMENT_TABLE.TABLE_NAME +
- " VALUES (?, ?, ?);";
- }
- /**
- * Contains all the select statements.
- */
- public static final class SELECT {
- /**
- * Contains all the select statements selecting documents.
- */
- public static final class DOCUMENT {
- /**
- * Selects a document via an ID.
- */
- public static final String VIA_DOCUMENT_ID =
- "SELECT * FROM " + DOCUMENT_TABLE.TABLE_NAME + " WHERE " +
- DOCUMENT_TABLE.ID_COLUMN.NAME + " = ?;";
- /**
- * Selects a document via an ID and an edit token.
- */
- public static final String VIA_DOCUMENT_ID_AND_EDIT_TOKEN =
- "SELECT * FROM " + DOCUMENT_TABLE.TABLE_NAME + " WHERE " +
- DOCUMENT_TABLE.ID_COLUMN.NAME + " = ? AND " +
- DOCUMENT_TABLE.EDIT_TOKEN_COLUMN.NAME + " = ?;";
- }
- }
- /**
- * Contains all the update statements.
- */
- public static final class UPDATE {
- /**
- * Contains all the update statements on the document.
- */
- public static final class DOCUMENT {
- /**
- * Updates the text via the document ID.
- */
- public static final String VIA_DOCUMENT_ID =
- "UPDATE " + DOCUMENT_TABLE.TABLE_NAME + " SET " +
- DOCUMENT_TABLE.TEXT_COLUMN.NAME + " = ? WHERE " +
- DOCUMENT_TABLE.ID_COLUMN.NAME + " = ?;";
- }
- }
- }
- package net.coderodde.roddenotes.util;
- import javax.servlet.http.HttpServletRequest;
- /**
- * This class provides various facilities.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class MiscellaneousUtilities {
- /**
- * Returns the full URL of the web application.
- *
- * @param request the servlet request object.
- * @return an URL.
- */
- public static String getServerURL(HttpServletRequest request) {
- String url = request.getRequestURL().toString();
- int lastSlashIndex = url.lastIndexOf('/');
- return url.substring(0, lastSlashIndex);
- }
- }
- package net.coderodde.roddenotes.util;
- import java.util.concurrent.ThreadLocalRandom;
- import net.coderodde.roddenotes.sql.support.MySQLDefinitions;
- /**
- * This class provides various utilities for dealing with random strings.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class RandomUtilities {
- private static final char[] ALPHABET = new char[62];
- static {
- int index = 0;
- for (char c = '0'; c <= '9'; ++c) {
- ALPHABET[index++] = c;
- }
- for (char c = 'A'; c <= 'Z'; ++c) {
- ALPHABET[index++] = c;
- }
- for (char c = 'a'; c <= 'z'; ++c) {
- ALPHABET[index++] = c;
- }
- }
- /**
- * Generates a random document ID.
- *
- * @return a document ID.
- */
- public static String generateRandomDocumentId() {
- return generateRandomString(
- MySQLDefinitions.DOCUMENT_TABLE.ID_COLUMN.LENGTH);
- }
- /**
- * Generates a random edit token.
- *
- * @return an edit token.
- */
- public static String generateRandomEditToken() {
- return generateRandomString(
- MySQLDefinitions.DOCUMENT_TABLE.EDIT_TOKEN_COLUMN.LENGTH);
- }
- /**
- * Generates a random string of given length.
- *
- * @param length the length of the string to generate.
- * @return a random string.
- */
- public static String generateRandomString(int length) {
- ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
- StringBuilder stringBuilder = new StringBuilder(length);
- for (int i = 0; i < length; ++i) {
- stringBuilder.append(
- ALPHABET[threadLocalRandom.nextInt(ALPHABET.length)]);
- }
- return stringBuilder.toString();
- }
- }
- package net.coderodde.roddenotes.util;
- import java.sql.SQLException;
- import javax.servlet.ServletContextEvent;
- import javax.servlet.ServletContextListener;
- import net.coderodde.roddenotes.sql.support.MySQLDataAccessObject;
- /**
- * This class implements a servlet context listener.
- *
- * @author Rodion "rodde" Efremov
- * @version 1.6 (Dec 15, 2017)
- */
- public final class RoddenoteServletContextListener
- implements ServletContextListener {
- @Override
- public void contextInitialized(ServletContextEvent sce) {
- try {
- MySQLDataAccessObject.INSTANCE.initializeDatabaseTables();
- } catch (SQLException ex) {
- throw new RuntimeException(ex);
- }
- }
- @Override
- public void contextDestroyed(ServletContextEvent sce) {
- }
- }
- var RoddeNotes = {};
- RoddeNotes.Parameters = {};
- RoddeNotes.Parameters.DOCUMENT_ID = "documentId";
- RoddeNotes.Parameters.EDIT_TOKEN = "editToken";
- RoddeNotes.Parameters.EDITOR_TEXT_AREA = "editorTextArea";
- function moveTextToDocument() {
- var editorElement =
- document.getElementById(
- RoddeNotes.Parameters.EDITOR_TEXT_AREA);
- var documentViewElement =
- document.getElementById("documentView");
- var documentText = editorElement.value;
- documentViewElement.innerHTML = documentText;
- }
- function typeset() {
- MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
- }
- function startTypesettingLoop() {
- setInterval(function() {typeset();}, 5000);
- }
- function startSaveLoop() {
- setInterval(function() {save();}, 10000);
- }
- function save() {
- var documentId =
- document.getElementById(
- RoddeNotes.Parameters.DOCUMENT_ID).value;
- var editToken =
- document.getElementById(
- RoddeNotes.Parameters.EDIT_TOKEN).value;
- var documentText =
- document.getElementById(
- RoddeNotes.Parameters.EDITOR_TEXT_AREA).value;
- documentText = encodeURIComponent(documentText);
- console.log(documentText);
- var xhr = new XMLHttpRequest();
- xhr.onreadystatechange = function() {
- if (this.readyState === 4 && this.status === 200) {
- var response = this.responseText;
- if (response == "success") {
- flashStatusSuccessMessage();
- } else if (response == "failure") {
- flashStatusFailureMessage();
- }
- }
- };
- xhr.open("POST", "update", true);
- xhr.setRequestHeader("Content-type",
- "application/x-www-form-urlencoded");
- xhr.send("documentId=" + documentId +
- "&editToken=" + editToken +
- "&documentText=" + documentText);
- }
- function flashStatusSuccessMessage() {
- $("#savedSuccessful").fadeIn();
- setTimeout(function() {
- $("#savedSuccessful").fadeOut();
- }, 1500);
- }
- function flashStatusFailureMessage() {
- $("#savedFailed").fadeIn();
- setTimeout(function() {
- $("#savedFailed").fadeOut();
- }, 1500);
- }
- function deleteDocument() {
- var input = prompt("Confirm current document ID:");
- var documentId =
- document.getElementById(
- RoddeNotes.Parameters.DOCUMENT_ID).value;
- var editToken =
- document.getElementById(
- RoddeNotes.Parameters.EDIT_TOKEN).value;
- if (documentId != input) {
- return;
- }
- var xhr = new XMLHttpRequest();
- xhr.onreadystatechange = function() {
- if (this.readyState === 4 && this.status === 200) {
- var response = this.responseText;
- if (response == "success") {
- window.location = "view?documentId=" + documentId;
- }
- }
- };
- xhr.open("POST", "deleteDocument", true);
- xhr.setRequestHeader("Content-type",
- "application/x-www-form-urlencoded");
- xhr.send("documentId=" + documentId + "&editToken=" + editToken);
- }
- .topNotifications {
- width: 800px;
- text-align: center;
- vertical-align: central;
- border-width: 2px;
- border-style: solid;
- margin: 0;
- padding-top: 15px;
- padding-bottom: 15px;
- font-family: sans-serif;
- font-size: 15px;
- margin-bottom: 1px;
- display: none;
- }
- #savedSuccessful {
- border-color: darkgreen;
- background-color: lightgreen;
- color: darkgreen;
- }
- #savedFailed {
- border-color: red;
- background-color: pink;
- color: red;
- }
- #documentContainer {
- width: 100%;
- }
- #documentView {
- width: 100%;
- }
- #editorTextArea {
- width: 100%;
- border-width: 2px;
- height: 200px;
- margin: 0;
- padding: 0;
- resize: none;
- font-family: monospace;
- font-size: 11pt;
- }
- .button {
- width: 100%;
- border-width: 2px;
- height: 30px;
- font-family: sans-serif;
- font-size: 15px;
- }
- #publishLink {
- width: 100%;
- border: 2px solid blue;
- background-color: lightblue;
- color: blue;
- margin: 0;
- margin-top: 3px;
- padding-top: 15px;
- padding-bottom: 15px;
- padding-left: 10px;
- padding-right: 10px;
- font-family: sans-serif;
- font-size: 15px;
- display: inline-block;
- box-sizing: border-box;
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- text-align: left;
- overflow: hidden;
- }
- #publishLinkLabel {
- font-family: sans-serif;
- font-size: 15px;
- padding-right: 15px;
- padding-bottom: 7px;
- }
- #publishLinkContent {
- background-color: white;
- color: gray;
- font-family: monospace;
- font-size: 20px;
- padding: 3px;
- padding-left: 5px;
- padding-right: 5px;
- }
- .error {
- color: red;
- }
- <%@page contentType="text/html" pageEncoding="UTF-8"%>
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>rodde-notes</title>
- <style>
- <%@include file="styles.css" %>
- </style>
- <script
- src="https://code.jquery.com/jquery-3.2.1.min.js"
- integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
- crossorigin="anonymous">
- </script>
- <script
- src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
- integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
- crossorigin="anonymous">
- </script>
- <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>
- <script type="text/x-mathjax-config">
- MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\[','\]']]}});
- </script>
- <script>
- <%@include file="code.js" %>
- </script>
- </head>
- <body>
- <div id="page">
- <div id="documentContainer">
- <div id="documentView" align="justify"></div>
- <textarea id="editorTextArea" oninput="moveTextToDocument()">${documentText}</textarea>
- <button id="typesetButton" class="button" onclick="typeset()">Typeset!</button>
- <button id="saveButton" class="button" onclick="save()">Save!</button>
- <button id="deleteButton" class="button" onclick="deleteDocument()">Delete!</button>
- <div id="publishLink">
- <div id="publishLinkLabel">Non-editable publish link:</div>
- <div id="publishLinkContent">${publishLink}</div>
- </div>
- </div>
- <form id="dataForm">
- <input type="hidden" value="${documentId}" id="documentId" />
- <input type="hidden" value="${editToken}" id="editToken" />
- </form>
- <form id="deleteForm" action="delete" method="post" style="display: none;">
- <input type="text" id="idField" name="documentId"/>
- <button type="submit">Delete</button>
- </form>
- <div id ="savedSuccessful" class="topNotifications">
- The document is updated.
- </div>
- <div id="savedFailed" class="topNotifications">
- Could not update the document.
- </div>
- </div>
- <script>
- startTypesettingLoop();
- startSaveLoop();
- moveTextToDocument();
- </script>
- </body>
- </html>
- <%@page contentType="text/html" pageEncoding="UTF-8"%>
- <!DOCTYPE html>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>rodde-notes</title>
- <style>
- #view {
- width: 800px;
- margin: auto;
- }
- #text {
- text-align: justify;
- }
- </style>
- <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>
- <script type="text/x-mathjax-config">
- MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\[','\]']]}});
- </script>
- </head>
- <body>
- <div id="view">
- <p id="text">${documentText}</p>
- </div>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement