Part 6 :: Connecting the dots to finish the application

We have a running Vaadin application and we have the api to access our Notes application. All we have to do is to connect them together…

Open your Vaadin application and there open the DatabaseConnector class. This class is providing our cachedData list which will be accessed from other parts of the application to show and manipulate data. Currently, this list will be filled with sample data during startup. We now change this to be filled with our Notes data. That’s all we have to do, to connect our Vaadin application to the Notes database (for reading data).

Create a new method called readDominoData:

/**
* Read all tasks from the Domino Database and write it to cachedData list
*/
private void readDominoData() {
	//TODO: Add errorhandling and do other useful validations

	//Create an empty list
	cachedData = new ArrayList<DocumentData>();

	//Read JSON from domino REST api
	String url = "http://10.211.55.6/demo/tasks.nsf/api.xsp/readData"; //TODO: Change URL to your domino server

	RestTemplate restTemplate = new RestTemplate();
	ResponseEntity<JsonNode> response = restTemplate.getForEntity(url, JsonNode.class);
	try {
		//Get the returned JsonNode object
		JsonNode json = response.getBody();

		//Check if the api returned a Json array
		if (json.isArray()) {
			//Loop through JSONArray
			for(int i=0; i< json.size(); i++) {
				//Extract each individual JSON Object representing on of our task documents
				JsonNode json_task = json.get(i);

				//Create DocumentData objects
				DocumentData document = new DocumentData();
				if (json_task.has("universalID")) document.setUniversalId(json_task.get("universalID").asText());
				if (json_task.has("status")) document.setStatus(json_task.get("status").asText());
				if (json_task.has("topic")) document.setTopic(json_task.get("topic").asText());
				if (json_task.has("description")) document.setDescription(json_task.get("description").asText());

				//we get the due date as pure text, so we have to convert it to a LocalDate object
				if (json_task.has("dueDate")) {
					String dueDateString = json_task.get("dueDate").asText();
					DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
					document.setDueDate(LocalDate.parse(dueDateString, formatter));
				}

				//Add DocumentData to the cache
				cachedData.add(document);
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

This code uses the REST api to read all data in JSON format, convert those JSON objects into DocumentData objects and store them in our cachedData list. This is a demo code only. For production, you should create multiple methods for reading JSON, converting JSON and so on. The url is also hard coded, which should be typically read from a config file.

NOTE: This code assumes, that your Domino server provides the property “dueDate” in format MM/dd/yyyy. Change this to your date format our normalise the date for example to the json format in your API implementation.

One thing is left, change your constructor code to call the readDominoData method instead of createSampleData:

/**
* Constructor
*/
public DatabaseConnector() {
	//createSampleData();
	readDominoData();
}

This is the complete code of the DatabaseConnector class:

package net.stephankopp;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;

@Service
public class DatabaseConnector {

	private List<DocumentData> cachedData;

	/**
	 * Constructor
	 */
	public DatabaseConnector() {
		//createSampleData();
		readDominoData();
	}

	/**
	 * Create sample data
	 */
	private void createSampleData() {
		cachedData = new ArrayList<DocumentData>();
		for (int i=1; i<=100; i++) {
			DocumentData data = new DocumentData();
			data.setStatus("OPEN");
			data.setDescription("Task " + i);
			data.setDueDate(LocalDate.now().plusDays(i));
			data.setTopic("Project " + i);
			cachedData.add(data);
		}
	}

	/**
	 * Deletes the given documentData object
	 * @param documentData
	 */
	public void delete(DocumentData documentData) {
		//Remove the object from the cachedData list
		cachedData.remove(documentData);
	}

	/**
	 * Adds the given documentData object
	 * @param documentData
	 */
	public void add(DocumentData documentData) {
		//add the given DocumentData to the cachedData list
		cachedData.add(documentData);

		//create the document also in the Domino database
		save(documentData);
	}

	/**
	 * Save changes to the Domino Database. This method can also be used to create documents.
	 * For new documents, the UniversalID property is null and will be omitted, which means for the api to create a new document.
	 * @param doc
	 */
	public void save(DocumentData doc) {
		String url = "http://10.211.55.6/demo/tasks.nsf/api.xsp/updateData"; //TODO: Change URL to your domino server

		//Create a json object which will be sent to the REST api
		ObjectNode json = JsonNodeFactory.instance.objectNode();

		//Add all DocumentData details to your JSON object
		if (doc.getUniversalId() != null) json.put("universalID", doc.getUniversalId());
		if (doc.getStatus() != null) json.put("status", doc.getStatus());
		if (doc.getTopic() != null) json.put("topic", doc.getTopic());
		if (doc.getDescription() != null) json.put("description", doc.getDescription());
		if (doc.getDueDate() != null) {
			//Due date has to be converted to the string format expected by the rest api ("02/22/2017")
			DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
			json.put("dueDate", doc.getDueDate().format(formatter));
		}

		//Send the json object to the REST api
		RestTemplate restTemplate = new RestTemplate();
		ResponseEntity<String> response = restTemplate.postForEntity(url, json, String.class);

		//TODO: Check the response and show an appropriate message to the user
	}

	/**
	 * Read all tasks from the Domino Database and write it to cachedData list
	 */
	private void readDominoData() {
		//TODO: Add errorhandling and do other useful validations

		//Create an empty list
		cachedData = new ArrayList<DocumentData>();

		//Read JSON from domino REST api
		String url = "http://10.211.55.6/demo/tasks.nsf/api.xsp/readData"; //TODO: Change URL to your domino server

		RestTemplate restTemplate = new RestTemplate();
		ResponseEntity<JsonNode> response = restTemplate.getForEntity(url, JsonNode.class);

		try {
			//Get the returned JsonNode object
			JsonNode json = response.getBody();

			//Check if the api returned a Json array
			if (json.isArray()) {
				//Loop through JSONArray
				for(int i=0; i< json.size(); i++) {
					//Extract each individual JSON Object representing on of our task documents
					JsonNode json_task = json.get(i);

					//Create DocumentData objects
					DocumentData document = new DocumentData();
					if (json_task.has("universalID")) document.setUniversalId(json_task.get("universalID").asText());
					if (json_task.has("status")) document.setStatus(json_task.get("status").asText());
					if (json_task.has("topic")) document.setTopic(json_task.get("topic").asText());
					if (json_task.has("description")) document.setDescription(json_task.get("description").asText());

					//we get the due date as pure text, so we have to convert it to a LocalDate object
					if (json_task.has("dueDate")) {
						String dueDateString = json_task.get("dueDate").asText();
						DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
						document.setDueDate(LocalDate.parse(dueDateString, formatter));
					}

					//Add DocumentData to the cache
					cachedData.add(document);
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public List<DocumentData> getCachedData() {
		return cachedData;
	}

	public void setCachedData(List<DocumentData> cachedData) {
		this.cachedData = cachedData;
	}

}

Start the application and you should see your tasks from the Notes Database in your Vaadin application. The data will be read from the REST api only during application start, but you can easily call this created method from anywhere in your code. Even create a scheduler to update your data automatically in background.

vaadinui_withdominodata

Deleting, editing and creating documents is still working in the ui, but lost as soon as you restart your application. We need another method to write your changes back to the Domino database. In the DatabaseConnector class, create a new method called save:

/**
* Save changes to the Domino Database. This method can also be used to create documents.
* For new documents, the UniversalID property is null and will be omitted, which means for the api to create a new document.
* @param doc
*/
public void save(DocumentData doc) {
	String url = "http://10.211.55.6/demo/tasks.nsf/api.xsp/updateData"; //TODO: Change URL to your domino server

	//Create a json object which will be sent to the REST api
	ObjectNode json = JsonNodeFactory.instance.objectNode();

	//Add all DocumentData details to your JSON object
	if (doc.getUniversalId() != null) json.put("universalID", doc.getUniversalId());
	if (doc.getStatus() != null) json.put("status", doc.getStatus());
	if (doc.getTopic() != null) json.put("topic", doc.getTopic());
	if (doc.getDescription() != null) json.put("description", doc.getDescription());
	if (doc.getDueDate() != null) {
		//Due date has to be converted to the string format expected by the rest api ("02/22/2017")
		DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
		json.put("dueDate", doc.getDueDate().format(formatter));
	}

	//Send the json object to the REST api
	RestTemplate restTemplate = new RestTemplate();
	ResponseEntity<String> response = restTemplate.postForEntity(url, json, String.class);

	//TODO: Check the response and show an appropriate message to the user
}

This method is not really complicated. It just creates a json object, puts all data from the given DocumentData object in the format expected by our api. This json object will be sent to the api to update or create a document. The response is ignored for this demo, but in production you should return valuable information to your application to show more details to the users, e.g. “successfully created” or “not created, because due date is in the past” or whatever you want. Don’t forget to change the url to your domino server and notes application!

The newly created save method is not yet used in our UI application. Now, we can use this method when creating a document. In our DatabaseConnector class, we have the method add which will be already called form the UI to add a new document:

/**
* Adds the given documentData object
* @param documentData
*/
public void add(DocumentData documentData) {
	//add the given DocumentData to the cachedData list
	cachedData.add(documentData);
}

Simply add one line of code, to add the document also to the Domino database. That’s all we have to do to create new documents in the Domino database:

/**
* Adds the given documentData object
* @param documentData
*/
public void add(DocumentData documentData) {
	//add the given DocumentData to the cachedData list
	cachedData.add(documentData);
	//create the document also in the Domino database
	save(documentData);
}

Updating documents have to be implemented in the VaadinUI class, because we are not yet calling any method in the DatabaseConnector during an update. Open your VaadinUI class and search for the section, where we add the double-click listener to the grid:

//Add a double-click listener
dataGrid.addItemClickListener(event -> {
	//Check, if it is a double-click event
	if (event.getMouseEventDetails().isDoubleClick()) {
		//get the item which has been clicked
		DocumentData documentData = event.getItem();

		//open the item in a window
		DocumentDataView window = new DocumentDataView(documentData);
		getUI().addWindow(window);

		//add a listender, which will be executed when the window will be closed
		window.addCloseListener(closeEvent -> {
			updateGrid(); //refresh grid to show any changes
		});
	}
});

After closing the window, we have to check if user has saved the changes. This is stored in the isChanged property of DocumentData object. The following code saves the document to the Domino database when user hit the save button to close the window:

//Add a double-click listener
dataGrid.addItemClickListener(event -> {
	//Check, if it is a double-click event
	if (event.getMouseEventDetails().isDoubleClick()) {
		//get the item which has been clicked
		DocumentData documentData = event.getItem();

		//open the item in a window
		DocumentDataView window = new DocumentDataView(documentData);
		getUI().addWindow(window);

		//add a listender, which will be executed when the window will be closed
		window.addCloseListener(closeEvent -> {
			updateGrid(); //refresh grid to show any changes

			//check if "changed" property of DocumentData object is true, which means user hit the save button
			if (documentData.isChanged()) {
				//save the documentData object to the Domino database
				databaseConnector.save(documentData);
			}
		});
	}
});

There is currently no check, if a user has really changed anything. This should be done in production to avoid unnecessary api calls. Start your application and try if your changes are now written correctly to your Notes database. As an exercise, you can implement the delete function by your own.

That’s it, you have a running example of a Vaadin application connected to a Domino database. Read in the last part 7 a summary and about further advanced techniques…