Skip to content
Snippets Groups Projects
Commit f912c20a authored by Grégory Mantelet's avatar Grégory Mantelet
Browse files

[TAP] Fix cancellation of a text formatting process.

Before this fix, cancelling a TAP job (async or not) which was formatting the
result in ASCII may failed, especially for large results. This was due to a
non interruptible alignment process. This process is now checking whether a
cancellation has been requested before formatting a new result line/row ; if so,
the process is immediately stopped and the job can be cleaning declared as
aborted.
parent fb4cbcfd
No related branches found
No related tags found
No related merge requests found
...@@ -16,20 +16,25 @@ package cds.util; ...@@ -16,20 +16,25 @@ package cds.util;
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>.
* *
* Copyright 2012 - UDS/Centre de Données astronomiques de Strasbourg (CDS) * Copyright 2012-2018 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
*/ */
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* An object of this class manages an ascii table: it receives lines to add, * An object of this class manages an ASCII table: it receives lines to add,
* made of columns separated by a given separator char. Columns can be aligned * made of columns separated by a given separator char. Columns can be aligned
* (RIGHT, LEFT or CENTER) before display * (RIGHT, LEFT or CENTER) before display
*
* @author Marc Wenger/CDS * @author Marc Wenger/CDS
* @version 1.0 May 2008 Creation<br> * @author Gr&eacute;gory Mantelet/CDS
* @version 1.1 May 2008 Fix a bug: lines are kept without a newline at the end<br> *
* @version 1.2 Jun 2008 Add a toString method (items aligned).<br> * @version 1.0 May 2008 MW Creation
* @version 1.1 May 2008 MW Fix a bug: lines are kept without a newline at the
* end
* @version 1.2 Jun 2008 MW Add a toString method (items aligned) ;
* Fix a bug in align() when the last line is not full * Fix a bug in align() when the last line is not full
* @version 1.3 Nov 2018 GM Make the alignment process - align() - interruptible
*/ */
public class AsciiTable { public class AsciiTable {
public static final int LEFT = 0; public static final int LEFT = 0;
...@@ -86,8 +91,8 @@ public class AsciiTable { ...@@ -86,8 +91,8 @@ public class AsciiTable {
/** /**
* Add a line to the table * Add a line to the table
* @param line string containing the line with all the columns separated by the column separator. * @param line string containing the line with all the columns separated by the column separator.
* The line should not end up with a newline char. If it is the case, alignement errors can be experienced * The line should not end up with a newline char. If it is the case, alignment errors can be experienced
* depending on the alignement type of the last column. * depending on the alignment type of the last column.
*/ */
public void addLine(String line){ public void addLine(String line){
// compute the number of columns, if we add the first line // compute the number of columns, if we add the first line
...@@ -129,7 +134,7 @@ public class AsciiTable { ...@@ -129,7 +134,7 @@ public class AsciiTable {
} }
/** /**
* Get all the lines without alignement, as they were entered * Get all the lines without alignment, as they were entered
* @return the array of the lines in the table * @return the array of the lines in the table
*/ */
public String[] displayRaw(){ public String[] displayRaw(){
...@@ -137,7 +142,7 @@ public class AsciiTable { ...@@ -137,7 +142,7 @@ public class AsciiTable {
} }
/** /**
* Get all the lines without alignement, as they were entered, with separator control * Get all the lines without alignment, as they were entered, with separator control
* @param newsep separator to use, replacing the original one * @param newsep separator to use, replacing the original one
* @return the array of the lines in the table * @return the array of the lines in the table
*/ */
...@@ -156,49 +161,121 @@ public class AsciiTable { ...@@ -156,49 +161,121 @@ public class AsciiTable {
/** /**
* Get all the lines in the table, properly aligned. * Get all the lines in the table, properly aligned.
* @param pos array of flags, indicating how each column should be justified. *
* The array must have as many columns as the table has. Each column can contain * <p><strong>IMPORTANT:</strong>
* either AsciiTable.LEFT, AsciiTable.CENTER or AsciiTable.RIGHT<br> * The array must have as many columns as the table has. Each column can
* if the array contains ONE item, it will be used for every column. * contain either {@link AsciiTable#LEFT}, {@link AsciiTable#CENTER} or
* @return an array of the table lines, aligned and justified * {@link AsciiTable#RIGHT}. If the array contains ONE item, it will be
* used for every column.
* </p>
*
* @param pos Array of flags, indicating how each column should be
* justified.
*
* @return An array of the table lines, aligned and justified.
*
* @throws InterruptedException If the current thread has been interrupted.
* <em>This interruption is useful when this
* alignment operation becomes time and memory
* consuming.</em>
*/ */
public String[] displayAligned(int[] pos){ public String[] displayAligned(int[] pos) throws InterruptedException{
return align(pos, '\0'); return align(pos, '\0', null);
} }
/** /**
* Get all the lines in the table, properly aligned. * Get all the lines in the table, properly aligned.
* @param pos array of flags, indicating how each column should be justified. *
* The array must have as many columns as the table has. Each column can contain * <p><strong>IMPORTANT:</strong>
* either AsciiTable.LEFT, AsciiTable.CENTER or AsciiTable.RIGHT<br> * The array must have as many columns as the table has. Each column can
* if the array contains ONE item, it will be used for every column. * contain either {@link AsciiTable#LEFT}, {@link AsciiTable#CENTER} or
* @param newsep separator to use, replacing the original one * {@link AsciiTable#RIGHT}. If the array contains ONE item, it will be
* @return an array of the table lines, aligned and justified * used for every column.
* </p>
*
* @param pos Array of flags, indicating how each column should be
* justified.
* @param newsep Separator to use, replacing the original one.
*
* @return An array of the table lines, aligned and justified.
*
* @throws InterruptedException If the current thread has been interrupted.
* <em>This interruption is useful when this
* alignment operation becomes time and memory
* consuming.</em>
*/
public String[] displayAligned(int[] pos, char newsep) throws InterruptedException{
return displayAligned(pos, newsep, null);
}
/**
* Get all the lines in the table, properly aligned.
*
* <p><strong>IMPORTANT:</strong>
* The array must have as many columns as the table has. Each column can
* contain either {@link AsciiTable#LEFT}, {@link AsciiTable#CENTER} or
* {@link AsciiTable#RIGHT}. If the array contains ONE item, it will be
* used for every column.
* </p>
*
* @param pos Array of flags, indicating how each column should be
* justified.
* @param newsep Separator to use, replacing the original one.
* @param thread Thread to watch. If it is interrupted, this task should
* be as well.
*
* @return An array of the table lines, aligned and justified.
*
* @throws InterruptedException If the current thread has been interrupted.
* <em>This interruption is useful when this
* alignment operation becomes time and memory
* consuming.</em>
*/ */
public String[] displayAligned(int[] pos, char newsep){ public String[] displayAligned(int[] pos, char newsep, final Thread thread) throws InterruptedException{
if (newsep == csep) if (newsep == csep)
newsep = '\0'; newsep = '\0';
return align(pos, newsep); return align(pos, newsep, thread);
} }
/** /**
* Get the array of lines in which all the columns are aligned * Get the array of lines in which all the columns are aligned
* @param pos array of flags, indicating how each column should be justified. *
* The array must have as many columns as the table has. Each column can contain * <p><strong>IMPORTANT:</strong>
* either AsciiTable.LEFT, AsciiTable.CENTER or AsciiTable.RIGHT<br> * The array must have as many columns as the table has. Each column can
* if the array contains ONE item, it will be used for every column. * contain either {@link AsciiTable#LEFT}, {@link AsciiTable#CENTER} or
* @param newsep separator to use, replacing the original one (no replacement if '\0') * {@link AsciiTable#RIGHT}. If the array contains ONE item, it will be
* @return an array of the table lines, aligned and justified * used for every column.
* </p>
*
* @param pos Array of flags, indicating how each column should be
* justified.
* @param newsep Separator to use, replacing the original one.
* @param thread Thread to watch. If it is interrupted, this task should
* be as well.
*
* @return An array of the table lines, aligned and justified.
*
* @throws InterruptedException If the current thread has been interrupted.
* <em>This interruption is useful when this
* alignment operation becomes time and memory
* consuming.</em>
*/ */
private String[] align(int[] pos, char newsep){ private String[] align(int[] pos, char newsep, final Thread thread) throws InterruptedException{
int nblines = lines.size(); int nblines = lines.size();
String[] result = new String[nblines]; String[] result = new String[nblines];
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
int p0, p1, col, fldsize, colsize, n1, inserted; int p0, p1, col, fldsize, colsize, n1, inserted;
boolean inHeader = header; // A header can contain several lines. The end is detected by a line boolean inHeader = header; // A header can contain several lines. The end is detected by a line
// beginning by the separator char // beginning by the separator char
int uniqueJustif = pos.length == 1 ? pos[0] : -1; int uniqueJustif = pos.length == 1 ? pos[0] : -1;
for(int i = 0; i < nblines; i++){ for(int i = 0; i < nblines; i++){
/* stop everything if this thread or the given one has been
* interrupted: */
if (Thread.currentThread().isInterrupted() || (thread != null && thread.isInterrupted()))
throw new InterruptedException();
buf.delete(0, buf.length()); buf.delete(0, buf.length());
String line = lines.get(i); String line = lines.get(i);
p0 = 0; p0 = 0;
...@@ -277,11 +354,12 @@ public class AsciiTable { ...@@ -277,11 +354,12 @@ public class AsciiTable {
} }
/** /**
* Display the whole table, with left alignement * Display the whole table, with left alignment
* @return the table as a unique string * @return the table as a unique string
*/ */
@Override @Override
public String toString(){ public String toString(){
try{
StringBuffer buf = new StringBuffer(); StringBuffer buf = new StringBuffer();
String[] ids = displayAligned(new int[]{ AsciiTable.LEFT }); String[] ids = displayAligned(new int[]{ AsciiTable.LEFT });
...@@ -292,5 +370,8 @@ public class AsciiTable { ...@@ -292,5 +370,8 @@ public class AsciiTable {
} }
return buf.toString(); return buf.toString();
}catch(InterruptedException ie){
return "!!! Operation unexpectedly interrupted !!!";
}
} }
} }
...@@ -41,7 +41,7 @@ import tap.backup.DefaultTAPBackupManager; ...@@ -41,7 +41,7 @@ import tap.backup.DefaultTAPBackupManager;
* </i></p> * </i></p>
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
* @version 2.3 (09/2018) * @version 2.3 (11/2018)
* @since 2.0 * @since 2.0
*/ */
public final class TAPConfiguration { public final class TAPConfiguration {
...@@ -166,12 +166,12 @@ public final class TAPConfiguration { ...@@ -166,12 +166,12 @@ public final class TAPConfiguration {
* This is the fetch size for to apply for synchronous queries. */ * This is the fetch size for to apply for synchronous queries. */
public final static String KEY_SYNC_FETCH_SIZE = "sync_fetch_size"; public final static String KEY_SYNC_FETCH_SIZE = "sync_fetch_size";
/** Default value of the property {@link #KEY_SYNC_FETCH_SIZE}: {@value #DEFAULT_SYNC_FETCH_SIZE}. */ /** Default value of the property {@link #KEY_SYNC_FETCH_SIZE}: {@value #DEFAULT_SYNC_FETCH_SIZE}. */
public final static int DEFAULT_SYNC_FETCH_SIZE = 10000; public final static int DEFAULT_SYNC_FETCH_SIZE = 1000;
/** Name/Key of the property specifying by how many rows the library should fetch a query result from the database. /** Name/Key of the property specifying by how many rows the library should fetch a query result from the database.
* This is the fetch size for to apply for asynchronous queries. */ * This is the fetch size for to apply for asynchronous queries. */
public final static String KEY_ASYNC_FETCH_SIZE = "async_fetch_size"; public final static String KEY_ASYNC_FETCH_SIZE = "async_fetch_size";
/** Default value of the property {@link #KEY_ASYNC_FETCH_SIZE}: {@value #DEFAULT_ASYNC_FETCH_SIZE}. */ /** Default value of the property {@link #KEY_ASYNC_FETCH_SIZE}: {@value #DEFAULT_ASYNC_FETCH_SIZE}. */
public final static int DEFAULT_ASYNC_FETCH_SIZE = 100000; public final static int DEFAULT_ASYNC_FETCH_SIZE = 10000;
/** Name/Key of the property specifying the name of the DataSource into the JDNI. */ /** Name/Key of the property specifying the name of the DataSource into the JDNI. */
public final static String KEY_DATASOURCE_JNDI_NAME = "datasource_jndi_name"; public final static String KEY_DATASOURCE_JNDI_NAME = "datasource_jndi_name";
/** Name/Key of the property specifying the full class name of the JDBC driver. /** Name/Key of the property specifying the full class name of the JDBC driver.
......
...@@ -193,9 +193,9 @@ ...@@ -193,9 +193,9 @@
too much waiting time from the TAP /sync users (and thus, avoiding some HTTP client timeouts).</p> too much waiting time from the TAP /sync users (and thus, avoiding some HTTP client timeouts).</p>
<p>A negative or null value means that the default value of the JDBC driver will be used. Generally, <p>A negative or null value means that the default value of the JDBC driver will be used. Generally,
it means that the database must wait to have collected all data before sending them to the library.</p> it means that the database must wait to have collected all data before sending them to the library.</p>
<p><em>Default: <code>sync_fetch_size=10000</code></em></p> <p><em>Default: <code>sync_fetch_size=1000</code></em></p>
</td> </td>
<td><ul><li>10000 <em>(default)</em><li>0 <em>(wait for the the whole result)</em></li><li>100000</li></ul></td> <td><ul><li>1000 <em>(default)</em><li>0 <em>(wait for the the whole result)</em></li><li>100000</li></ul></td>
</tr> </tr>
<tr class="optional"> <tr class="optional">
<td class="done">async_fetch_size</td> <td class="done">async_fetch_size</td>
...@@ -208,9 +208,9 @@ ...@@ -208,9 +208,9 @@
If supported by the DBMS and the JDBC driver, this feature may help sparing memory.</p> If supported by the DBMS and the JDBC driver, this feature may help sparing memory.</p>
<p>A negative or null value means that the default value of the JDBC driver will be used. Generally, <p>A negative or null value means that the default value of the JDBC driver will be used. Generally,
it means that the database must wait to have collected all data before sending them to the library.</p> it means that the database must wait to have collected all data before sending them to the library.</p>
<p><em>Default: <code>async_fetch_size=100000</code></em></p> <p><em>Default: <code>async_fetch_size=10000</code></em></p>
</td> </td>
<td><ul><li>100000 <em>(default)</em><li>0 <em>(wait for the the whole result)</em></li><li>1000000</li></ul></td> <td><ul><li>10000 <em>(default)</em><li>0 <em>(wait for the the whole result)</em></li><li>1000000</li></ul></td>
</tr> </tr>
<tr><td colspan="5">&#10551; JNDI datasource <i>(only if database_access=jndi)</i></td></tr> <tr><td colspan="5">&#10551; JNDI datasource <i>(only if database_access=jndi)</i></td></tr>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# FULL TAP CONFIGURATION FILE # # FULL TAP CONFIGURATION FILE #
# # # #
# TAP Version: 2.3 # # TAP Version: 2.3 #
# Date: 26 Oct. 2018 # # Date: 2 Nov. 2018 #
# Author: Gregory Mantelet (CDS;ARI) # # Author: Gregory Mantelet (CDS;ARI) #
# # # #
################################################################################ ################################################################################
...@@ -104,7 +104,7 @@ sql_translator = postgres ...@@ -104,7 +104,7 @@ sql_translator = postgres
# data before sending them to the library. # data before sending them to the library.
# #
# Default: sync_fetch_size=10000 # Default: sync_fetch_size=10000
sync_fetch_size = 10000 sync_fetch_size = 1000
# [OPTIONAL] # [OPTIONAL]
# Size of result blocks to fetch from the database when an ADQL query is # Size of result blocks to fetch from the database when an ADQL query is
...@@ -120,7 +120,7 @@ sync_fetch_size = 10000 ...@@ -120,7 +120,7 @@ sync_fetch_size = 10000
# data before sending them to the library. # data before sending them to the library.
# #
# Default: async_fetch_size=100000 # Default: async_fetch_size=100000
async_fetch_size=100000 async_fetch_size=10000
############################# #############################
# IF DATABASE ACCESS = JNDI # # IF DATABASE ACCESS = JNDI #
......
...@@ -16,7 +16,7 @@ package tap.formatter; ...@@ -16,7 +16,7 @@ package tap.formatter;
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>. * along with TAPLibrary. If not, see <http://www.gnu.org/licenses/>.
* *
* Copyright 2012-2015 - UDS/Centre de Données astronomiques de Strasbourg (CDS), * Copyright 2012-2018 - UDS/Centre de Données astronomiques de Strasbourg (CDS),
* Astronomisches Rechen Institut (ARI) * Astronomisches Rechen Institut (ARI)
*/ */
...@@ -37,7 +37,7 @@ import tap.data.TableIterator; ...@@ -37,7 +37,7 @@ import tap.data.TableIterator;
* (columns' width are adjusted so that all columns are well aligned and of the same width). * (columns' width are adjusted so that all columns are well aligned and of the same width).
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
* @version 2.0 (04/2015) * @version 2.3 (11/2018)
*/ */
public class TextFormat implements OutputFormat { public class TextFormat implements OutputFormat {
...@@ -101,10 +101,10 @@ public class TextFormat implements OutputFormat { ...@@ -101,10 +101,10 @@ public class TextFormat implements OutputFormat {
// Finally write the formatted ASCII table (header + data) in the output stream: // Finally write the formatted ASCII table (header + data) in the output stream:
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output)); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output));
String[] lines = asciiTable.displayAligned(new int[]{AsciiTable.LEFT}, '|'); String[] lines = asciiTable.displayAligned(new int[]{ AsciiTable.LEFT }, '|', thread);
execReport.nbRows = 0; execReport.nbRows = 0;
for(String l : lines){ for(String l : lines){
// stop right now the formatting if the job has been aborted/canceled/interrupted: // stop right now the formatting if the job has been aborted/cancelled/interrupted:
if (thread.isInterrupted()) if (thread.isInterrupted())
throw new InterruptedException(); throw new InterruptedException();
// write the line: // write the line:
...@@ -180,7 +180,7 @@ public class TextFormat implements OutputFormat { ...@@ -180,7 +180,7 @@ public class TextFormat implements OutputFormat {
StringBuffer line = new StringBuffer(); StringBuffer line = new StringBuffer();
while(queryResult.nextRow()){ while(queryResult.nextRow()){
// Stop right now the formatting if the job has been aborted/canceled/interrupted: // Stop right now the formatting if the job has been aborted/cancelled/interrupted:
if (thread.isInterrupted()) if (thread.isInterrupted())
throw new InterruptedException(); throw new InterruptedException();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment