Mercurial > hg > monetdb-java
changeset 503:7e3987c16cde onclient
Succesful file uploading
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Wed, 18 Aug 2021 15:02:59 +0200 (2021-08-18) |
parents | 83354bd21320 |
children | 8aa70bd8d21f |
files | src/main/java/org/monetdb/jdbc/MonetConnection.java src/main/java/org/monetdb/jdbc/MonetFileTransfer.java src/main/java/org/monetdb/jdbc/MonetUploadHandle.java |
diffstat | 3 files changed, 190 insertions(+), 36 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main/java/org/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java @@ -153,6 +153,8 @@ public class MonetConnection /** A cache to reduce the number of DatabaseMetaData objects created by getMetaData() to maximum 1 per connection */ private DatabaseMetaData dbmd; + /** A handler for ON CLIENT requests */ + private MonetFileTransfer fileTransfer; /** * Constructor of a Connection for MonetDB. At this moment the @@ -1197,6 +1199,22 @@ public class MonetConnection } /** + * Registers a MonetFileTransfer handler to support for example COPY ON CLIENT + * + * @param fileTransfer the handler to register, or null to deregister + */ + public void setFileTransfer(MonetFileTransfer fileTransfer) { + this.fileTransfer = fileTransfer; + } + + /** + * Returns the currently registerered MonetFileTransfer handler, or null + */ + public MonetFileTransfer setFileTransfer() { + return fileTransfer; + } + + /** * Returns a string identifying this Connection to the MonetDB server. * * @return a String representing this Object @@ -3174,53 +3192,41 @@ public class MonetConnection // }}} private String handleTransfer(String transferCommand) throws IOException { - String[] parts = transferCommand.split(" " , 3); - if (parts.length == 3) { - if (parts[0].equals("r")) { - int offset; - try { - offset = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - return e.toString(); - } - return handleUpload(parts[2], true, offset); + String[] parts = transferCommand.split(" ", 3); + if (transferCommand.startsWith("r ") && parts.length == 3) { + final int offset; + try { + offset = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + return e.toString(); } - if (parts[0].equals("r")) { - int offset; - try { - offset = Integer.parseInt(parts[1]); - } catch (NumberFormatException e) { - return e.toString(); - } - return handleUpload(parts[2], false, offset); - } - } else if (parts.length == 2) { - if (parts[0].equals("w")) { - return handleDownload(parts[1]); - } + return handleUpload(parts[2], true, offset); + } else if (transferCommand.startsWith("rb ")) { + return handleUpload(transferCommand.substring(3), false, 0); + } else { + return "JDBC does not support this file transfer yet: " + transferCommand; } - return "JDBC does not support this file transfer yet: " + transferCommand; } private String handleUpload(String path, boolean textMode, int offset) throws IOException { + if (fileTransfer == null) { + return "No file transfer handler has been registered"; + } + + MonetUploadHandle handle = new MonetUploadHandle(server); boolean wasFaking = server.setInsertFakeFlushes(false); try { - MapiSocket.UploadStream us = server.uploadStream(); - us.write('\n'); - PrintStream ps = null; - try { - ps = new PrintStream(us, false, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return e.toString(); + fileTransfer.handleUpload(handle, path, textMode, offset); + if (!handle.hasBeenUsed()) { + String message = String.format("Call to %s.handleUpload for path '%s' sent neither data nor an error message", + fileTransfer.getClass().getCanonicalName(), path); + throw new IOException(message); } - for (int i = 0; i < 1200; i++) { - ps.println("banana " + i); - } - ps.close(); - return null; + handle.close(); } finally { server.setInsertFakeFlushes(wasFaking); } + return handle.getError(); } private String handleDownload(String path) {
new file mode 100644 --- /dev/null +++ b/src/main/java/org/monetdb/jdbc/MonetFileTransfer.java @@ -0,0 +1,53 @@ +package org.monetdb.jdbc; + +import java.io.BufferedReader; +import java.io.IOException; +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; + +public abstract class MonetFileTransfer { + public void handleUpload(MonetUploadHandle handle, String name, boolean textMode, int offset) throws IOException { + throw new UnsupportedOperationException("This client is not prepared to handle uploads"); + } + + public static class UploadFromDirectory extends MonetFileTransfer { + private final Path root; + private final boolean utf8Encoded; + + public UploadFromDirectory(Path dir, boolean utf8Encoded) { + root = dir.toAbsolutePath().normalize(); + this.utf8Encoded = utf8Encoded; + } + + public UploadFromDirectory(String dir, boolean utf8Encoded) { + this(FileSystems.getDefault().getPath(dir), utf8Encoded); + } + + @Override + public void handleUpload(MonetUploadHandle handle, String name, boolean textMode, int offset) throws IOException { + Path path = root.resolve(name).normalize(); + if (!path.startsWith(root)) { + handle.sendError("File is not in upload directory"); + return; + } + if (!Files.isReadable(path)) { + handle.sendError("Cannot read " + name); + return; + } + if (textMode && (offset > 1 || !utf8Encoded)) { + Charset encoding = utf8Encoded ? StandardCharsets.UTF_8 : Charset.defaultCharset(); + BufferedReader reader = Files.newBufferedReader(path, encoding); + int toSkip = offset > 1 ? offset - 1 : 0; + for (int i = 0; i < toSkip; i++) { + reader.readLine(); + } + handle.uploadFrom(reader); + } else { + handle.uploadFrom(Files.newInputStream(path)); + } + } + } +}
new file mode 100644 --- /dev/null +++ b/src/main/java/org/monetdb/jdbc/MonetUploadHandle.java @@ -0,0 +1,95 @@ +package org.monetdb.jdbc; + +import org.monetdb.mcl.net.MapiSocket; + +import java.io.*; +import java.nio.charset.StandardCharsets; + +public class MonetUploadHandle { + private final MapiSocket server; + private PrintStream print = null; + private String error = null; + + MonetUploadHandle(MapiSocket server) { + this.server = server; + } + + public void sendError(String errorMessage) throws IOException { + if (error != null) { + throw new IOException("another error has already been sent: " + error); + } + error = errorMessage; + } + + public PrintStream getStream() throws IOException { + if (error != null) { + throw new IOException("Cannot send data after an error has been sent"); + } + if (print == null) { + try { + MapiSocket.UploadStream up = server.uploadStream(); + print = new PrintStream(up, false, "UTF-8"); + up.write('\n'); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("The system is guaranteed to support the UTF-8 encoding but apparently it doesn't", e); + } + } + return print; + } + + public boolean hasBeenUsed() { + return print != null || error != null; + } + + public String getError() { + return error; + } + + public void uploadFrom(InputStream inputStream) throws IOException { + OutputStream s = getStream(); + byte[] buffer = new byte[64 * 1024]; + while (true) { + int nread = inputStream.read(buffer); + if (nread < 0) { + break; + } + s.write(buffer, 0, nread); + } + } + + public void uploadFrom(BufferedReader reader, int offset) throws IOException { + // we're 1-based but also accept 0 + if (offset > 0) { + offset -= 1; + } + + for (int i = 0; i < offset; i++) { + String line = reader.readLine(); + if (line == null) { + return; + } + } + + uploadFrom(reader); + } + + public void uploadFrom(BufferedReader reader) throws IOException { + OutputStream s = getStream(); + OutputStreamWriter writer = new OutputStreamWriter(s, StandardCharsets.UTF_8); + char[] buffer = new char[64 * 1024]; + while (true) { + int nread = reader.read(buffer, 0, buffer.length); + if (nread < 0) { + break; + } + writer.write(buffer, 0, nread); + writer.close(); + } + } + + public void close() { + if (print != null) { + print.close(); + } + } +}