comparison src/main/java/org/monetdb/jdbc/MonetConnection.java @ 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 6060ca8c5c1a
children 5d524783f7b0
comparison
equal deleted inserted replaced
534:b437529144f1 535:c9d88af06d35
150 150
151 /** A cache to reduce the number of DatabaseMetaData objects created by getMetaData() to maximum 1 per connection */ 151 /** A cache to reduce the number of DatabaseMetaData objects created by getMetaData() to maximum 1 per connection */
152 private DatabaseMetaData dbmd; 152 private DatabaseMetaData dbmd;
153 153
154 /** Handlers for ON CLIENT requests */ 154 /** Handlers for ON CLIENT requests */
155 private MonetUploadHandler uploader; 155 private MonetUploadHandler uploadHandler;
156 private MonetDownloadHandler downloader; 156 private MonetDownloadHandler downloadHandler;
157 157
158 /** 158 /**
159 * Constructor of a Connection for MonetDB. At this moment the 159 * Constructor of a Connection for MonetDB. At this moment the
160 * current implementation limits itself to storing the given host, 160 * current implementation limits itself to storing the given host,
161 * database, username and password for later use by the 161 * database, username and password for later use by the
1196 public void setTypeMap(final Map<String, Class<?>> map) { 1196 public void setTypeMap(final Map<String, Class<?>> map) {
1197 typeMap = map; 1197 typeMap = map;
1198 } 1198 }
1199 1199
1200 /** 1200 /**
1201 * Registers a MonetUploader to support for example COPY ON CLIENT 1201 * Registers a {@link MonetUploadHandler} to support for example COPY ON CLIENT
1202 * 1202 *
1203 * @param uploadHandler the handler to register, or null to deregister 1203 * @param uploadHandler the handler to register, or null to deregister
1204 */ 1204 */
1205 public void setUploadHandler(MonetUploadHandler uploadHandler) { 1205 public void setUploadHandler(MonetUploadHandler uploadHandler) {
1206 this.uploader = uploadHandler; 1206 this.uploadHandler = uploadHandler;
1207 } 1207 }
1208 1208
1209 /** 1209 /**
1210 * Returns the currently registerered MonetUploader, or null 1210 * Returns the currently registerered {@link MonetUploadHandler}, or null
1211 */ 1211 */
1212 public MonetUploadHandler getUploadHandler() { 1212 public MonetUploadHandler getUploadHandler() {
1213 return uploader; 1213 return uploadHandler;
1214 } 1214 }
1215 /** 1215 /**
1216 * Registers a MonetDownloader to support for example COPY ON CLIENT 1216 * Registers a {@link MonetDownloadHandler} to support for example COPY ON CLIENT
1217 * 1217 *
1218 * @param downloadHandler the handler to register, or null to deregister 1218 * @param downloadHandler the handler to register, or null to deregister
1219 */ 1219 */
1220 public void setDownloadHandler(MonetDownloadHandler downloadHandler) { 1220 public void setDownloadHandler(MonetDownloadHandler downloadHandler) {
1221 this.downloader = downloadHandler; 1221 this.downloadHandler = downloadHandler;
1222 } 1222 }
1223 1223
1224 /** 1224 /**
1225 * Returns the currently registerered MonetDownloadHandler handler, or null 1225 * Returns the currently registerered {@link MonetDownloadHandler} handler, or null
1226 */ 1226 */
1227 public MonetDownloadHandler getDownloadHandler() { 1227 public MonetDownloadHandler getDownloadHandler() {
1228 return downloader; 1228 return downloadHandler;
1229 } 1229 }
1230 1230
1231 /** 1231 /**
1232 * Returns a string identifying this Connection to the MonetDB server. 1232 * Returns a string identifying this Connection to the MonetDB server.
1233 * 1233 *
3225 return "JDBC does not support this file transfer yet: " + transferCommand; 3225 return "JDBC does not support this file transfer yet: " + transferCommand;
3226 } 3226 }
3227 } 3227 }
3228 3228
3229 private String handleUpload(String path, boolean textMode, int offset) throws IOException { 3229 private String handleUpload(String path, boolean textMode, int offset) throws IOException {
3230 if (uploader == null) { 3230 if (uploadHandler == null) {
3231 return "No file upload handler has been registered with the JDBC driver"; 3231 return "No file upload handler has been registered with the JDBC driver";
3232 } 3232 }
3233 3233
3234 Upload handle = new Upload(server); 3234 Upload handle = new Upload(server);
3235 boolean wasFaking = server.setInsertFakePrompts(false); 3235 boolean wasFaking = server.setInsertFakePrompts(false);
3236 try { 3236 try {
3237 uploader.handleUpload(handle, path, textMode, offset); 3237 uploadHandler.handleUpload(handle, path, textMode, offset);
3238 if (!handle.hasBeenUsed()) { 3238 if (!handle.hasBeenUsed()) {
3239 String message = String.format("Call to %s.handleUpload for path '%s' sent neither data nor an error message", 3239 String message = String.format("Call to %s.handleUpload for path '%s' sent neither data nor an error message",
3240 uploader.getClass().getCanonicalName(), path); 3240 uploadHandler.getClass().getCanonicalName(), path);
3241 throw new IOException(message); 3241 throw new IOException(message);
3242 } 3242 }
3243 handle.close(); 3243 handle.close();
3244 } finally { 3244 } finally {
3245 server.setInsertFakePrompts(wasFaking); 3245 server.setInsertFakePrompts(wasFaking);
3246 } 3246 }
3247 return handle.getError(); 3247 return handle.getError();
3248 } 3248 }
3249 3249
3250 private String handleDownload(String path) throws IOException { 3250 private String handleDownload(String path) throws IOException {
3251 if (downloader == null) { 3251 if (downloadHandler == null) {
3252 return "No file download handler has been registered with the JDBC driver"; 3252 return "No file download handler has been registered with the JDBC driver";
3253 } 3253 }
3254 3254
3255 Download handle = new Download(server); 3255 Download handle = new Download(server);
3256 downloader.handleDownload(handle, path, true); 3256 downloadHandler.handleDownload(handle, path, true);
3257 if (!handle.hasBeenUsed()) { 3257 if (!handle.hasBeenUsed()) {
3258 String message = String.format("Call to %s.handleDownload for path '%s' sent neither data nor an error message", 3258 String message = String.format("Call to %s.handleDownload for path '%s' sent neither data nor an error message",
3259 downloader.getClass().getCanonicalName(), path); 3259 downloadHandler.getClass().getCanonicalName(), path);
3260 throw new IOException(message); 3260 throw new IOException(message);
3261 } 3261 }
3262 handle.close(); 3262 handle.close();
3263 return handle.getError(); 3263 return handle.getError();
3264 } 3264 }
3265 3265
3266 /**
3267 * Handle passed to {@link MonetUploadHandler} to allow communication with the server
3268 */
3266 public static class Upload { 3269 public static class Upload {
3267 private final MapiSocket server; 3270 private final MapiSocket server;
3268 private PrintStream print = null; 3271 private PrintStream print = null;
3269 private String error = null; 3272 private String error = null;
3270 private int customChunkSize = -1; 3273 private int customChunkSize = -1;
3271 3274
3272 Upload(MapiSocket server) { 3275 Upload(MapiSocket server) {
3273 this.server = server; 3276 this.server = server;
3274 } 3277 }
3275 3278
3279 /**
3280 * Send an error message to the server
3281 *
3282 * The server will generally let the currently executing statement fail
3283 * with this error message. The connection will remain usable.
3284 *
3285 * This method can only be sent if no data has been sent to the server
3286 * yet. After data has been sent, you can still throw an
3287 * {@link IOException} but this will terminate the connection.
3288 * @param errorMessage
3289 * @throws IOException
3290 */
3276 public void sendError(String errorMessage) throws IOException { 3291 public void sendError(String errorMessage) throws IOException {
3277 if (error != null) { 3292 if (error != null) {
3278 throw new IOException("another error has already been sent: " + error); 3293 throw new IOException("another error has already been sent: " + error);
3279 } 3294 }
3280 error = errorMessage; 3295 error = errorMessage;
3281 } 3296 }
3282 3297
3298 /**
3299 * After every {@code chunkSize} bytes, the server gets the opportunity to
3300 * terminate the upload.
3301 * @param chunkSize
3302 */
3283 public void setChunkSize(int chunkSize) { 3303 public void setChunkSize(int chunkSize) {
3284 this.customChunkSize = chunkSize; 3304 this.customChunkSize = chunkSize;
3285 } 3305 }
3286 3306
3307 /**
3308 * Get a {@link PrintStream} to write data to.
3309 *
3310 * For text mode uploads, the data MUST be validly UTF-8 encoded.
3311 * @return
3312 * @throws IOException
3313 */
3287 public PrintStream getStream() throws IOException { 3314 public PrintStream getStream() throws IOException {
3288 if (error != null) { 3315 if (error != null) {
3289 throw new IOException("Cannot send data after an error has been sent"); 3316 throw new IOException("Cannot send data after an error has been sent");
3290 } 3317 }
3291 if (print == null) { 3318 if (print == null) {
3298 } 3325 }
3299 } 3326 }
3300 return print; 3327 return print;
3301 } 3328 }
3302 3329
3330 /**
3331 * Returns true if data or an error has been sent.
3332 * @return
3333 */
3303 public boolean hasBeenUsed() { 3334 public boolean hasBeenUsed() {
3304 return print != null || error != null; 3335 return print != null || error != null;
3305 } 3336 }
3306 3337
3338 /**
3339 * Get the error that was sent, if any
3340 * @return
3341 */
3307 public String getError() { 3342 public String getError() {
3308 return error; 3343 return error;
3309 } 3344 }
3310 3345
3346 /**
3347 * Read from the given input stream and write it to the server.
3348 *
3349 * For text mode uploads, the data MUST be validly UTF-8 encoded.
3350 * @param inputStream
3351 * @throws IOException
3352 */
3311 public void uploadFrom(InputStream inputStream) throws IOException { 3353 public void uploadFrom(InputStream inputStream) throws IOException {
3312 OutputStream s = getStream(); 3354 OutputStream s = getStream();
3313 byte[] buffer = new byte[64 * 1024]; 3355 byte[] buffer = new byte[64 * 1024];
3314 while (true) { 3356 while (true) {
3315 int nread = inputStream.read(buffer); 3357 int nread = inputStream.read(buffer);
3318 } 3360 }
3319 s.write(buffer, 0, nread); 3361 s.write(buffer, 0, nread);
3320 } 3362 }
3321 } 3363 }
3322 3364
3365 /**
3366 * Read data from the given buffered reader and send it to the server
3367 * @param reader reader to read from
3368 * @param offset start uploading at line {@code offset}. Value 0 and 1
3369 * both mean upload the whole file, value 2 means skip the first line, etc.q
3370 * @throws IOException
3371 */
3323 public void uploadFrom(BufferedReader reader, int offset) throws IOException { 3372 public void uploadFrom(BufferedReader reader, int offset) throws IOException {
3324 // we're 1-based but also accept 0 3373 // we're 1-based but also accept 0
3325 if (offset > 0) { 3374 if (offset > 0) {
3326 offset -= 1; 3375 offset -= 1;
3327 } 3376 }
3334 } 3383 }
3335 3384
3336 uploadFrom(reader); 3385 uploadFrom(reader);
3337 } 3386 }
3338 3387
3388
3389 /**
3390 * Read data from the given buffered reader and send it to the server
3391 * @param reader reader to read from
3392 * @throws IOException
3393 */
3339 public void uploadFrom(Reader reader) throws IOException { 3394 public void uploadFrom(Reader reader) throws IOException {
3340 OutputStream s = getStream(); 3395 OutputStream s = getStream();
3341 OutputStreamWriter writer = new OutputStreamWriter(s, StandardCharsets.UTF_8); 3396 OutputStreamWriter writer = new OutputStreamWriter(s, StandardCharsets.UTF_8);
3342 char[] buffer = new char[64 * 1024]; 3397 char[] buffer = new char[64 * 1024];
3343 while (true) { 3398 while (true) {
3355 print.close(); 3410 print.close();
3356 } 3411 }
3357 } 3412 }
3358 } 3413 }
3359 3414
3415 /**
3416 * Handle passed to {@link MonetDownloadHandler} to allow communication with the server
3417 */
3360 public static class Download { 3418 public static class Download {
3361 private final MapiSocket server; 3419 private final MapiSocket server;
3362 private MapiSocket.DownloadStream stream = null; 3420 private MapiSocket.DownloadStream stream = null;
3363 private String error = null; 3421 private String error = null;
3364 3422
3366 3424
3367 Download(MapiSocket server) { 3425 Download(MapiSocket server) {
3368 this.server = server; 3426 this.server = server;
3369 } 3427 }
3370 3428
3429 /**
3430 * Send an error message to the server
3431 *
3432 * The server will generally let the currently executing statement fail
3433 * with this error message. The connection will remain usable.
3434 *
3435 * This method can only be sent if no data has been received from the server
3436 * yet. After data has been received, you can still throw an
3437 * {@link IOException} but this will terminate the connection.
3438 *
3439 * Note: as of MonetDB version Jul2021 the server always terminates the connection
3440 * when this error is used. This will probably change in the future.
3441 * @param errorMessage
3442 * @throws IOException
3443 */
3371 public void sendError(String errorMessage) throws IOException { 3444 public void sendError(String errorMessage) throws IOException {
3372 if (error != null) { 3445 if (error != null) {
3373 throw new IOException("another error has already been sent: " + error); 3446 throw new IOException("another error has already been sent: " + error);
3374 } 3447 }
3375 error = errorMessage; 3448 error = errorMessage;
3376 } 3449 }
3377 3450
3451 /**
3452 * Get an {@link InputStream} to read data from.
3453 *
3454 * Textual data is UTF-8 encoded.
3455 * @return
3456 * @throws IOException
3457 */
3378 public InputStream getStream() throws IOException { 3458 public InputStream getStream() throws IOException {
3379 if (error != null) { 3459 if (error != null) {
3380 throw new IOException("cannot receive data after error has been sent"); 3460 throw new IOException("cannot receive data after error has been sent");
3381 } 3461 }
3382 if (stream == null) { 3462 if (stream == null) {
3384 server.getOutputStream().flush(); 3464 server.getOutputStream().flush();
3385 } 3465 }
3386 return stream; 3466 return stream;
3387 } 3467 }
3388 3468
3469 /**
3470 * Write the data from the server to the given {@link OutputStream}.
3471 * @param stream
3472 * @throws IOException
3473 */
3389 public void downloadTo(OutputStream stream) throws IOException { 3474 public void downloadTo(OutputStream stream) throws IOException {
3390 InputStream s = getStream(); 3475 InputStream s = getStream();
3391 byte[] buffer = new byte[65536]; 3476 byte[] buffer = new byte[65536];
3392 while (true) { 3477 while (true) {
3393 int nread = s.read(buffer); 3478 int nread = s.read(buffer);
3395 break; 3480 break;
3396 stream.write(buffer, 0, nread); 3481 stream.write(buffer, 0, nread);
3397 } 3482 }
3398 } 3483 }
3399 3484
3485 /**
3486 * Returns true if data has been received or an error has been sent.
3487 * @return
3488 */
3489
3400 public boolean hasBeenUsed() { 3490 public boolean hasBeenUsed() {
3401 return error != null || stream != null; 3491 return error != null || stream != null;
3402 } 3492 }
3493
3494 /**
3495 * Get the error that was sent, if any
3496 * @return
3497 */
3403 3498
3404 public String getError() { 3499 public String getError() {
3405 return error; 3500 return error;
3406 } 3501 }
3407 public void close() throws IOException { 3502 public void close() throws IOException {
3411 if (stream != null) { 3506 if (stream != null) {
3412 stream.close(); 3507 stream.close();
3413 } 3508 }
3414 closed = true; 3509 closed = true;
3415 } 3510 }
3416
3417 } 3511 }
3418 } 3512 }