Java Quiz Player

A CRUD App with MongoDB Java - Java Source Code

Main.java

package com.javaquizplayer.examples.mongocrudapp;

import org.bson.Document;
import java.util.Scanner;
import java.util.List;
import java.util.Random;
import java.util.logging.*;


/**
 * Application's starter class.
 * Displays a main menu, accepts user options and inputs, performs
 * database operations and returns a result.
 * See MongoOps.java, which has functions for database operations.
 */
public class Main {

	private static Scanner scanner;
	private static MongoOps mongoOps;

	// Flag specifies if the app has an error and is set to false.
	private static boolean runapp = true;
	
	// A set of Name and City field values from which the program
	// selects a value randomly, when the user doesn't enter one.
	private static final String [] NAMES = 
		{ "Arian", "Kwame", "Jack", "Lin", "Marie" };
	private static final String [] CITIES = 
		{ "Avalon", "Shangri-La", "Troy", "Utopia", "Xanadu" };


	
	/*
	 * Main method, runs the program.
	 */
	public static void main(String [] args) {

		// Initial setup:
		// Sets the logger.
		// Loads application properties.
		// Instantiate the MongoOps object to get the database
		// connection and then perform database operations.
		// Initialize the scanner object.
		System.out.println("\nStarting the app... ");
		setupLogger();
		
		try {
			AppProperties props = new AppProperties();
			mongoOps = new MongoOps(props);
		}
		catch(RuntimeException e) {
			System.out.println(e.getMessage());
			runapp = false;
		}
			
		scanner = new Scanner(System.in);

		// Indefinite while loop displays the menu, accepts user's choice,
		// performs a CRUD action and prints an output. Exit the loop
		// (and the program) when the user chooses so.
		COMMAND_LOOP:
		while (runapp) {
		
			showMenu();
			System.out.print("Enter Menu Option [ <ENTER> to exit ]: ");
			String option = scanner.nextLine().trim();
			
			if (option.isEmpty() || option.equals("0")) {
				// User has entered a "0" -or- pressed <ENTER>
				// Exit the while-loop and the program.
				break COMMAND_LOOP;
			}

			System.out.println("");
			
			try {
				processMenuOptions(option);
			}
			catch(RuntimeException e) {
				System.out.println(e.getMessage());
				runapp = false;
			}			

			System.out.print("\nPress <ENTER> to Continue...");
			scanner.nextLine();
		}

		// Close the resources and exit the program.
		closeResources();
		System.out.println("CRUD Company App - Closed.");
	}
	
	/*
	 * Routine to turn off the MongoDB Java driver logging output on the
	 * console. This log output interferes with the program functionality.
	 * Only, the severe errors are logged.
	 */
	private static void setupLogger() {
		Logger logger = Logger.getLogger("org.mongodb.driver");
		logger.setLevel(Level.SEVERE);
	}
	
	/*
	 * Displays the menu and its options.
	 */
	private static void showMenu() {
	
		System.out.println("");
		System.out.println("             x------------------------------x");
		System.out.println("               CRUD Company App - Main Menu  ");
		System.out.println("             x------------------------------x");
		System.out.println("                       1. Add                ");
		System.out.println("                       2. Update             ");
		System.out.println("                       3. Delete             ");
		System.out.println("                       4. Query All          ");
		System.out.println("                       5. Query by Field     ");
		System.out.println("                       0. Exit               ");
		System.out.println("             x------------------------------x");
		System.out.println("");	
	}

	/*
	 * Process the user entered menu option.
	 * Calls the appropriate CRUD routine based upon the input option.
	 * Throws a RuntimeException propagated from the doXXX method, if
	 * there is a database error during a write.
	 */	
	private static void processMenuOptions(String option) {
	
		switch(option) {
		
			case "1": doAdd(); break;
			case "2": doUpdate(); break;			
			case "3": doDelete(); break;
			case "4": doQueryAll(); break;
			case "5": doQueryByNameAndOrCity(); break;
			default: System.out.println("Not a valid option!"); 
		}
	}

	/*
	 * Routine for the add option.
	 * Accepts a Name and City value, and if not provided generates
	 * random values for them. Calls a database routine to add a
	 * document into the database with the entered values. The newly
	 * added document is returned and printed.
	 * Throws a RuntimeException propagated from the MongoOps.java class
	 * method, if there is a database error during a write.
	 */	
	private static void doAdd() {
	
		System.out.println("[ Add Document, enter Name and City field values ]");
		System.out.print("Enter Name [ Defaults to a random Name ]: ");
		String name = scanner.nextLine().trim();
		System.out.print("Enter City [ Defaults to a random City ]: ");
		String city = scanner.nextLine().trim();
		
		if (name.isEmpty()) {
			name = NAMES[new Random().nextInt(NAMES.length)];
		}
		
		if (city.isEmpty()) {
			city = CITIES[new Random().nextInt(CITIES.length)];
		}
		
		Document doc = mongoOps.insertDoc(name, city);
		System.out.println("Document added: " + doc.toJson());
	}

