From 021054e428566fa0a81c0ebada457b8e50511151 Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Fri, 23 Jan 2015 15:54:23 +0100 Subject: [PATCH] [UWS,TAP] Fix UPLOAD bug (the last line of the table to upload was ignored). Fix an improbable NullPointerException in the MultipartParser. --- src/tap/data/VOTableIterator.java | 129 ++++++++++--------- src/tap/parameters/DALIUpload.java | 2 +- src/uws/service/request/MultipartParser.java | 2 + 3 files changed, 72 insertions(+), 61 deletions(-) diff --git a/src/tap/data/VOTableIterator.java b/src/tap/data/VOTableIterator.java index c7bd03b..e60d5a8 100644 --- a/src/tap/data/VOTableIterator.java +++ b/src/tap/data/VOTableIterator.java @@ -51,17 +51,17 @@ public class VOTableIterator implements TableIterator { * <p><i>Note: this may be NULL after the metadata has been read if an error occurred while performing the conversion. * In this case, metaError contains this error.</> */ private TAPColumn[] meta = null; - + /** The error which happened while converting the StarTable metadata into TAP metadata. */ private DataReadException metaError = null; - + /** The last accepted row. */ private Object[] pendingRow = null; - + /** Flag meaning that the end of the stream has been reached * OR if the VOTable reading should be stopped before reading more rows. */ private boolean endReached = false; - + /** * <p>Stop nicely reading the VOTable.</p> * @@ -74,17 +74,17 @@ public class VOTableIterator implements TableIterator { endReached = true; notifyAll(); } - + @Override - public synchronized void acceptMetadata(final StarTable metaTable) throws TableFormatException { + public synchronized void acceptMetadata(final StarTable metaTable) throws TableFormatException{ try{ // Convert the StartTable metadata into TAP metadata: meta = extractColMeta(metaTable); - + }catch(DataReadException dre){ // Save the error ; this error will be throw when a call to getMetadata() will be done: metaError = dre; - + }finally{ // Free all waiting threads: notifyAll(); @@ -92,18 +92,18 @@ public class VOTableIterator implements TableIterator { } @Override - public synchronized void acceptRow(final Object[] row) throws IOException { + public synchronized void acceptRow(final Object[] row) throws IOException{ try{ // Wait until the last accepted row has been consumed: while(!endReached && pendingRow != null) wait(); - + /* If the end has been reached, this is not normal * (because endRows() is always called after acceptRow()...so, it means the iteration has been aborted before the end) * and so the stream reading should be interrupted: */ if (endReached) throw new IOException("Streaming aborted!"); - + // Otherwise, keep the given row: pendingRow = row; @@ -113,13 +113,13 @@ public class VOTableIterator implements TableIterator { * ...which should then mean that the end of the stream has been reached. */ if (pendingRow == null) endReached = true; - + }catch(InterruptedException ie){ /* If the thread has been interrupted, set this TableSink in a state similar to * when the end of the stream has been reached: */ pendingRow = null; endReached = true; - + }finally{ // In all cases, all waiting threads must be freed: notifyAll(); @@ -127,15 +127,23 @@ public class VOTableIterator implements TableIterator { } @Override - public synchronized void endRows() throws IOException { - // No more rows are available: - pendingRow = null; - // Set the END flag: - endReached = true; - // Notify all waiting threads that the end has been reached: - notifyAll(); + public synchronized void endRows() throws IOException{ + try{ + // Wait until the last accepted row has been consumed: + while(!endReached && pendingRow != null) + wait(); + }catch(InterruptedException ie){ + /* Nothing to do in particular ; the end of the stream will be set anyway. */ + }finally{ + // No more rows are available: + pendingRow = null; + // Set the END flag: + endReached = true; + // Notify all waiting threads that the end has been reached: + notifyAll(); + } } - + /** * <p>Get the metadata found in the VOTable.</p> * @@ -154,14 +162,14 @@ public class VOTableIterator implements TableIterator { // Wait until metadata are available, or if an error has occurred while accepting them: while(metaError == null && meta == null) wait(); - + // If there was an error while interpreting the accepted metadata, throw it: if (metaError != null) throw metaError; - + // Otherwise, just return the metadata: return meta; - + }catch(InterruptedException ie){ /* If the thread has been interrupted, set this TableSink in a state similar to * when the end of the stream has been reached: */ @@ -169,13 +177,13 @@ public class VOTableIterator implements TableIterator { /* Return the metadata ; * NULL will be returned if the interruption has occurred before the real reading of the VOTable metadata: */ return meta; - + }finally{ // In all cases, the waiting threads must be freed: notifyAll(); } } - + /** * <p>Get the last accepted row.</p> * @@ -186,35 +194,35 @@ public class VOTableIterator implements TableIterator { * * @return */ - public synchronized Object[] getRow() { + public synchronized Object[] getRow(){ try{ // Wait until a row has been accepted or the end has been reached: while(!endReached && pendingRow == null) wait(); - + // If there is no more rows, just return NULL (meaning for the called "end of stream"): - if (endReached) + if (endReached && pendingRow == null) return null; - + /* Otherwise, reset pendingRow to NULL in order to enable the reading of the next row, * and finally return the last accepted row: */ Object[] row = pendingRow; pendingRow = null; return row; - + }catch(InterruptedException ie){ /* If the thread has been interrupted, set this TableSink in a state similar to * when the end of the stream has been reached: */ endReached = true; // Return NULL, meaning the end of the stream has been reached: return null; - - }finally { + + }finally{ // In all cases, the waiting threads must be freed: notifyAll(); } } - + /** * Extract an array of {@link TAPColumn} objects. Each corresponds to one of the columns listed in the given table, * and so corresponds to the metadata of a column. @@ -307,9 +315,9 @@ public class VOTableIterator implements TableIterator { // Build the VOTable type: return new VotType(votdatatype, arraysize, xtype); } - + } - + /** Stream containing the VOTable on which this {@link TableIterator} is iterating. */ protected final InputStream input; /** The StarTable consumer which is used to iterate on each row. */ @@ -319,14 +327,14 @@ public class VOTableIterator implements TableIterator { protected boolean iterationStarted = false; /** Indicate whether the last row has already been reached. */ protected boolean endReached = false; - + /** The last read row. Column iteration is done on this array. */ protected Object[] row; /** Index of the last read column (=0 just after {@link #nextRow()} and before {@link #nextCol()}, ={@link #nbColumns} after the last column has been read). */ protected int indCol = -1; /** Number of columns available according to the metadata. */ protected int nbCol = 0; - + /** * Build a TableIterator able to read rows and columns inside the given VOTable input stream. * @@ -340,7 +348,7 @@ public class VOTableIterator implements TableIterator { if (input == null) throw new NullPointerException("Missing VOTable document input stream over which to iterate!"); this.input = input; - + try{ // Set the VOTable builder/interpreter: @@ -348,19 +356,20 @@ public class VOTableIterator implements TableIterator { // Build the TableSink to use: sink = new StreamVOTableSink(); - + // Initiate the stream process: - Thread streamThread = new Thread() { - public void run() { - try{ - tb.streamStarTable(input, sink, null); - }catch(IOException e) { - if (e.getMessage() != null && !e.getMessage().equals("Reading interrupted!")) - e.printStackTrace(); - } - } - }; - streamThread.start(); + Thread streamThread = new Thread(){ + @Override + public void run(){ + try{ + tb.streamStarTable(input, sink, null); + }catch(IOException e){ + if (e.getMessage() != null && !e.getMessage().equals("Reading interrupted!")) + e.printStackTrace(); + } + } + }; + streamThread.start(); }catch(Exception ex){ throw new DataReadException("Unable to parse/read the given VOTable input stream!", ex); @@ -368,33 +377,33 @@ public class VOTableIterator implements TableIterator { } @Override - public TAPColumn[] getMetadata() throws DataReadException { + public TAPColumn[] getMetadata() throws DataReadException{ return sink.getMeta(); } @Override - public boolean nextRow() throws DataReadException { + public boolean nextRow() throws DataReadException{ // If no more rows, return false directly: if (endReached) return false; - + // Fetch the row: row = sink.getRow(); - + // Reset the column iteration: if (!iterationStarted){ iterationStarted = true; nbCol = sink.getMeta().length; } indCol = 0; - + // Tells whether there is more rows or not: endReached = (row == null); return !endReached; } @Override - public boolean hasNextCol() throws IllegalStateException, DataReadException { + public boolean hasNextCol() throws IllegalStateException, DataReadException{ // Check the read state: checkReadState(); @@ -403,7 +412,7 @@ public class VOTableIterator implements TableIterator { } @Override - public Object nextCol() throws NoSuchElementException, IllegalStateException, DataReadException { + public Object nextCol() throws NoSuchElementException, IllegalStateException, DataReadException{ // Check the read state and ensure there is still at least one column to read: if (!hasNextCol()) throw new NoSuchElementException("No more field to read!"); @@ -413,7 +422,7 @@ public class VOTableIterator implements TableIterator { } @Override - public DBType getColType() throws IllegalStateException, DataReadException { + public DBType getColType() throws IllegalStateException, DataReadException{ // Basically check the read state (for rows iteration): checkReadState(); @@ -428,7 +437,7 @@ public class VOTableIterator implements TableIterator { } @Override - public void close() throws DataReadException { + public void close() throws DataReadException{ endReached = true; sink.stop(); // input.close(); // in case sink.stop() is not enough to stop the VOTable reading! diff --git a/src/tap/parameters/DALIUpload.java b/src/tap/parameters/DALIUpload.java index 71694b8..e55c74a 100644 --- a/src/tap/parameters/DALIUpload.java +++ b/src/tap/parameters/DALIUpload.java @@ -512,7 +512,7 @@ public class DALIUpload { // Check the label: if (!parts[0].matches("[a-zA-Z][a-zA-Z0-9_]*")) - throw new TAPException("Wrong uploaded item name syntax: \"" + parts[0] + "\"! An uploaded item must have a label with the syntax: [a-zA-Z][a-zA-Z0-9_]*.", UWSException.BAD_REQUEST); + throw new TAPException("Wrong uploaded item name syntax: \"" + parts[0] + "\"! An uploaded item must have a label respecting the 'regular_identifier' production of ADQL 2.0 (regular expression: [a-zA-Z][a-zA-Z0-9_]*).", UWSException.BAD_REQUEST); // Check the URI: else if (!parts[1].matches("[a-zA-Z][a-zA-Z0-9\\+\\.\\-]*:.+")) throw new TAPException("Bad URI syntax: \"" + parts[1] + "\"! A URI must start with: \"<scheme>:\", where <scheme>=\"[a-zA-Z][a-zA-Z0-9+.-]*\".", UWSException.BAD_REQUEST); diff --git a/src/uws/service/request/MultipartParser.java b/src/uws/service/request/MultipartParser.java index 5d4fc6a..a6760ac 100644 --- a/src/uws/service/request/MultipartParser.java +++ b/src/uws/service/request/MultipartParser.java @@ -166,6 +166,8 @@ public class MultipartParser implements RequestParser { throw new UWSException(UWSException.BAD_REQUEST, "Uploads are not allowed by this service!"); while(e.hasMoreElements()){ param = e.nextElement(); + if (multipart.getFile(param) == null) + continue; /* * TODO !!!POSSIBLE ISSUE!!! -- GitLab