Mercurial > hg > monetdb-java
changeset 535:c9d88af06d35 onclient
Javadoc and some minor changes
author | Joeri van Ruth <joeri.van.ruth@monetdbsolutions.com> |
---|---|
date | Fri, 27 Aug 2021 16:13:54 +0200 (2021-08-27) |
parents | b437529144f1 |
children | daf6a3b828f9 |
files | src/main/java/org/monetdb/jdbc/MonetConnection.java src/main/java/org/monetdb/jdbc/MonetDownloadHandler.java src/main/java/org/monetdb/jdbc/MonetUploadHandler.java src/main/java/org/monetdb/mcl/io/LineType.java src/main/java/org/monetdb/mcl/net/MapiSocket.java src/main/java/org/monetdb/util/FileTransferHandler.java tests/build.xml |
diffstat | 7 files changed, 212 insertions(+), 23 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main/java/org/monetdb/jdbc/MonetConnection.java +++ b/src/main/java/org/monetdb/jdbc/MonetConnection.java @@ -152,8 +152,8 @@ public class MonetConnection private DatabaseMetaData dbmd; /** Handlers for ON CLIENT requests */ - private MonetUploadHandler uploader; - private MonetDownloadHandler downloader; + private MonetUploadHandler uploadHandler; + private MonetDownloadHandler downloadHandler; /** * Constructor of a Connection for MonetDB. At this moment the @@ -1198,34 +1198,34 @@ public class MonetConnection } /** - * Registers a MonetUploader to support for example COPY ON CLIENT + * Registers a {@link MonetUploadHandler} to support for example COPY ON CLIENT * * @param uploadHandler the handler to register, or null to deregister */ public void setUploadHandler(MonetUploadHandler uploadHandler) { - this.uploader = uploadHandler; + this.uploadHandler = uploadHandler; } /** - * Returns the currently registerered MonetUploader, or null + * Returns the currently registerered {@link MonetUploadHandler}, or null */ public MonetUploadHandler getUploadHandler() { - return uploader; + return uploadHandler; } /** - * Registers a MonetDownloader to support for example COPY ON CLIENT + * Registers a {@link MonetDownloadHandler} to support for example COPY ON CLIENT * * @param downloadHandler the handler to register, or null to deregister */ public void setDownloadHandler(MonetDownloadHandler downloadHandler) { - this.downloader = downloadHandler; + this.downloadHandler = downloadHandler; } /** - * Returns the currently registerered MonetDownloadHandler handler, or null + * Returns the currently registerered {@link MonetDownloadHandler} handler, or null */ public MonetDownloadHandler getDownloadHandler() { - return downloader; + return downloadHandler; } /** @@ -3227,17 +3227,17 @@ public class MonetConnection } private String handleUpload(String path, boolean textMode, int offset) throws IOException { - if (uploader == null) { + if (uploadHandler == null) { return "No file upload handler has been registered with the JDBC driver"; } Upload handle = new Upload(server); boolean wasFaking = server.setInsertFakePrompts(false); try { - uploader.handleUpload(handle, path, textMode, offset); + uploadHandler.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", - uploader.getClass().getCanonicalName(), path); + uploadHandler.getClass().getCanonicalName(), path); throw new IOException(message); } handle.close(); @@ -3248,21 +3248,24 @@ public class MonetConnection } private String handleDownload(String path) throws IOException { - if (downloader == null) { + if (downloadHandler == null) { return "No file download handler has been registered with the JDBC driver"; } Download handle = new Download(server); - downloader.handleDownload(handle, path, true); + downloadHandler.handleDownload(handle, path, true); if (!handle.hasBeenUsed()) { String message = String.format("Call to %s.handleDownload for path '%s' sent neither data nor an error message", - downloader.getClass().getCanonicalName(), path); + downloadHandler.getClass().getCanonicalName(), path); throw new IOException(message); } handle.close(); return handle.getError(); } + /** + * Handle passed to {@link MonetUploadHandler} to allow communication with the server + */ public static class Upload { private final MapiSocket server; private PrintStream print = null; @@ -3273,6 +3276,18 @@ public class MonetConnection this.server = server; } + /** + * Send an error message to the server + * + * The server will generally let the currently executing statement fail + * with this error message. The connection will remain usable. + * + * This method can only be sent if no data has been sent to the server + * yet. After data has been sent, you can still throw an + * {@link IOException} but this will terminate the connection. + * @param errorMessage + * @throws IOException + */ public void sendError(String errorMessage) throws IOException { if (error != null) { throw new IOException("another error has already been sent: " + error); @@ -3280,10 +3295,22 @@ public class MonetConnection error = errorMessage; } + /** + * After every {@code chunkSize} bytes, the server gets the opportunity to + * terminate the upload. + * @param chunkSize + */ public void setChunkSize(int chunkSize) { this.customChunkSize = chunkSize; } + /** + * Get a {@link PrintStream} to write data to. + * + * For text mode uploads, the data MUST be validly UTF-8 encoded. + * @return + * @throws IOException + */ public PrintStream getStream() throws IOException { if (error != null) { throw new IOException("Cannot send data after an error has been sent"); @@ -3300,14 +3327,29 @@ public class MonetConnection return print; } + /** + * Returns true if data or an error has been sent. + * @return + */ public boolean hasBeenUsed() { return print != null || error != null; } + /** + * Get the error that was sent, if any + * @return + */ public String getError() { return error; } + /** + * Read from the given input stream and write it to the server. + * + * For text mode uploads, the data MUST be validly UTF-8 encoded. + * @param inputStream + * @throws IOException + */ public void uploadFrom(InputStream inputStream) throws IOException { OutputStream s = getStream(); byte[] buffer = new byte[64 * 1024]; @@ -3320,6 +3362,13 @@ public class MonetConnection } } + /** + * Read data from the given buffered reader and send it to the server + * @param reader reader to read from + * @param offset start uploading at line {@code offset}. Value 0 and 1 + * both mean upload the whole file, value 2 means skip the first line, etc.q + * @throws IOException + */ public void uploadFrom(BufferedReader reader, int offset) throws IOException { // we're 1-based but also accept 0 if (offset > 0) { @@ -3336,6 +3385,12 @@ public class MonetConnection uploadFrom(reader); } + + /** + * Read data from the given buffered reader and send it to the server + * @param reader reader to read from + * @throws IOException + */ public void uploadFrom(Reader reader) throws IOException { OutputStream s = getStream(); OutputStreamWriter writer = new OutputStreamWriter(s, StandardCharsets.UTF_8); @@ -3357,6 +3412,9 @@ public class MonetConnection } } + /** + * Handle passed to {@link MonetDownloadHandler} to allow communication with the server + */ public static class Download { private final MapiSocket server; private MapiSocket.DownloadStream stream = null; @@ -3368,6 +3426,21 @@ public class MonetConnection this.server = server; } + /** + * Send an error message to the server + * + * The server will generally let the currently executing statement fail + * with this error message. The connection will remain usable. + * + * This method can only be sent if no data has been received from the server + * yet. After data has been received, you can still throw an + * {@link IOException} but this will terminate the connection. + * + * Note: as of MonetDB version Jul2021 the server always terminates the connection + * when this error is used. This will probably change in the future. + * @param errorMessage + * @throws IOException + */ public void sendError(String errorMessage) throws IOException { if (error != null) { throw new IOException("another error has already been sent: " + error); @@ -3375,6 +3448,13 @@ public class MonetConnection error = errorMessage; } + /** + * Get an {@link InputStream} to read data from. + * + * Textual data is UTF-8 encoded. + * @return + * @throws IOException + */ public InputStream getStream() throws IOException { if (error != null) { throw new IOException("cannot receive data after error has been sent"); @@ -3386,6 +3466,11 @@ public class MonetConnection return stream; } + /** + * Write the data from the server to the given {@link OutputStream}. + * @param stream + * @throws IOException + */ public void downloadTo(OutputStream stream) throws IOException { InputStream s = getStream(); byte[] buffer = new byte[65536]; @@ -3397,10 +3482,20 @@ public class MonetConnection } } + /** + * Returns true if data has been received or an error has been sent. + * @return + */ + public boolean hasBeenUsed() { return error != null || stream != null; } + /** + * Get the error that was sent, if any + * @return + */ + public String getError() { return error; } @@ -3413,6 +3508,5 @@ public class MonetConnection } closed = true; } - } }
--- a/src/main/java/org/monetdb/jdbc/MonetDownloadHandler.java +++ b/src/main/java/org/monetdb/jdbc/MonetDownloadHandler.java @@ -2,6 +2,21 @@ package org.monetdb.jdbc; import java.io.IOException; +/** + * Callback for receiving files with COPY ON CLIENT + * + * To be registered with {@link MonetConnection#setDownloadHandler(MonetDownloadHandler)} + */ public interface MonetDownloadHandler { + /** + * Called if the server sends a request to write a file. + * + * Use the given handle to send data or errors to the server. + * + * @param handle Handle to communicate with the server + * @param name Name of the file the server would like to write. Make sure to validate this before writing to + * the file system + * @param textMode Whether this is text or binary data. + */ void handleDownload(MonetConnection.Download handle, String name, boolean textMode) throws IOException; -} +} \ No newline at end of file
--- a/src/main/java/org/monetdb/jdbc/MonetUploadHandler.java +++ b/src/main/java/org/monetdb/jdbc/MonetUploadHandler.java @@ -2,6 +2,22 @@ package org.monetdb.jdbc; import java.io.IOException; +/** + * Callback for sending files for COPY ON CLIENT + * + * To be registered with {@link MonetConnection#setUploadHandler(MonetUploadHandler)} + */ + public interface MonetUploadHandler { + /** + * Called if the server sends a request to write a file. + * + * Use the given handle to receive data or send errors to the server. + * + * @param handle Handle to communicate with the server + * @param name Name of the file the server would like to read. Make sure to validate this before reading from + * the file system + * @param textMode Whether this is text or binary data. + */ void handleUpload(MonetConnection.Upload handle, String name, boolean textMode, int offset) throws IOException; -} +} \ No newline at end of file
--- a/src/main/java/org/monetdb/mcl/io/LineType.java +++ b/src/main/java/org/monetdb/mcl/io/LineType.java @@ -1,5 +1,8 @@ package org.monetdb.mcl.io; +/** + * Enumeration of the various message types used in the MAPI protocol. + */ public enum LineType { /** "there is currently no line", or the the type is unknown is represented by UNKNOWN */ UNKNOWN(null), @@ -41,6 +44,9 @@ public enum LineType { return this.bytes; } + /** + * Look at a mapi message and decide the LineType + */ public static final LineType classify(String line) { if (line == null) { return UNKNOWN; @@ -54,6 +60,9 @@ public enum LineType { } } + /** + * Look at a mapi message and decide the LineType + */ public static final LineType classify(byte[] line) { if (line == null) { return UNKNOWN;
--- a/src/main/java/org/monetdb/mcl/net/MapiSocket.java +++ b/src/main/java/org/monetdb/mcl/net/MapiSocket.java @@ -720,6 +720,9 @@ public class MapiSocket { /* cannot (yet return handshakeOptions; } + /** + * For internal use + */ public boolean setInsertFakePrompts(boolean b) { return fromMonet.setInsertFakePrompts(b); } @@ -1081,6 +1084,9 @@ public class MapiSocket { /* cannot (yet return n; } + /** + * For internal use + */ Raw getRaw() { return new Raw(); } @@ -1164,14 +1170,31 @@ public class MapiSocket { /* cannot (yet } } + /** + * Return an UploadStream for use with for example COPY FROM filename ON CLIENT. + * + * Building block for {@link org.monetdb.jdbc.MonetUploadHandler}. + * @param chunkSize chunk size for the upload stream + */ public UploadStream uploadStream(int chunkSize) { return new UploadStream(chunkSize); } + /** + * Return an UploadStream for use with for example COPY FROM filename ON CLIENT. + * + * Building block for {@link org.monetdb.jdbc.MonetUploadHandler}. + */ public UploadStream uploadStream() { return new UploadStream(); } + /** + * Return a DownloadStream for use with for example COPY INTO filename ON CLIENT + * + * Building block for {@link org.monetdb.jdbc.MonetDownloadHandler}. + * @return + */ public DownloadStream downloadStream() { return new DownloadStream(fromMonet.getRaw(), toMonet); } @@ -1190,6 +1213,14 @@ public class MapiSocket { /* cannot (yet super.finalize(); } + /** + * Stream of data sent to the server + * + * Building block for {@link org.monetdb.jdbc.MonetUploadHandler}. + * + * An UploadStream has a chunk size. Every chunk size bytes, the server gets + * the opportunity to abort the upload. + */ public class UploadStream extends FilterOutputStream { public final static int DEFAULT_CHUNK_SIZE = 1024 * 1024; private final int chunkSize; @@ -1198,6 +1229,7 @@ public class MapiSocket { /* cannot (yet private int chunkLeft; private byte[] promptBuffer; + /** Create an UploadStream with the given chunk size */ UploadStream(int chunkSize) { super(toMonet); if (chunkSize <= 0) { @@ -1210,6 +1242,7 @@ public class MapiSocket { /* cannot (yet chunkLeft = this.chunkSize; } + /** Create an UploadStream with the default chunk size */ UploadStream() { this(DEFAULT_CHUNK_SIZE); } @@ -1323,6 +1356,11 @@ public class MapiSocket { /* cannot (yet } } + /** + * Stream of data received from the server + * + * Building block for {@link org.monetdb.jdbc.MonetDownloadHandler}. + */ public static class DownloadStream extends InputStream { private final BlockInputStream.Raw rawIn; @@ -1390,6 +1428,4 @@ public class MapiSocket { /* cannot (yet return off - origOff; } } - - }
--- a/src/main/java/org/monetdb/util/FileTransferHandler.java +++ b/src/main/java/org/monetdb/util/FileTransferHandler.java @@ -14,15 +14,34 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +/** + * Sample implement of ON CLIENT handling + * + * Can be registered with {@link MonetConnection#setUploadHandler(MonetUploadHandler)} + * and {@link MonetConnection#setDownloadHandler(MonetDownloadHandler)}. + * Implements uploads and downloads by reading and writing files on the file system. + */ public class FileTransferHandler implements MonetUploadHandler, MonetDownloadHandler { private final Path root; private final boolean utf8Encoded; + /** + * Create a new FileTransferHandler which serves the given directory. + * + * @param dir directory to read and write files from + * @param utf8Encoded set this to true if all files in the directory are known to be utf-8 encoded. + */ public FileTransferHandler(Path dir, boolean utf8Encoded) { root = dir.toAbsolutePath().normalize(); this.utf8Encoded = utf8Encoded; } + /** + * Create a new FileTransferHandler which serves the given directory. + * + * @param dir directory to read and write files from + * @param utf8Encoded set this to true if all files in the directory are known to be utf-8 encoded. + */ public FileTransferHandler(String dir, boolean utf8Encoded) { this(FileSystems.getDefault().getPath(dir), utf8Encoded); } @@ -57,6 +76,5 @@ public class FileTransferHandler impleme return; } OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW); - } -} +} \ No newline at end of file
--- a/tests/build.xml +++ b/tests/build.xml @@ -106,6 +106,7 @@ Copyright 1997 - July 2008 CWI, August 2 <target name="test_class" depends="compile,jdbc"> <echo message="Testing class ${test.class}" /> + <!-- fork="true" allows the test program to call System.exit() --> <java classname="${test.class}" failonerror="true" fork="true"> <classpath> <pathelement path="${builddir}" />