	/*
	 * Routine for the update option.
	 * Accepts a Name to query the document and a City value to be updated.
	 * Calls a database routine to update the document in the database 
	 * using the entered values. An update status message is printed.
	 * Throws a RuntimeException propagated from the MongoOps.java class
	 * method, if there is a database error during a write.
	 */	
	private static void doUpdate() {
	
		System.out.println("[ Update a Document, for a matching Name ]");
		System.out.print("Enter Name to match: ");
		String name = scanner.nextLine().trim();
		
		if (! name.isEmpty()) {

			System.out.print("Enter City to update: ");
			String city = scanner.nextLine().trim();
			
			if (! city.isEmpty()) {

				long modifiedCount = mongoOps.updateDoc(name, city);
				System.out.println("Updated " + modifiedCount + " document(s)");
			}
			else {
				System.out.println("City value required to update!");
			}
		}
		else {
			System.out.println("Name value required to update!");
		}
	}
	
	/*
	 * Routine for the delete option.
	 * Accepts a Name to query the document and delete. Calls a database
	 * routine to delete the document in the database using the entered
	 * Name value. A delete status message is printed.
	 * Throws a RuntimeException propagated from the MongoOps.java class
	 * method,if there is a database error during a write.
	 */	
	private static void doDelete() {
	
		System.out.println("[ Delete a Document ]");
		System.out.print("Enter Name to delete: ");
		String name = scanner.nextLine().trim();
		
		if (name.isEmpty()) {
			System.out.println("Name value required to delete!");
		}
		else {
			long deletedCount = mongoOps.deleteDoc(name);
			System.out.println("Deleted " + deletedCount + " document(s)");
		}
	}

	/*
	 * Routine for the query all option.
	 * Calls a database routine to get all the documents in the database.
	 * The returned documents are printed.
	 */		
	private static void doQueryAll() {
	
		System.out.println("[ Query All Result ]");
		List<Document> docs = mongoOps.getAll();
		
		if (docs.isEmpty()) {
			System.out.println("No documents found!");
		}
		else {
			docs.forEach(doc -> System.out.println(doc.toJson()));
		}
	}

	/*
	 * Routine for the query by name and/or city option.
	 * Accepts the Name and/or City values to query by.
	 * - When no values are entered for both fields, no query is made
	 * - When both values are entered, the database is queried with
	 * Name -and- City.
	 * - When only one field value is entered, the database is queried
	 * with that field only.
	 * Calls a database routine to select the documents in the database
	 * based upon the field values entered. The returned documens are
	 * printed.
	 */		
	private static void doQueryByNameAndOrCity() {
	
		System.out.println("[ Query by Name and/or City ]");
		System.out.print("Enter Name: ");
		String name = scanner.nextLine().trim();
		System.out.print("Enter City: ");
		String city = scanner.nextLine().trim();
		
		if (name.isEmpty() && city.isEmpty()) {		
			System.out.println("Cannot query without one of the values!");
		}
		else {
			List<Document> docs = mongoOps.queryByField(name, city);
			
			if (docs.isEmpty()) {
				System.out.println("No documents found!");
			}
			else {
				System.out.println("Document(s) found:");
				docs.forEach(doc -> System.out.println(doc.toJson()));
			}
		}
	}
	
	/*
	 * Close to release resources for the Scanner and the MongoDB connection.
	 */
	private static void closeResources() {

		if (scanner != null) {
			scanner.close();
		}
		
		if (mongoOps != null) {
			mongoOps.close();
		}
	}

}

MongoOps.java

package com.javaquizplayer.examples.mongocrudapp;

import java.util.List;
import java.util.ArrayList;
import org.bson.Document;
import org.bson.conversions.Bson;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.*;
import com.mongodb.client.model.Updates;
import com.mongodb.client.model.Filters;
import com.mongodb.MongoException;


/**
 * Class has MongoDB database connection creation and CRUD operation methods.
 * See Main.java, where this class is accessed.
 */
public class MongoOps {

	private MongoClient mongoClient;
	private MongoCollection<Document> collection;

	/*
	 * Constructor with the input application properties to build the connection URL.
	 * Gets connection to the database and creates an instance of the collection.
	 * Throws a RuntimeException if the conection URL, the database namespace or
	 * the collection name are not valid.
	 */
	public MongoOps(AppProperties props) {
	
		try {
			final String URL = "mongodb://" + 
						props.getPropertyValue(AppProperties.HOST_K) + 
						":" + 
						props.getPropertyValue(AppProperties.PORT_K) + 
						"/";
			mongoClient = MongoClients.create(URL);
			collection = mongoClient
						.getDatabase("crud")
						.getCollection("test");
		}
		catch(RuntimeException e) {
			System.out.println("Database connection format, database or collection name error.");
			throw new RuntimeException(e);
		}
		
		System.out.println("Connected to MongoDB database.");
	}
	
