changeset 608:c462161504a3

Extend example with Download functionality
author Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com>
date Tue, 04 Jan 2022 14:42:37 +0100 (2022-01-04)
parents 69b0bcf5f62d
children 6666a9c62460
files example/OnClientExample.java
diffstat 1 files changed, 112 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/example/OnClientExample.java
+++ b/example/OnClientExample.java
@@ -7,12 +7,17 @@
  */
 
 import org.monetdb.jdbc.MonetConnection;
-import org.monetdb.jdbc.MonetConnection.UploadHandler;
 
 import java.io.BufferedReader;
+import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.PrintStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -32,9 +37,26 @@ public class OnClientExample {
 			String[] queries = {
 					"DROP TABLE IF EXISTS mytable",
 					"CREATE TABLE mytable(i INT, t TEXT)",
+
+					// with this filename our handler will make up data by itself
 					"COPY INTO mytable FROM 'generated.csv' ON CLIENT",
+
+					// with this filename our handler will access the file system
+					"COPY INTO mytable FROM 'data.csv' ON CLIENT",
+
+					// with this statement, the server will ask the handler to stop halfway
 					"COPY 20 RECORDS OFFSET 5 INTO mytable FROM 'generated.csv' ON CLIENT",
+
+					// this demonstrates sending errors
 					"COPY INTO mytable FROM 'nonexistentfilethatdoesnotexist.csv' ON CLIENT",
+
+					// downloads but does not write to file, only counts the lines
+					"COPY SELECT * FROM mytable INTO 'justcount.csv' ON CLIENT",
+
+					// downloads to actual file
+					"COPY SELECT * FROM mytable INTO 'download.csv' ON CLIENT",
+
+					// demonstrate that the connection is still alive
 					"SELECT COUNT(*) FROM mytable",
 			};
 
@@ -54,9 +76,11 @@ public class OnClientExample {
 		Class.forName("org.monetdb.jdbc.MonetDriver");
 		Connection conn = DriverManager.getConnection(dbUrl, userName, password);
 
-		// Register upload handler
-		MyUploader handler = new MyUploader(uploadDir, filesAreUtf8);
-		conn.unwrap(MonetConnection.class).setUploadHandler(handler);
+		// Register upload- and download handler
+		MyHandler handler = new MyHandler(uploadDir, filesAreUtf8);
+		MonetConnection monetConnection = conn.unwrap(MonetConnection.class);
+		monetConnection.setUploadHandler(handler);
+		monetConnection.setDownloadHandler(handler);
 
 		Statement stmt = conn.createStatement();
 		for (String q : queries) {
@@ -86,12 +110,12 @@ public class OnClientExample {
 	}
 
 
-	private static class MyUploader implements UploadHandler {
+	private static class MyHandler implements MonetConnection.UploadHandler, MonetConnection.DownloadHandler {
 		private final Path uploadDir;
 		private final boolean filesAreUtf8;
 		private boolean stopUploading = false;
 
-		public MyUploader(String uploadDir, boolean filesAreUtf8) {
+		public MyHandler(String uploadDir, boolean filesAreUtf8) {
 			this.uploadDir = FileSystems.getDefault().getPath(uploadDir).normalize();
 			this.filesAreUtf8 = filesAreUtf8;
 		}
@@ -104,16 +128,58 @@ public class OnClientExample {
 
 		@Override
 		public void handleUpload(MonetConnection.Upload handle, String name, boolean textMode, long linesToSkip) throws IOException {
-
 			// We can upload data read from the file system but also make up our own data
 			if (name.equals("generated.csv")) {
 				uploadGeneratedData(handle, linesToSkip);
-				return;
+			} else {
+				uploadFileData(handle, name, textMode, linesToSkip);
 			}
+		}
+
+		@Override
+		public void handleDownload(MonetConnection.Download handle, String name, boolean textMode) throws IOException {
+			if (name.equals("justcount.csv")) {
+				justCountLines(handle);
+			} else {
+				downloadFileData(handle, name, textMode);
+			}
+		}
+
+		private Path securelyResolvePath(String name) {
+			Path p = uploadDir.resolve(name).normalize();
+			if (p.startsWith(uploadDir)) {
+				return p;
+			} else {
+				return null;
+			}
+		}
 
+		private void uploadGeneratedData(MonetConnection.Upload handle, long toSkip) throws IOException {
+			// Set the chunk size to a tiny amount, so we can demonstrate
+			// cancellation handling. The default chunk size is one megabyte.
+			// DO NOT DO THIS IN PRODUCTION!
+			handle.setChunkSize(50);
+
+			// Make up some data and upload it.
+			PrintStream stream = handle.getStream();
+			long n = 100;
+			System.out.printf("  HANDLER: uploading %d generated lines, numbered %d to %d%n", n - toSkip, toSkip + 1, n);
+			long i;
+			for (i = toSkip + 1; i <= n; i++) {
+				if (stopUploading) {
+					System.out.printf("  HANDLER: at line %d we noticed the server asked us to stop sending%n", i);
+					break;
+				}
+				stream.printf("%d|the number is %d%n", i, i);
+			}
+			System.out.println("  HANDLER: done uploading");
+			stream.close();
+		}
+
+		private void uploadFileData(MonetConnection.Upload handle, String name, boolean textMode, long linesToSkip) throws IOException {
 			// Validate the path, demonstrating two ways of dealing with errors
-			Path path = securityCheck(name);
-			if (path == null || !Files.exists(path)) {
+			Path path = securelyResolvePath(name);
+			if (path == null  || !Files.exists(path)) {
 				// This makes the COPY command fail but keeps the connection
 				// alive. Can only be used if we haven't sent any data yet
 				handle.sendError("Invalid path");
@@ -136,47 +202,17 @@ public class OnClientExample {
 				// by pretending the data is binary.
 				uploadAsBinary(handle, path);
 			} else {
-				// Charset and skip handling really necessary
+				// Charset and skip handling are really necessary
 				uploadAsText(handle, path, linesToSkip);
 			}
 		}
 
-		private Path securityCheck(String name) {
-			Path p = uploadDir.resolve(name).normalize();
-			if (p.startsWith(uploadDir)) {
-				return p;
-			} else {
-				return null;
-			}
-		}
-
-		private void uploadGeneratedData(MonetConnection.Upload handle, long toSkip) throws IOException {
-			// Set the chunk size to a tiny amount so we can demonstrate
-			// cancellation handling. The default chunk size is one megabyte.
-			// DO NOT DO THIS IN PRODUCTION!
-			handle.setChunkSize(50);
-
-			// Make up some data and upload it.
-			PrintStream stream = handle.getStream();
-			long n = 100;
-			System.out.printf("  HANDLER: uploading %d generated lines, numbered %d to %d%n", n - toSkip, toSkip + 1, n);
-			long i;
-			for (i = toSkip + 1; i <= n; i++) {
-				if (stopUploading) {
-					System.out.printf("  HANDLER: at line %d we noticed the server asked us to stop sending%n", i);
-					break;
-				}
-				stream.printf("%d|the number is %d%n", i, i);
-			}
-			System.out.println("  HANDLER: done uploading");
-			stream.close();
-		}
-
 		private void uploadAsText(MonetConnection.Upload handle, Path path, long toSkip) throws IOException {
 			BufferedReader reader = Files.newBufferedReader(path);// Converts from system encoding to Java text
 			for (long i = 0; i < toSkip; i++) {
 				reader.readLine();
 			}
+			// This variant of uploadFrom takes a Reader
 			handle.uploadFrom(reader); // Converts from Java text to UTF-8 as required by MonetDB
 		}
 
@@ -184,7 +220,41 @@ public class OnClientExample {
 			// No charset conversion whatsoever..
 			// Use this for binary data or when you are certain the file is UTF-8 encoded.
 			InputStream stream = Files.newInputStream(path);
+			// This variant of uploadFrom takes a Stream
 			handle.uploadFrom(stream);
 		}
+
+
+		private void justCountLines(MonetConnection.Download handle) throws IOException {
+			System.out.println("  HANDLER: not writing the download to file, just counting the lines");
+			InputStream stream = handle.getStream();
+			InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8); // MonetDB always sends UTF-8
+			BufferedReader bufreader = new BufferedReader(reader);
+			long count = 0;
+			while (bufreader.readLine() != null) {
+				count++;
+			}
+			System.out.println("  HANDLER: file had " + count + " lines");
+		}
+
+		private void downloadFileData(MonetConnection.Download handle, String name, boolean textMode) throws IOException {
+			Path path = securelyResolvePath(name);
+			if (path == null) {
+				handle.sendError("Illegal path");
+				return;
+			}
+
+			OutputStream stream = Files.newOutputStream(path);
+			if (!textMode || filesAreUtf8) {
+				handle.downloadTo(stream);
+				stream.close(); // do not forget this
+			} else {
+				OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.defaultCharset()); // let system decide the encoding
+				BufferedWriter bufwriter = new BufferedWriter(writer);
+				handle.downloadTo(bufwriter);
+				bufwriter.close(); // do not forget this
+			}
+		}
+
 	}
 }