	/*
	 * Routine to close the database connection.
	 */
	public void close() {

		if (mongoClient != null) {
		
			mongoClient.close();
		}
		
		System.out.println("\nMongoDB database connection closed.");
	}

	/*
	 * Returns the document inserted in the database collection using
	 * the input field values. The inserted document, in addition,
	 * has the _id field, which was generated by the driver software
	 * as part of the insert operation.
	 * Throws a RuntimeException if there is a database error during the write.
	 */	
	public Document insertDoc(String name, String city) {

		Document doc = new Document("name", name).append("city", city);

		try {
			collection.insertOne(doc);
		}
		catch(MongoException e) {
			System.out.println("Database insert error.");
			throw new RuntimeException(e.getMessage());
		}
		
		return doc;
	}

	/*
	 * Returns the number of documents updated in the database collection
	 * using the input field values. Updates the first matching document.
	 * Throws a RuntimeException if there is a database error during the write.
	 */		
	public long updateDoc(String name, String city) {
	
		Bson filter = Filters.eq("name", name);
		Bson update = Updates.set("city", city);
		UpdateResult result = null;
		
		try {
			result = collection.updateOne(filter, update);
		}
		catch(MongoException e) {
			System.out.println("Database update error.");
			throw new RuntimeException(e.getMessage());
		}
		
		return result.getModifiedCount();
	}

	/*
	 * Returns the number of  documents deleted from the database collection
	 * using the input Name field value. Deletes the first matching document.
	 * Throws a RuntimeException if there is a database error during the write.
	 */	
	public long deleteDoc(String name) {
	
		Bson filter = new Document().append("name", name);
		DeleteResult result = null;
		
		try {
			result = collection.deleteOne(filter);		
		}
		catch(MongoException e) {
			System.out.println("Database delete error.");
			throw new RuntimeException(e.getMessage());
		}

		return result.getDeletedCount();
	}
	
	/*
	 * Returns all documents from the database collection.
	 */
	public List<Document> getAll() {

		return collection.find().into(new ArrayList<Document>());	
	}

	/*
	 * Returns the documents from the database collection based upon
	 * the input field values.
	 * - When both values are entered, the database is queried with
	 * Name -and- City.
	 * - When only one field value is entered, the database is queried
	 * with -that- field only.
	 */	
	public List<Document> queryByField(String name, String city) {

		Bson filter = null;

		if (name.isEmpty()) {
			// Query by City
			filter = Filters.eq("city", city);
		}
		else
		if (city.isEmpty()) {
			// Query by Name			
			filter = Filters.eq("name", name);
		}
		else {
			// Query with Name and City
			filter = Filters.and(Filters.eq("name", name), Filters.eq("city", city));
		}
		
		return collection.find(filter).into(new ArrayList<Document>());
	}

}

AppProperties.java

package com.javaquizplayer.examples.mongocrudapp;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.util.Properties;


/*
 * Class provides read access to the MongoDB connection
 * information (host and port) stored in a properties file.
 */
public class AppProperties {

	// Represents persistent set of properties, 
	// (key/value pairs that can retrieved).
	private Properties properties;

	// Property keys
	public static final String HOST_K = "host";
	public static final String PORT_K = "port";

	// Location where the file is stored.
	private static final String BASE_DIRECTORY =
			System.getProperty("user.dir");
	// The properties file name.		
	private static final String APP_PROPS_FILE =
			"application_properties.properties";


	/*
	 * Constructs an instance.
	 * Loads the properties from the file.
	 * Throws an runtime exception in case of a problem with the properties file.
	 */
	public AppProperties() {

		try {
			properties = loadPropertiesFromFile();
		}
		catch(IOException e) {
			System.out.println("Error reading the properties file: " + APP_PROPS_FILE);
			throw new RuntimeException(e.toString());
		}
	}

	/*
	 * Returns the value of the specified property.
	 */
	public String getPropertyValue(String propertyName) {

		return properties.getProperty(propertyName);
	}

	/*
	 * Loads and returns the properties from the file.
	 * Throws IOException if there is a problem with the file.
	 */
	private Properties loadPropertiesFromFile()
			throws IOException {

		Path path = Paths.get(BASE_DIRECTORY, APP_PROPS_FILE);
		InputStream fis = Files.newInputStream(path);
		Properties loadedProperties = new Properties();
		loadedProperties.load(fis);
		fis.close();
		return loadedProperties;
	}
}

Return to top


This page uses Java code formatting from http://hilite.me/.