diff --git a/src/tap/ADQLExecutor.java b/src/tap/ADQLExecutor.java
index c1327eb3..689a152a 100644
--- a/src/tap/ADQLExecutor.java
+++ b/src/tap/ADQLExecutor.java
@@ -2,21 +2,21 @@
/*
* This file is part of TAPLibrary.
- *
+ *
* TAPLibrary is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* TAPLibrary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Lesser General Public License
* along with TAPLibrary. If not, see
Let process completely an ADQL query.
- * + * *Thus, this class aims to apply the following actions (in the given order):
** This executor is able to process queries coming from a synchronous job (the result must be written directly in the HTTP response) * and from an asynchronous job (the result must be written, generally, in a file). Two start(...) functions let deal with * the differences between the two job execution modes: {@link #start(AsyncThread)} for asynchronous jobs * and {@link #start(Thread, String, TAPParameters, HttpServletResponse)} for synchronous jobs. *
- * + * *Uploaded tables must be provided in VOTable format.
- * + * ** Query results must be formatted in the format specified by the user in the job parameters. A corresponding formatter ({@link OutputFormat}) * is asked to the description of the TAP service ({@link ServiceConnection}). If none can be found, VOTable will be chosen by default. *
- * + * *It is totally possible to customize some parts of the ADQL query processing. However, the main algorithm must remain the same and is implemented * by {@link #start()}. This function is final, like {@link #start(AsyncThread)} and {@link #start(Thread, String, TAPParameters, HttpServletResponse)}, * which are just preparing the execution for {@link #start()} in function of the job execution mode (asynchronous or synchronous). *
- * + * *Note: * {@link #start()} is using the Template Method Design Pattern: it defines the skeleton/algorithm of the processing, and defers some steps * to other functions. *
- * + * ** So, you are able to customize almost all individual steps of the ADQL query processing: {@link #parseADQL()}, {@link #executeADQL(ADQLQuery)} and * {@link #writeResult(TableIterator, OutputFormat, OutputStream)}. *
- * + * *Note: * Note that the formatting of the result is done by an OutputFormat and that the executor is just calling the appropriate function of the formatter. *
- * + * ** There is no way in this executor to customize the upload. However, it does not mean it can not be customized. * Indeed you can do it easily by extending {@link Uploader} and by providing the new class inside your {@link TAPFactory} implementation * (see {@link TAPFactory#createUploader(DBConnection)}). *
- * + * * @author Grégory Mantelet (CDS;ARI) - * @version 2.1 (04/2017) + * @version 2.3 (03/2019) */ public class ADQLExecutor { @@ -139,7 +139,7 @@ public class ADQLExecutor { /** * Build an {@link ADQLExecutor}. - * + * * @param service The description of the TAP service. */ public ADQLExecutor(final ServiceConnection service){ @@ -149,7 +149,7 @@ public ADQLExecutor(final ServiceConnection service){ /** * Get the logger used by this executor. - * + * * @return The used logger. */ public final TAPLog getLogger(){ @@ -158,12 +158,12 @@ public final TAPLog getLogger(){ /** *Get the report of the query execution. It helps indicating the execution progression and the duration of each step.
- * + * *Note: * Before starting the execution (= before the call of a "start(...)" function), this function will return NULL. * It is set when the query processing starts and remains not NULL after that (even when the execution is finished). *
- * + * * @return The execution report. */ public final TAPExecutionReport getExecReport(){ @@ -173,11 +173,11 @@ public final TAPExecutionReport getExecReport(){ /** *Get the object to use in order to write the query result in the appropriate format * (either the asked one, or else VOTable).
- * + * * @return The appropriate result formatter to use. Can not be NULL! - * + * * @throws TAPException If no format corresponds to the asked one and if no default format (for VOTable) can be found. - * + * * @see ServiceConnection#getOutputFormat(String) */ protected OutputFormat getFormatter() throws TAPException{ @@ -196,19 +196,19 @@ protected OutputFormat getFormatter() throws TAPException{ /** *Start the asynchronous processing of the ADQL query.
- * + * ** This function initialize the execution report, get the execution parameters (including the query to process) * and then call {@link #start()}. *
- * + * * @param thread The asynchronous thread which asks the query processing. - * + * * @return The resulting execution report. - * + * * @throws UWSException If any error occurs while executing the ADQL query. * @throws InterruptedException If the job has been interrupted (by the user or a time-out). - * + * * @see #start() */ public final TAPExecutionReport start(final AsyncThread thread) throws UWSException, InterruptedException{ @@ -244,16 +244,16 @@ public final TAPExecutionReport start(final AsyncThread thread) throws UWSExcept /** *Create the database connection required for the ADQL execution.
- * + * *Note: This function has no effect if the DB connection already exists.
- * + * * @param jobID ID of the job which will be executed by this {@link ADQLExecutor}. * This ID will be the database connection ID. - * + * * @throws TAPException If the DB connection creation fails. - * + * * @see TAPFactory#getConnection(String) - * + * * @since 2.0 */ public final void initDBConnection(final String jobID) throws TAPException{ @@ -264,7 +264,7 @@ public final void initDBConnection(final String jobID) throws TAPException{ /** * Cancel the current SQL query execution or result set fetching if any is currently running. * If no such process is on going, this function has no effect. - * + * * @since 2.1 */ public final void cancelQuery(){ @@ -274,20 +274,20 @@ public final void cancelQuery(){ /** *Start the synchronous processing of the ADQL query.
- * + * *This function initialize the execution report and then call {@link #start()}.
- * + * * @param thread The synchronous thread which asks the query processing. * @param jobId ID of the corresponding job. * @param params All execution parameters (including the query to process). * @param response Object in which the result or the error must be written. - * + * * @return The resulting execution report. - * + * * @throws TAPException If any error occurs while executing the ADQL query. * @throws IOException If any error occurs while writing the result in the given {@link HttpServletResponse}. * @throws InterruptedException If the job has been interrupted (by the user or a time-out). - * + * * @see #start() */ public final TAPExecutionReport start(final Thread thread, final String jobId, final TAPParameters params, final HttpServletResponse response) throws TAPException, IOException, InterruptedException{ @@ -308,7 +308,7 @@ public final TAPExecutionReport start(final Thread thread, final String jobId, f /** *Process the ADQL query.
- * + * *This function calls the following function (in the same order):
** The execution report is updated gradually. Besides a job parameter - progression - is set at each step of the process in order to * notify the user of the progression of the query execution. This parameter is removed at the end of the execution if it is successful. *
- * + * *The "interrupted" flag of the associated thread is often tested so that stopping the execution as soon as possible.
- * + * * @return The updated execution report. - * + * * @throws TAPException If any error occurs while executing the ADQL query. * @throws UWSException If any error occurs while executing the ADQL query. * @throws IOException If an error happens while writing the result in the specified {@link HttpServletResponse}. @@ -434,18 +434,18 @@ protected final TAPExecutionReport start() throws TAPException, UWSException, IO /** *Memorize the time at which the step starts, the step ID and update the job parameter "progression" * (to notify the user about the progression of the query processing).
- * + * *Note: * If for some reason the job parameter "progression" can not be updated, no error will be thrown. A WARNING message * will be just written in the log. *
- * + * *Note: * This function is designed to work with {@link #endStep()}, which must be called after it, when the step is finished (successfully or not). *
- * + * * @param progression ID of the starting step. - * + * * @see #endStep() */ private void startStep(final ExecutionProgression progression){ @@ -464,16 +464,16 @@ private void startStep(final ExecutionProgression progression){ /** *Set the duration of the current step in the execution report.
- * + * *Note: * The start time and the ID of the step are then forgotten. *
- * + * *Note: * This function is designed to work with {@link #startStep(ExecutionProgression)}, which must be called before it, when the step is starting. * It marks the end of a step. *
- * + * * @see #startStep(ExecutionProgression) */ private void endStep(){ @@ -489,11 +489,11 @@ private void endStep(){ /** *Create in the "database" all tables uploaded by the user (only for this specific query execution).
- * + * *Note: * Obviously, nothing is done if no table has been uploaded. *
- * + * * @throws TAPException If any error occurs while reading the uploaded table * or while importing them in the database. */ @@ -510,23 +510,23 @@ private final void uploadTables() throws TAPException{ /** *Parse the ADQL query provided in the parameters by the user.
- * + * *The query factory and the query checker are got from the TAP factory.
- * + * ** The configuration of this TAP service list all allowed coordinate systems. These are got here and provided to the query checker * in order to ensure the coordinate systems used in the query are in this list. *
- * + * ** The row limit specified in the ADQL query (with TOP) is checked and adjusted (if needed). Indeed, this limit * can not exceed MAXREC given in parameter and the maximum value specified in the configuration of this TAP service. * In the case no row limit is specified in the query or the given value is greater than MAXREC, (MAXREC+1) is used by default. * The "+1" aims to detect overflows. *
- * + * * @return The object representation of the ADQL query. - * + * * @throws ParseException If the given ADQL query can not be parsed or if the construction of the object representation has failed. * @throws InterruptedException If the thread has been interrupted. * @throws TAPException If the TAP factory is unable to create the ADQL factory or the query checker. @@ -551,7 +551,30 @@ protected ADQLQuery parseADQL() throws ParseException, InterruptedException, TAP parser.setQueryChecker(service.getFactory().createQueryChecker(uploadSchema)); // Parse the ADQL query: - ADQLQuery query = parser.parseQuery(tapParams.getQuery()); + ADQLQuery query = null; + // if the fixOnFail option is enabled... + if (service.fixOnFailEnabled()){ + try{ + // try parsing the query: + query = parser.parseQuery(tapParams.getQuery()); + }catch(ParseException pe){ + // if it fails... + // ...log the auto fix attempt: + logger.logTAP(LogLevel.INFO, report, "PARSING", "Parse attempt of the original input query failed! Trying auto-fix...", null); + // ...try fixing the query: + String fixedQuery = parser.tryQuickFix(tapParams.getQuery()); + // ...log the auto fixed query, if successful: + logger.logTAP(LogLevel.INFO, report, "PARSING", "Auto-fixed query: " + fixedQuery.replaceAll("(\t|\r?\n)+", " "), null); + // ...keep this fixed query in the exec report: + report.fixedQuery = fixedQuery; + // ...and finally try parsing it a last time: + query = parser.parseQuery(fixedQuery); + } + } + // if not enabled, parse immediately the query: + else{ + query = parser.parseQuery(tapParams.getQuery()); + } // Set or check the row limit: final int limit = query.getSelect().getLimit(); @@ -566,22 +589,22 @@ protected ADQLQuery parseADQL() throws ParseException, InterruptedException, TAP /** *Execute in "database" the given object representation of an ADQL query.
- * + * *By default, this function is just calling {@link DBConnection#executeQuery(ADQLQuery)} and then it returns the value returned by this call.
- * + * *Note: * An INFO message is logged at the end of the query execution in order to report the result status (success or error) * and the execution duration. *
- * + * * @param adql The object representation of the ADQL query to execute. - * + * * @return The result of the query. - * + * * @throws InterruptedException If the thread has been interrupted. * @throws DBCancelledException If the inner DB connection has been canceled. * @throws TAPException If the {@link DBConnection} has failed to deal with the given ADQL query. - * + * * @see DBConnection#executeQuery(ADQLQuery) */ protected TableIterator executeADQL(final ADQLQuery adql) throws InterruptedException, DBCancelledException, TAPException{ @@ -617,21 +640,21 @@ protected TableIterator executeADQL(final ADQLQuery adql) throws InterruptedExce /** *Write the given query result into the appropriate format in the appropriate output * (HTTP response for a synchronous execution, otherwise a file or any output provided by UWS).
- * + * *This function prepare the output in function of the execution type (synchronous or asynchronous). * Once prepared, the result, the output and the formatter to use are given to {@link #writeResult(TableIterator, OutputFormat, OutputStream)} * which will really process the result formatting and writing. *
- * + * * @param queryResult The result of the query execution in database. - * + * * @throws InterruptedException If the thread has been interrupted. * @throws IOException If an error happens while writing the result in the {@link HttpServletResponse}. * That kind of error can be thrown only in synchronous mode. * In asynchronous, the error is stored as job error report and is never propagated. * @throws TAPException If an error occurs while getting the appropriate formatter or while formatting or writing (synchronous execution) the result. * @throws UWSException If an error occurs while getting the output stream or while writing (asynchronous execution) the result. - * + * * @see #writeResult(TableIterator, OutputFormat, OutputStream) */ protected final void writeResult(final TableIterator queryResult) throws InterruptedException, IOException, TAPException, UWSException{ @@ -707,18 +730,18 @@ protected final void writeResult(final TableIterator queryResult) throws Interru /** *Format and write the given result in the given output with the given formatter.
- * + * *By default, this function is just calling {@link OutputFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)}.
- * + * *Note: * {@link OutputFormat#writeResult(TableIterator, OutputStream, TAPExecutionReport, Thread)} is often testing the "interrupted" flag of the * thread in order to stop as fast as possible if the user has cancelled the job or if the thread has been interrupted for another reason. *
- * + * * @param queryResult Query result to format and to output. * @param formatter The object able to write the result in the appropriate format. * @param output The stream in which the result must be written. - * + * * @throws InterruptedException If the thread has been interrupted. * @throws IOException If there is an error while writing the result in the given stream. * @throws TAPException If there is an error while formatting the result. @@ -729,11 +752,11 @@ protected void writeResult(TableIterator queryResult, OutputFormat formatter, Ou /** *Drop all tables uploaded by the user from the database.
- * + * *Note: * By default, if an error occurs while dropping a table from the database, the error will just be logged ; it won't be thrown/propagated. *
- * + * * @throws TAPException If a grave error occurs. By default, no exception is thrown ; they are just logged. */ protected void dropUploadedTables() throws TAPException{ diff --git a/src/tap/ServiceConnection.java b/src/tap/ServiceConnection.java index 6545b5f3..e22e657f 100644 --- a/src/tap/ServiceConnection.java +++ b/src/tap/ServiceConnection.java @@ -16,7 +16,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, seeThis function tells whether TAP-Lib should automatically try to fix a + * query whose parsing failed because of a token error. After this fix + * attempt the query is parsed again for a last time.
+ * + * @return true to allow automatic fix attempt in case of error, + * false to disable this option. + * + * @since 2.3 + */ + public boolean fixOnFailEnabled(); + } diff --git a/src/tap/TAPExecutionReport.java b/src/tap/TAPExecutionReport.java index fe2733f0..5e4db034 100644 --- a/src/tap/TAPExecutionReport.java +++ b/src/tap/TAPExecutionReport.java @@ -1,49 +1,66 @@ package tap; +import adql.db.DBColumn; + /* * This file is part of TAPLibrary. - * + * * TAPLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * TAPLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, seeReport the execution (including the parsing and the output writing) of an ADQL query. - * It gives information on the job parameters, the job ID, whether it is a synchronous task or not, times of each execution step (uploading, parsing, executing and writing), - * the resulting columns and the success or not of the execution.
- * - *This report is completely filled by {@link ADQLExecutor}, and aims to be used/read only at the end of the job or when it is definitely finished.
- * + * Report the execution (including the parsing and the output writing) of an + * ADQL query. + * + *+ * It gives information on the job parameters, the job ID, whether it is a + * synchronous task or not, times of each execution step (uploading, parsing, + * executing and writing), the resulting columns and the success or not of the + * execution. + *
+ * + *+ * This report is completely filled by {@link ADQLExecutor}, and aims to be + * used/read only at the end of the job or when it is definitely finished. + *
+ * * @author Grégory Mantelet (CDS;ARI) - * @version 2.0 (04/2015) + * @version 2.3 (03/2019) */ public class TAPExecutionReport { /** ID of the job whose the execution is reported here. */ public final String jobID; - /** Indicate whether this execution is done in a synchronous or asynchronous job. */ + /** Indicate whether this execution is done in a synchronous or asynchronous + * job. */ public final boolean synchronous; /** List of all parameters provided in the user request. */ public final TAPParameters parameters; + /** Input ADQL query after an automatic fix by TAP-Lib. + *This field is set only if the option fix_on_fail is enabled in the TAP + * configuration and that a query has been fixed.
+ * @since 2.3 */ + public String fixedQuery = null; + /** List of all resulting columns. Empty array, if not yet known. */ public DBColumn[] resultingColumns = new DBColumn[0]; @@ -51,21 +68,26 @@ public class TAPExecutionReport { * @since 2.0 */ public long nbRows = -1; - /** Duration of all execution steps. For the moment only 4 steps (in the order): uploading, parsing, executing and writing. */ - protected final long[] durations = new long[]{-1,-1,-1,-1}; + /** Duration of all execution steps. For the moment only 4 steps (in the + * order): uploading, parsing, executing and writing. */ + protected final long[] durations = new long[]{ -1, -1, -1, -1 }; /** Total duration of the job execution. */ protected long totalDuration = -1; - /** Indicate whether this job has ended successfully or not. At the beginning or while executing, this field is always FALSE. */ + /** Indicate whether this job has ended successfully or not. + * At the beginning or while executing, this field is always FALSE. */ public boolean success = false; /** * Build an empty execution report. - * - * @param jobID ID of the job whose the execution must be described here. - * @param synchronous true if the job is synchronous, false otherwise. - * @param params List of all parameters provided by the user for the execution. + * + * @param jobID ID of the job whose the execution must be described + * here. + * @param synchronous true if the job is synchronous, + * false otherwise. + * @param params List of all parameters provided by the user for the + * execution. */ public TAPExecutionReport(final String jobID, final boolean synchronous, final TAPParameters params){ this.jobID = jobID; @@ -74,14 +96,20 @@ public TAPExecutionReport(final String jobID, final boolean synchronous, final T } /** - *Map the execution progression with an index inside the {@link #durations} array.
- * - *Warning: for the moment, only {@link ExecutionProgression#UPLOADING}, {@link ExecutionProgression#PARSING}, - * {@link ExecutionProgression#EXECUTING_ADQL} and {@link ExecutionProgression#WRITING_RESULT} are managed.
- * + * Map the execution progression with an index inside the {@link #durations} + * array. + * + *Warning: + * for the moment, only {@link ExecutionProgression#UPLOADING}, + * {@link ExecutionProgression#PARSING}, + * {@link ExecutionProgression#EXECUTING_ADQL} and + * {@link ExecutionProgression#WRITING_RESULT} are managed. + *
+ * * @param tapProgression Execution progression. - * - * @return Index in the array {@link #durations}, or -1 if the given execution progression is not managed. + * + * @return Index in the array {@link #durations}, + * or -1 if the given execution progression is not managed. */ protected int getIndexDuration(final ExecutionProgression tapProgression){ switch(tapProgression){ @@ -100,11 +128,12 @@ protected int getIndexDuration(final ExecutionProgression tapProgression){ /** * Get the duration corresponding to the given job execution step. - * + * * @param tapStep Job execution step. - * - * @return The corresponding duration (in ms), or -1 if this step has not been (yet) processed. - * + * + * @return The corresponding duration (in ms), or -1 if this step has not + * been (yet) processed. + * * @see #getIndexDuration(ExecutionProgression) */ public final long getDuration(final ExecutionProgression tapStep){ @@ -117,7 +146,7 @@ public final long getDuration(final ExecutionProgression tapStep){ /** * Set the duration corresponding to the given execution step. - * + * * @param tapStep Job execution step. * @param duration Duration (in ms) of the given execution step. */ diff --git a/src/tap/config/ConfigurableServiceConnection.java b/src/tap/config/ConfigurableServiceConnection.java index 09e2ce7d..f9213925 100644 --- a/src/tap/config/ConfigurableServiceConnection.java +++ b/src/tap/config/ConfigurableServiceConnection.java @@ -16,13 +16,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with TAPLibrary. If not, seeConcrete implementation of {@link ServiceConnection}, fully parameterized with a TAP configuration file.
+ * Concrete implementation of {@link ServiceConnection}, fully parameterized + * with a TAP configuration file. * *- * Every aspects of the TAP service are configured here. This instance is also creating the {@link TAPFactory} using the - * TAP configuration file thanks to the implementation {@link ConfigurableTAPFactory}. + * Every aspects of the TAP service are configured here. This instance is also + * creating the {@link TAPFactory} using the TAP configuration file thanks to + * the implementation {@link ConfigurableTAPFactory}. *
* * @author Grégory Mantelet (CDS;ARI) - * @version 2.3 (11/2018) + * @version 2.3 (03/2019) * @since 2.0 */ public final class ConfigurableServiceConnection implements ServiceConnection { @@ -142,10 +146,12 @@ public final class ConfigurableServiceConnection implements ServiceConnection { /** File manager to use in the TAP service. */ private UWSFileManager fileManager; - /** Object to use in the TAP service in order to log different types of messages (e.g. DEBUG, INFO, WARNING, ERROR, FATAL). */ + /** Object to use in the TAP service in order to log different types of + * messages (e.g. DEBUG, INFO, WARNING, ERROR, FATAL). */ private TAPLog logger; - /** Factory which can create different types of objects for the TAP service (e.g. database connection). */ + /** Factory which can create different types of objects for the TAP service + * (e.g. database connection). */ private TAPFactory tapFactory; /** Object gathering all metadata of this TAP service. */ @@ -175,9 +181,11 @@ public final class ConfigurableServiceConnection implements ServiceConnection { private final ArrayListIf enabled, this option lets automatically try fixing a query whose parsing + failed because of a token error. This is particularly useful in the following + cases:
+_RAJ2000
, 2mass
)distance
, point
, min
)public
, date
, year
, user
)In all these cases, the name/identifier/alias will be surrounded between
+ double quotes if the option fix_on_fail
is enabled.
This feature is also able to fix some incorrect Unicode characters + (e.g. LaTeX alternatives for underscores, spaces and double/single quotes, + copy-pasted from a PDF into a TAP query field).
+Warning: + since this option alters the query provided by the query, it is + possible that, after fix attempt, the query parses and runs but might + generate an output different from what was expected. So, if this + option is enabled, it should made be clear to the user that the TAP + server might alter the query to make it work. +
+Note:
+ when an input query is fixed and run successfully, the fixed ADQL query
+ is reported in an INFO element named QUERY_AFTER_AUTO_FIX
inside the
+ output VOTable.
+
Default: fix_on_fail=false
/examples
endpoint by specifying an
-# XHTML-RDFa document listing TAP query examples using the syntax specified by
-# TAPNotes 1.0 DALI 1.0.
-#
-# 3 different kinds of value are accepted:
-# * nothing (default): no /examples endpoint.
-# * name or relative path of a file: this method MUST be chosen if the
-# endpoint content is a JSP file. This file MUST be inside the directory
-# WebContent of your web application.
-# * URI starting with file://: in this method the local file pointed by the
-# URI will be merely returned when the endpoint will be requested.
-# * a URL: here, a redirection toward this URL will be made at each request
-# on the endpoint
-#
-# If you want a custom /examples endpoint (i.e. you do not)
-# want to forward/redirect to a file/URL), you can create a class which
-# implements TAPResource AND VOSIResource. The function getName() must return
-# "examples". Then, just append the classpath to the property
-# "additional_resources" of the TAP configuration file.
-#
-# By default, the TAP service does not have any /examples
endpoint.
-examples =
-
-# [OPTIONAL]
-# Comma-separated list of additional TAP resources/end-point.
-#
-# By default, the following standard TAP resources are already existing: /sync,
-# /async, /tables, /capabilities and /availability. With this property, you can
-# add a custom resource to your TAP service (e.g. /adqlValidator, /admin).
-#
-# Each item of the list MUST be the name of a class implementing
-# tap.resource.TAPResource. This class MUST have at least one constructor with
-# exactly one parameter of type tap.resource.TAP.
-#
-# The string returned by tap.resource.TAPResource.getName() will be the
-# resource name, following the root TAP service URL (e.g. if getName() returns
-# "foo", then its access URL will "{tapRoot}/foo"). Then, it is possible to
-# replace TAP resources already existing by using the same name (e.g. if
-# getName() returns "sync", the /sync resource won't be anymore the default Sync
-# resource of this library but your new resource).
-#
-# By default, this list is empty ; only the standard TAP resources exist.
-additional_resources =
-
-######################
-# CUSTOM TAP_FACTORY #
-######################
-
-# [OPTIONAL]
-# Class to use in replacement of the default TAPFactory.
-#
-# This property must be a class name (given between {...}). It must reference an
-# implementation of TAPFactory. This implementation must have at least one
-# constructor with exactly one parameter of type ServiceConnection.
-#
-# It is recommended to extend an existing implementation such as:
-# tap.AbstractTAPFactory or tap.config.ConfigurableTAPFactory.
-#
-# By default, the default TAPFactory (tap.config.ConfigurableTAPFactory) is used
-# and may use all properties related to the backup management, the database
-# access, the TAP_SCHEMA mapping and the ADQL translation.
-tap_factory =
+################################################################################
+# FULL TAP CONFIGURATION FILE #
+# #
+# TAP Version: 2.3 #
+# Date: 20 March. 2019 #
+# Author: Gregory Mantelet (CDS;ARI) #
+# #
+################################################################################
+
+###########
+# GENERAL #
+###########
+
+# [OPTIONAL]
+# This property lets set a custom home page.
+#
+# 4 different kinds of value are accepted:
+# * nothing (default): the default home page provided by the library (just
+# a simple HTML page displaying a list of all available
+# TAP resources).
+# * name or relative path of a file: this method MUST be chosen if the new
+# home page is a JSP file. This file MUST
+# be inside the directory WebContent of
+# your web application.
+# * a URI starting with file://: in this method the local file pointed by
+# the URI will be merely returned when the
+# home page will be requested.
+# * a URL: here, a redirection toward this URL will be made at each request
+# on the home page
+# * a class name: the class name of an extension of tap.resource.HomePage
+# which must replace the default home page resource. This
+# class MUST have at least one constructor with exactly one
+# parameter not NULL of type tap.resource.TAP.
+home_page =
+
+# [OPTIONAL]
+# MIME type of the service home page.
+#
+# This property is used only if the specified "home_page" is a local file path
+# (i.e. if "home_page=file://...").
+#
+# If no value is provided "text/html" will be set by default.
+#
+# Default: text/html
+home_page_mime_type =
+
+############
+# PROVIDER #
+############
+
+# [OPTIONAL]
+# Name of the provider of the TAP Service.
+provider_name = CDS
+
+# [OPTIONAL]
+# Description of the TAP Service.
+service_description = My TAP Service is so amazing! You should use it with your favorite TAP client.
+
+############
+# DATABASE #
+############
+
+# [MANDATORY]
+# Method to use in order to create database connections.
+#
+# Only two values are supported:
+# * jndi: database connections will be supplied by a Datasource whose the
+# JNDI name must be given. This method may propose connection
+# pooling in function of the datasource configuration.
+# * jdbc: the library will create itself connections when they will be
+# needed thanks to the below JDBC parameters. This method does not
+# propose any connection pooling.
+#
+# Allowed values: jndi, jdbc.
+database_access =
+
+# [MANDATORY]
+# The translator to use in order to translate ADQL to a SQL compatible with the
+# used DBMS and its spatial extension.
+#
+# The TAP library supports only Postgresql (no spatial extension),
+# PostgreSQL+PgSphere, SQLServer (no spatial extension), MySQL (no spatial
+# extension) and H2 (no spatial extension) for the moment. But you can provide
+# your own SQL translator (even if it does not have spatial features), by
+# providing the name of a class (within brackets: {...}) that implements
+# ADQLTranslator (for instance: {apackage.MyADQLTranslator}) and which have at
+# least an empty constructor.
+#
+# Allowed values: postgres, pgsphere, sqlserver, mysql, h2, a class name
+sql_translator = postgres
+
+# [OPTIONAL]
+# Size of result blocks to fetch from the database when a ADQL query is executed
+# in Synchronous mode.
+#
+# Rather than fetching a query result in a whole, it may be possible to specify
+# to the database that results may be retrieved by blocks whose the size can be
+# specified with this property. If supported by the DBMS and the JDBC driver,
+# this feature may help sparing memory and avoid too much waiting time from the
+# TAP /sync users (and thus, avoiding some HTTP client timeouts).
+#
+# 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.
+#
+# Default: sync_fetch_size=10000
+sync_fetch_size = 1000
+
+# [OPTIONAL]
+# Size of result blocks to fetch from the database when an ADQL query is
+# executed in Asynchronous mode.
+#
+# Rather than fetching a query result in a whole, it may be possible to specify
+# to the database that results may be retrieved by blocks whose the size can be
+# specified with this property. If supported by the DBMS and the JDBC driver,
+# this feature may help sparing memory.
+#
+# 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.
+#
+# Default: async_fetch_size=100000
+async_fetch_size = 10000
+
+# [OPTIONAL]
+# If enabled, this option lets automatically try fixing a query whose parsing
+# failed because of a token error. This is particularly useful in the following
+# cases:
+#
+# - when a column/table identifier/alias has an incorrect syntax
+# (e.g. `_RAJ2000`, `2mass`)
+# - if the name of an ADQL function is used as a column/table identifier/alias
+# (e.g. `distance`, `point`, `min`)
+# - if the name of a reserved SQL keyword is used as a column/table
+# identifier/alias
+# (e.g. `public`, `date`, `year`, `user`)
+#
+# In all these cases, the name/identifier/alias will be surrounded between
+# double quotes if the option `fix_on_fail` is enabled.
+#
+# This feature is also able to fix some incorrect Unicode characters
+# (e.g. LaTeX alternatives for underscores, spaces and double/single quotes,
+# copy-pasted from a PDF into a TAP query field).
+#
+# Warning: since this option alters the query provided by the query, it is
+# possible that, after fix attempt, the query parses and runs but might
+# generate an output different from what was expected. So, if this
+# option is enabled, it should made be clear to the user that the TAP
+# server might alter the query to make it work.
+#
+# Note: when an input query is fixed and run successfully, the fixed ADQL query
+# is reported in an INFO element named `QUERY_AFTER_AUTO_FIX` inside the
+# output VOTable.
+#
+# Default: fix_on_fail=false
+fix_on_fail = false
+
+#############################
+# IF DATABASE ACCESS = JNDI #
+#############################
+
+# [MANDATORY]
+# JNDI name of the datasource pointing toward the database to use.
+# It should be defined in the web application (e.g. in the META-INF/context.xml
+# file in tomcat).
+datasource_jndi_name =
+
+#############################
+# IF DATABASE ACCESS = JDBC #
+#############################
+
+# [MANDATORY]
+# JDBC driver URL pointing toward the database to use.
+#
+# Note: The username, password or other parameters may be included in it, but
+# in this case, the corresponding properties should leave empty or not
+# provided at all.
+jdbc_url =
+
+# [OPTIONAL]
+# JDBC driver path.
+#
+# By default, it is guessed in function of the database name provided in the
+# jdbc_url property. It MUST be provided if another DBMS is used or if the
+# JDBC driver path does not match the following ones:
+# * Oracle : oracle.jdbc.OracleDriver
+# * PostgreSQL: org.postgresql.Driver
+# * MySQL : com.mysql.jdbc.Driver
+# * SQLite : org.sqlite.JDBC
+# * H2 : org.h2.Driver
+#jdbc_driver =
+
+# [MANDATORY]
+# [Mandatory if the username is not already provided in jdbc_url]
+#
+# Username used to access to the database.
+db_username =
+
+# [MANDATORY]
+# [Mandatory if the password is not already provided in jdbc_url]
+#
+# Password used by db_username to access to the database.
+#
+# Note: No password encryption can be done in this configuration file for the
+# moment.
+db_password =
+
+############
+# METADATA #
+############
+
+# [MANDATORY]
+# Metadata fetching method.
+#
+# The value of this key defines the way the library will get the list of all
+# schemas, tables and columns to publish and all their metadata (e.g. utype,
+# description, type, ...).
+#
+# In its current state, the library proposes three methods:
+# 1/ Parse a TableSet XML document and load its content into the database
+# schema TAP_SCHEMA (note: this schema is first erased and rebuilt by the
+# library).
+# 2/ Get all metadata from the database schema TAP_SCHEMA.
+# 3/ Build yourself the metadata of your service by creating an extension of
+# tap.metadata.TAPMetadata. This extension must have either an empty
+# constructor or a constructor with exactly 3 parameters of type
+# UWSFileManager, TAPFactory and TAPLog ; if both constructor are
+# provided, only the one with parameters will be used.
+#
+# For the two first methods, it is also possible to specify an extension of
+# tap.metadata.TAPMetadata which will wrap a default TAPMetadata objects created
+# using the specified methods (i.e. XML tableset or TAP_SCHEMA). In this way, it
+# is possible to get the "default" metadata from an XML file or the database
+# and then add/remove/modify some of them, or to change the output of the
+# 'tables' resource. The extension of tap.metadata.TAPMetadata must have at
+# least one constructor with the following parameters: (TAPMetadata) or
+# (TAPMetadata, UWSFileManager, TAPFactory, TAPLog).
+#
+# Allowed values: xml, xml {myTAPMetadata}, db, db {myTAPMetadata}
+# or a full class name (between {}).
+metadata =
+
+# [MANDATORY]
+# [Mandatory if the value of "metadata" is "xml".]
+#
+# Local file path to the TableSet XML document.
+#
+# The XML document must implement the schema TableSet defined by VODataService.
+# The file path must be either an absolute local file path or a file path
+# relative to WebContent (i.e. the web application directory in which there are
+# WEB-INF and META-INF).
+metadata_file =
+
+# [OPTIONAL]
+# [ONLY USED IF metadata = db]
+#
+# Mapping between TAP_SCHEMA ADQL names and their names in the database.
+#
+# Any property named exactly (case sensitive) like TAP_SCHEMA items will be
+# considered as a mapping between its ADQL name and its DB name.
+#
+# Examples: "TAP_SCHEMA = TAP_SCHEMA2"
+# or "TAP_SCHEMA.columns.column_name = name"
+#
+# The property value MUST be NOT qualified. Just the item name is required.
+# The value will be used as provided (with the same case).
+#
+# Note:
+# The column dbName in the database TAP_SCHEMA declaring the standard
+# TAP_SCHEMA entries (e.g. TAP_SCHEMA.schemas.dbName) is now ignored. Thus,
+# only the mapping defined here, in the configuration file, is taken into
+# account.
+#
+# TAP_SCHEMA =
+
+#########
+# FILES #
+#########
+
+# [MANDATORY]
+# Type of the file manager.
+#
+# Accepted values are: local (to manage files on the local system). You can also
+# add another way to manage files by providing the name (within brackets: {...})
+# of a class implementing TAPFileManager and having at least one constructor
+# with only a java.util.Properties parameter.
+#
+# Allowed values: local, a class name.
+file_manager = local
+
+# [MANDATORY]
+# Local file path of the directory in which all TAP files (logs, errors, job
+# results, backup, ...) must be.
+#
+# The file path must be either an absolute local directory path or a directory
+# path relative to WebContent (i.e. the web application directory in which there
+# are WEB-INF and META-INF).
+file_root_path =
+
+# [OPTIONAL]
+# Tells whether a directory should be created for each user.
+#
+# If yes, the user directory will be named with the user ID. In this directory,
+# there will be error files, job results and it may be the backup file of the
+# user.
+#
+# Allowed values: true (default), false.
+directory_per_user = true
+
+# [OPTIONAL]
+# Tells whether user directories must be grouped.
+#
+# If yes, directories are grouped by the first letter found in the user ID.
+#
+# Allowed values: true (default), false.
+group_user_dir = true
+
+# [OPTIONAL]
+# The default period (in seconds) to keep query results.
+#
+# The prefix "default" means here that this value is put by default by the TAP
+# Service if the client does not provide a value for it.
+#
+# The default period MUST be less or equals to the maximum retention period. If
+# this rule is not respected, the default retention period is set immediately to
+# the maximum retention period.
+#
+# A negative or null value means there is no restriction on the default
+# retention period: job results will be kept forever. Float values are not
+# allowed.
+#
+# Default: 0 (results kept forever).
+default_retention_period = 0
+
+# [OPTIONAL]
+# The maximum period (in seconds) to keep query results.
+#
+# The prefix "max" means here that the client can not set a retention period
+# greater than this one.
+#
+# The maximum period MUST be greater or equals to the default retention period.
+# If this rule is not respected, the default retention period is set immediately
+# to the maximum retention period.
+#
+# A negative or null value means there is no restriction on the maximum
+# retention period: the job results will be kept forever. Float values are not
+# allowed.
+#
+# Default: 0 (results kept forever).
+max_retention_period = 0
+
+#############
+# LOG FILES #
+#############
+
+# [OPTIONAL]
+# Logging method to use.
+#
+# Only two possibilities are already implemented.
+#
+# * default: default logger provided by the library. Any logged message
+# will be appended in the file 'service.log' inside the root
+# directory of this service (cf property 'file_root_path').
+#
+# * slf4j: wrapper for SLF4J (https://www.slf4j.org). All log messages will
+# be forwarded to SLF4J. It is up to the implementor to add the
+# suitable JAR files in the Java class-path. Exactly two JAR files
+# are expected by SLF4J to work as expected:
+# - slf4j-api-{version}.jar (the main API)
+# - and the slf4j-{binding}-{version}.jar.
+# Depending on the chosen SLF4J binding, you may also add another
+# JAR file (e.g. Log4J, LogBack, ...) in the Java class-path.
+# A configuration file might also be needed. There, it will be
+# possible to configure the the following loggers:
+# - "tap.service" (general/root purpose log),
+# - "tap.service.UWS" (UWS actions),
+# - "tap.service.HTTP" (HTTP requests and responses),
+# - "tap.service.JOB" (UWS's jobs actions),
+# - "tap.service.THREAD" (job's thread actions),
+# - "tap.service.TAP" (TAP actions)
+# - and "tap.service.DB" (DB actions).
+#
+# * {...}: a custom logger. A class name MUST be provided
+# (between {...}). The specified class must reference
+# an implementation of tap.log.TAPLog. This implementation
+# must have at least one constructor with a single parameter of
+# type uws.service.file.UWSFileManager.
+#
+# Default: 'default' (i.e. tap.log.DefaultTAPLog)
+logger =
+
+# [OPTIONAL]
+# Minimum level that a message must have in order to be logged by the default
+# logger.
+#
+# 5 possible values:
+# * DEBUG: every messages are logged.
+# * INFO: every messages EXCEPT DEBUG are logged.
+# * WARNING: every messages EXCEPT DEBUG and INFO are logged.
+# * ERROR: only ERROR and FATAL messages are logged.
+# * FATAL: only FATAL messages are logged.
+#
+# Note: this property is ignored if `logger != default`.
+#
+# Default: DEBUG (every messages are logged)
+min_log_level =
+
+# [OPTIONAL]
+# Frequency of the log file rotation performed by the default logger.
+# That's to say, logs will be written in a new file after this period. This
+# avoid having too big log files. Old log files are renamed so that highlighting
+# its logging period.
+#
+# The frequency string must respect the following syntax:
+# 'D' hh mm: daily schedule at hh:mm
+# 'W' dd hh mm: weekly schedule at the given day of the week
+# (1:sunday, 2:monday, ..., 7:saturday) at hh:mm
+# 'M' dd hh mm: monthly schedule at the given day of the month at hh:mm
+# 'h' mm: hourly schedule at the given minute
+# 'm': scheduled every minute (for completness :-))
+#
+# Where: hh = integer between 0 and 23,
+# mm = integer between 0 and 59,
+# dd (for 'W') = integer between 1 and 7
+# (1:sunday, 2:monday, ..., 7:saturday),
+# dd (for 'M') = integer between 1 and 31.
+#
+# Warning: The frequency type is case sensitive! Then you should particularly
+# pay attention at the case when using the frequency types 'M'
+# (monthly) and 'm' (every minute).
+#
+# Note 1: this property is ignored if the file manager is not any more an
+# extension of uws.service.file.LocalUWSFileManager.
+#
+# Note 2: this property is ignored if `logger != default`.
+#
+# Default: D 0 0 (daily at midnight)
+log_rotation =
+
+##############
+# UWS_BACKUP #
+##############
+
+# [OPTIONAL]
+# Frequency at which the UWS service (that's to say, all its users and jobs)
+# must be backuped.
+#
+# Allowed values: never (no backup will never be done ; default), user_action
+# (each time a user does a writing action, like creating or
+# execution a job), a time (must be positive and not null) in
+# milliseconds.
+#
+# The value user_action can be used ONLY IF backup_by_user=true.
+#
+# Default: never
+backup_frequency = never
+
+# [OPTIONAL]
+# Tells whether the backup must be one file for every user (false), or one file
+# for each user (true). This second option should be chosen if your TAP Service
+# is organizing its files by user directories ; see the property
+# directory_per_user.
+#
+# This option can be enabled ONLY IF a user identification method is provided ;
+# see property user_identifier.
+#
+# Default: false
+backup_by_user = false
+
+#####################
+# ASYNCHRONOUS JOBS #
+#####################
+
+# [OPTIONAL]
+# Maximum number of asynchronous jobs that can run simultaneously.
+#
+# A negative or null value means there is no restriction on the number of
+# running asynchronous jobs.
+#
+# Default: there is no restriction => max_async_jobs=0.
+max_async_jobs = 0
+
+###################
+# QUERY_EXECUTION #
+###################
+
+# [OPTIONAL]
+# Default time (in milliseconds) for query execution.
+#
+# The prefix "default" means here that the execution duration will be this one
+# if the client does not set one.
+#
+# The default duration MUST be less or equals to the maximum execution duration.
+# If this rule is not respected, the default execution duration is set
+# immediately to the maximum execution duration.
+#
+# A negative or null value means there is no restriction on the default
+# execution duration: the execution could never end. Float values are not
+# allowed.
+#
+# Default: there is no restriction => default_execution_duration=0.
+default_execution_duration = 0
+
+# [OPTIONAL]
+# Maximum time (in milliseconds) for query execution.
+#
+# The prefix "max" means here that the client can not set a time greater than
+# this one.
+#
+# The maximum duration MUST be greater or equals to the default execution
+# duration. If this rule is not respected, the default execution duration is set
+# immediately to the maximum execution duration.
+#
+# A negative or null value means there is no restriction on the maximum
+# execution duration: the execution could never end. Float values are not
+# allowed.
+#
+# Default: there is no restriction => max_execution_duration=0.
+max_execution_duration = 0
+
+##########
+# OUTPUT #
+##########
+
+# [OPTIONAL]
+# Comma separated list of output formats for query results.
+#
+# Allowed values are: votable (or 'vot'), fits, text, csv, tsv, json and html.
+#
+# The VOTable format may be more detailed with the following syntax:
+# (serialization,version):mime_type:short_mime_type.
+# The MIME type part and the parameters part may be omitted
+# (e.g. votable:application/xml:votable , votable(td,1.3)]).
+# Empty string values are allowed for each values (e.g. votable():: ,
+# votable(td)::votable).
+#
+# The default VOTable format (i.e. serialization and version) is the one defined
+# with the short form `votable`. It is be default set to
+# `vot(binary,1.3)::votable` (see the special value `ALL` below). To change it
+# just define a VOTable format with the short form `votable`.
+#
+# It is also possible to define a custom Separated Value format, different from
+# CSV and TSV, thanks to the following syntax:
+# sv(separator):mime_type:short_mime_type. On the contrary to the VOTable
+# syntax, the parameter (i.e. separator) MUST BE provided. The MIME type part
+# may be omitted ; then the MIME type will be set by default to text/plain.
+#
+# There is finally a last possible value: a class name of a class implementing
+# OutputFormat and having at least one constructor with exactly one parameter of
+# type tap.ServiceConnection.
+#
+# The special value "ALL" will select all formats provided by the library. It is
+# equivalent to the following:
+# output_formats = vot(binary,1.3)::votable, vot(td,1.3)::votable/td,
+# vot(binary,1.3)::votable/b, vot(binary2,1.3)::votable/b2,
+# vot(fits,1.3)::votable/fits, fits, csv, tsv, text, html,
+# json
+#
+# Default: ALL
+output_formats = ALL
+
+# [OPTIONAL]
+# Default limit for the result output.
+#
+# The prefix "default" means here that this value will be set if the client does
+# not provide one.
+#
+# This limit can be expressed in only one unit: rows.
+#
+# A negative value means there is no restriction on this limit. Float values are
+# not allowed.
+#
+# Obviously this limit MUST be less or equal than output_max_limit.
+#
+# Default: there is no restriction: output_default_limit=-1
+output_default_limit = -1
+
+# [OPTIONAL]
+# Maximum limit for the result output. The prefix "max" means here that the
+# client can not set a limit greater than this one.
+#
+# This limit can be expressed in only one unit: rows.
+#
+# A negative value means there is no restriction on this limit. Float values are
+# not allowed.
+#
+# Obviously this limit MUST be greater or equal than output_default_limit.
+#
+# Default: there is no restriction => output_max_limit=-1
+output_max_limit = -1
+
+##########
+# UPLOAD #
+##########
+
+# [OPTIONAL]
+# Tells whether the Upload must be enabled.
+#
+# If enabled, files can be uploaded in the file_root_path, the corresponding
+# tables can be added inside the UPLOAD_SCHEMA of the database, queried and then
+# deleted.
+#
+# NOTE: Before being stored in the directory file_root_path, it is first
+# uploaded in the temporary directory (defined in the JVM ; generally
+# `/tmp` on Unix system and `c:\temp` ; it can be changed at start of the
+# JVM with the property `java.io.tmpdir`). When the upload is complete,
+# the file is finally moved in file_root_path.
+#
+# Allowed values: true, false (default).
+upload_enabled = false
+
+# [OPTIONAL]
+# Maximum limit for the number of uploaded records that can be inserted inside
+# the database.
+#
+# This limit can be expressed with 2 types: rows or bytes. For rows, you just
+# have to suffix the value by a "r" (upper- or lower-case), with nothing (by
+# default, nothing will mean "rows"). For bytes, you have to suffix the numeric
+# value by "b", "kb", "Mb" or "Gb". Here, unit is case sensitive (except for the
+# last character: "b"). No other storage unit is allowed.
+#
+# A negative value means there is no restriction on this limit. Float values are
+# not allowed.
+#
+# IMPORTANT NOTE: the specified limit will be checked at a different step of
+# a query processing in function of its unit.
+# If expressed in bytes, the file size will be checked when
+# uploading the file on disk. Thus, when the uploading file
+# starts to exceed the set limit, it will be no longer uploaded
+# and the whole request will be immediately rejected.
+# On the contrary, if the limit is expressed in rows, it will
+# be tested only when ingesting the whole uploaded file
+# (whatever is its size) in the database ; so, after it has been
+# uploaded. As soon as, the rows insertion in the database
+# exceeds the limit, the query is rejected.
+# Consequently, a very huge file could potentially be
+# completely uploaded before being rejected if this property is
+# expressed in rows. Then, it is very important to set the
+# property `upload_max_request_size` limiting the size of a
+# whole HTTP request in order to better preserve your machine
+# from running out of disk space.
+#
+# Default: upload_max_db_limit=1000000r (i.e. 1 million rows)
+upload_max_db_limit = 1000000r
+
+
+# [OPTIONAL]
+# Maximum allowed size for a whole HTTP multipart request (i.e. request with
+# uploads).
+#
+# This limit MUST be expressed in bytes. Thus, you have to suffix the numeric
+# value by "B", "kB", "MB" or "GB". Here, unit is case sensitive. No other
+# storage unit is allowed.
+#
+# A negative value means there is no restriction on this limit. Float values are
+# not allowed.
+#
+# Warning: It is highly recommended to set this property in order to prevent
+# exceeding the disk storage space/quota (especially if
+# `upload_max_db_limit` is not set or is set in rows).
+#
+# Default: upload_max_request_size=250MB
+upload_max_request_size = 250MB
+
+# [OPTIONAL ; DEPRECATED since v2.3]
+# Default limit for the number of uploaded records that can be inserted inside
+# the database.
+#
+# This property is DEPRECATED. You should use `upload_max_db_limit` instead.
+# If it is set anyway, its value will be used as value for
+# `upload_max_db_limit` ONLY IF this latter is not set.
+#
+# Default: upload_default_db_limit=-1 (i.e. unlimited)
+upload_default_db_limit = -1
+
+# [OPTIONAL ; DEPRECATED since v2.3]
+# Maximum allowed size for the uploaded file.
+#
+# This property is DEPRECATED. You should use `upload_max_db_limit` with a value
+# expressed in bytes if you wanted to limit the size of each uploaded file, or
+# `upload_max_request_size` if your goal was to limit the input HTTP request
+# size. If it is set anyway, its value will be used as value for
+# `upload_max_request_size` ONLY IF this latter is not set.
+#
+# Default: upload_max_file_size=-1 (i.e. unlimited)
+upload_max_file_size = -1
+
+#######################
+# USER IDENTIFICATION #
+#######################
+
+# [OPTIONAL]
+# Class to use in order to identify a user of the TAP service.
+#
+# The same instance of this class will be used for every request sent to the
+# service.
+#
+# The value of this property MUST be a class name (with brackets: {...}) of a
+# class implementing the interface uws.service.UserIdentifier. This class MUST
+# have one of its constructors with no parameter.
+#
+# Default: no identification is performed => all users are then anonymous and
+# their jobs can be seen by everybody.
+user_identifier =
+
+######################
+# COORDINATE SYSTEMS #
+######################
+
+# [OPTIONAL]
+# Comma-separated list of all allowed coordinate systems.
+#
+# Each item of the list be a kind of regular expression respecting the following
+# syntax: Frame RefPos Flavor. In other words, it must be a string of exactly 3
+# parts. Each of this part is a single value, a list of allowed values
+# or a * meaning all values. A list of values must be indicated between
+# parenthesis and values must be separated by a pipe.
+#
+# Allowed values for Frame are: ICRS, FK4, FK5, J2000, ECLIPTIC, GALACTIC
+# and UNKNOWNFRAME.
+# Allowed values for RefPos are: BARYCENTER, GEOCENTER, HELIOCENTER, LSR,
+# TOPOCENTER, RELOCATABLE and UNKNOWNREFPOS.
+# Allowed values for Flavor are: CARTESIAN2, CARTESIAN3 and SPHERICAL2.
+#
+# If the special value NONE is given instead of a list of allowed coordinate
+# systems, no coordinate system will be allowed. And if the list is empty, any
+# coordinate system will be allowed.
+#
+# By default, any coordinate system is allowed.
+coordinate_systems =
+
+##############
+# GEOMETRIES #
+##############
+
+# [OPTIONAL]
+# Comma-separated list of all allowed geometries.
+#
+# Each item of the list must be the name (whatever is the case) of an ADQL
+# geometrical function (e.g. INTERSECTS, COORDSYS, POINT) to allow.
+# If the list is empty (no item), all functions are allowed. And if the special
+# value NONE is given, no ADQL function will be allowed.
+#
+# Default: all ADQL geometrical functions are allowed.
+geometries =
+
+#################################
+# USER DEFINED FUNCTIONS (UDFs) #
+#################################
+
+# [OPTIONAL]
+# Comma-separated list of all allowed UDFs (User Defined Functions).
+#
+# Each item of the list must have the following syntax: [fct_signature],
+# [fct_signature, className] or [fct_signature, className, description].
+# fct_function is the function signature. Its syntax is the same as in
+# TAPRegExt. className is the name of a class extending UserDefinedFunction.
+# An instance of this class will replace any reference of a UDF written in an
+# ADQL function with the associated signature. A class name must be specified if
+# the function to represent has a signature (and more particularly a name)
+# different in ADQL and in SQL. description is the human description of the
+# function to be displayed in the /capabilities of the TAP service. It must be
+# written between double quotes.
+#
+# Example: udfs = [ivo_healpix_index(hpxOrder integer, ra double, dec double)
+# -> bigint, {adql.query.operand.function.healpix.HealpixIndex}
+# , "Compute the index of the \"Healpix cell\" containing the
+# specified position at the given Healpix order."],
+# [trim(txt String) -> String],
+# [newFct(x double)->double, {apackage.MyNewFunction}],
+# [random() -> DOUBLE,,"Generate a random number."]
+#
+# If the list is empty (no item), all unknown functions are forbidden. And if
+# the special value ANY is given, any unknown function is allowed ; consequently
+# the unknown ADQL functions will be translated into SQL as they are in ADQL.
+#
+# Default: no unknown function is allowed.
+udfs =
+
+########################
+# ADDITIONAL RESOURCES #
+########################
+
+# [OPTIONAL]
+# URL of the XSLT stylesheet to link with the XML output of /capabilities.
+#
+# By default, no XSLT stylesheet is defined.
+capabilities_stylesheet =
+
+# [OPTIONAL]
+# URL of the XSLT stylesheet to link with the XML output of /tables.
+#
+# By default, no XSLT stylesheet is defined.
+tables_stylesheet =
+
+# [OPTIONAL]
+# This property lets add an /examples
endpoint by specifying an
+# XHTML-RDFa document listing TAP query examples using the syntax specified by
+# TAPNotes 1.0 DALI 1.0.
+#
+# 3 different kinds of value are accepted:
+# * nothing (default): no /examples endpoint.
+# * name or relative path of a file: this method MUST be chosen if the
+# endpoint content is a JSP file. This file MUST be inside the directory
+# WebContent of your web application.
+# * URI starting with file://: in this method the local file pointed by the
+# URI will be merely returned when the endpoint will be requested.
+# * a URL: here, a redirection toward this URL will be made at each request
+# on the endpoint
+#
+# If you want a custom /examples endpoint (i.e. you do not)
+# want to forward/redirect to a file/URL), you can create a class which
+# implements TAPResource AND VOSIResource. The function getName() must return
+# "examples". Then, just append the classpath to the property
+# "additional_resources" of the TAP configuration file.
+#
+# By default, the TAP service does not have any /examples
endpoint.
+examples =
+
+# [OPTIONAL]
+# Comma-separated list of additional TAP resources/end-point.
+#
+# By default, the following standard TAP resources are already existing: /sync,
+# /async, /tables, /capabilities and /availability. With this property, you can
+# add a custom resource to your TAP service (e.g. /adqlValidator, /admin).
+#
+# Each item of the list MUST be the name of a class implementing
+# tap.resource.TAPResource. This class MUST have at least one constructor with
+# exactly one parameter of type tap.resource.TAP.
+#
+# The string returned by tap.resource.TAPResource.getName() will be the
+# resource name, following the root TAP service URL (e.g. if getName() returns
+# "foo", then its access URL will "{tapRoot}/foo"). Then, it is possible to
+# replace TAP resources already existing by using the same name (e.g. if
+# getName() returns "sync", the /sync resource won't be anymore the default Sync
+# resource of this library but your new resource).
+#
+# By default, this list is empty ; only the standard TAP resources exist.
+additional_resources =
+
+######################
+# CUSTOM TAP_FACTORY #
+######################
+
+# [OPTIONAL]
+# Class to use in replacement of the default TAPFactory.
+#
+# This property must be a class name (given between {...}). It must reference an
+# implementation of TAPFactory. This implementation must have at least one
+# constructor with exactly one parameter of type ServiceConnection.
+#
+# It is recommended to extend an existing implementation such as:
+# tap.AbstractTAPFactory or tap.config.ConfigurableTAPFactory.
+#
+# By default, the default TAPFactory (tap.config.ConfigurableTAPFactory) is used
+# and may use all properties related to the backup management, the database
+# access, the TAP_SCHEMA mapping and the ADQL translation.
+tap_factory =
diff --git a/src/tap/config/tap_min.properties b/src/tap/config/tap_min.properties
index 26d4d6a2..08e0f115 100644
--- a/src/tap/config/tap_min.properties
+++ b/src/tap/config/tap_min.properties
@@ -1,149 +1,149 @@
-################################################################################
-# MINIMUM TAP CONFIGURATION FILE #
-# #
-# TAP Version: 2.1 #
-# Date: 22 Feb. 2018 #
-# Author: Gregory Mantelet (ARI) #
-# #
-################################################################################
-
-############
-# DATABASE #
-############
-
-# Method to use in order to create database connections.
-#
-# Only two values are supported:
-# * jndi: database connections will be supplied by a Datasource whose the
-# JNDI name must be given. This method may propose connection
-# pooling in function of the datasource configuration.
-# * jdbc: the library will create itself connections when they will be
-# needed thanks to the below JDBC parameters. This method does not
-# propose any connection pooling.
-#
-# Allowed values: jndi, jdbc.
-database_access =
-
-# The translator to use in order to translate ADQL to a SQL compatible with the
-# used DBMS and its spatial extension.
-#
-# The TAP library supports only Postgresql (no spatial extension),
-# PostgreSQL+PgSphere, SQLServer (no spatial extension) and MySQL (no spatial
-# extension) for the moment. But you can provide your own SQL translator (even
-# if it does not have spatial features), by providing the name of a class
-# (within brackets: {...}) that implements ADQLTranslator (for instance:
-# {apackage.MyADQLTranslator}) and which have at least an empty constructor.
-#
-# Allowed values: postgres, pgsphere, sqlserver, mysql, a class name
-sql_translator = postgres
-
-#############################
-# IF DATABASE ACCESS = JNDI #
-#############################
-
-# JNDI name of the datasource pointing toward the database to use.
-# It should be defined in the web application (e.g. in the META-INF/context.xml
-# file in tomcat).
-datasource_jndi_name =
-
-#############################
-# IF DATABASE ACCESS = JDBC #
-#############################
-
-# JDBC driver URL pointing toward the database to use.
-#
-# Note: The username, password or other parameters may be included in it, but
-# in this case, the corresponding properties should leave empty or not
-# provided at all.
-jdbc_url =
-
-# JDBC driver path.
-#
-# By default, it is guessed in function of the database name provided in the
-# jdbc_url property. It MUST be provided if another DBMS is used or if the
-# JDBC driver path does not match the following ones:
-# * Oracle : oracle.jdbc.OracleDriver
-# * PostgreSQL: org.postgresql.Driver
-# * MySQL : com.mysql.jdbc.Driver
-# * SQLite : org.sqlite.JDBC
-# * H2 : org.h2.Driver
-jdbc_driver =
-
-# [Mandatory if the username is not already provided in jdbc_url]
-#
-# Username used to access to the database.
-db_user =
-
-# [Mandatory if the password is not already provided in jdbc_url]
-#
-# Password used by db_username to access to the database.
-#
-# Note: No password encryption can be done in this configuration file for the
-# moment.
-db_password =
-
-############
-# METADATA #
-############
-
-# Metadata fetching method.
-#
-# The value of this key defines the way the library will get the list of all
-# schemas, tables and columns to publish and all their metadata (e.g. utype,
-# description, type, ...).
-#
-# In its current state, the library proposes three methods:
-# 1/ Parse a TableSet XML document and load its content into the database
-# schema TAP_SCHEMA (note: this schema is first erased and rebuilt by the
-# library).
-# 2/ Get all metadata from the database schema TAP_SCHEMA.
-# 3/ Build yourself the metadata of your service by creating an extension of
-# tap.metadata.TAPMetadata. This extension must have either an empty
-# constructor or a constructor with exactly 3 parameters of type
-# UWSFileManager, TAPFactory and TAPLog ; if both constructor are
-# provided, only the one with parameters will be used.
-#
-# For the two first methods, it is also possible to specify an extension of
-# tap.metadata.TAPMetadata which will wrap a default TAPMetadata objects created
-# using the specified methods (i.e. XML tableset or TAP_SCHEMA). In this way, it
-# is possible to get the "default" metadata from an XML file or the database
-# and then add/remove/modify some of them, or to change the output of the
-# 'tables' resource. The extension of tap.metadata.TAPMetadata must have at
-# least one constructor with the following parameters: (TAPMetadata) or
-# (TAPMetadata, UWSFileManager, TAPFactory, TAPLog).
-#
-# Allowed values: xml, xml {myTAPMetadata}, db, db {myTAPMetadata}
-# or a full class name (between {}).
-metadata =
-
-# [Mandatory if the value of "metadata" is "xml".]
-#
-# Local file path to the TableSet XML document.
-#
-# The XML document must implement the schema TableSet defined by VODataService.
-# The file path must be either an absolute local file path or a file path
-# relative to WebContent (i.e. the web application directory in which there are
-# WEB-INF and META-INF).
-metadata_file =
-
-#########
-# FILES #
-#########
-
-# Type of the file manager.
-#
-# Accepted values are: local (to manage files on the local system). You can also
-# add another way to manage files by providing the name (within brackets: {...})
-# of a class implementing TAPFileManager and having at least one constructor
-# with only a java.util.Properties parameter.
-#
-# Allowed values: local, a class name.
-file_manager = local
-
-# Local file path of the directory in which all TAP files (logs, errors, job
-# results, backup, ...) must be.
-#
-# The file path must be either an absolute local directory path or a directory
-# path relative to WebContent (i.e. the web application directory in which there
-# are WEB-INF and META-INF).
-file_root_path =
+################################################################################
+# MINIMUM TAP CONFIGURATION FILE #
+# #
+# TAP Version: 2.1 #
+# Date: 22 Feb. 2018 #
+# Author: Gregory Mantelet (ARI) #
+# #
+################################################################################
+
+############
+# DATABASE #
+############
+
+# Method to use in order to create database connections.
+#
+# Only two values are supported:
+# * jndi: database connections will be supplied by a Datasource whose the
+# JNDI name must be given. This method may propose connection
+# pooling in function of the datasource configuration.
+# * jdbc: the library will create itself connections when they will be
+# needed thanks to the below JDBC parameters. This method does not
+# propose any connection pooling.
+#
+# Allowed values: jndi, jdbc.
+database_access =
+
+# The translator to use in order to translate ADQL to a SQL compatible with the
+# used DBMS and its spatial extension.
+#
+# The TAP library supports only Postgresql (no spatial extension),
+# PostgreSQL+PgSphere, SQLServer (no spatial extension) and MySQL (no spatial
+# extension) for the moment. But you can provide your own SQL translator (even
+# if it does not have spatial features), by providing the name of a class
+# (within brackets: {...}) that implements ADQLTranslator (for instance:
+# {apackage.MyADQLTranslator}) and which have at least an empty constructor.
+#
+# Allowed values: postgres, pgsphere, sqlserver, mysql, a class name
+sql_translator = postgres
+
+#############################
+# IF DATABASE ACCESS = JNDI #
+#############################
+
+# JNDI name of the datasource pointing toward the database to use.
+# It should be defined in the web application (e.g. in the META-INF/context.xml
+# file in tomcat).
+datasource_jndi_name =
+
+#############################
+# IF DATABASE ACCESS = JDBC #
+#############################
+
+# JDBC driver URL pointing toward the database to use.
+#
+# Note: The username, password or other parameters may be included in it, but
+# in this case, the corresponding properties should leave empty or not
+# provided at all.
+jdbc_url =
+
+# JDBC driver path.
+#
+# By default, it is guessed in function of the database name provided in the
+# jdbc_url property. It MUST be provided if another DBMS is used or if the
+# JDBC driver path does not match the following ones:
+# * Oracle : oracle.jdbc.OracleDriver
+# * PostgreSQL: org.postgresql.Driver
+# * MySQL : com.mysql.jdbc.Driver
+# * SQLite : org.sqlite.JDBC
+# * H2 : org.h2.Driver
+jdbc_driver =
+
+# [Mandatory if the username is not already provided in jdbc_url]
+#
+# Username used to access to the database.
+db_user =
+
+# [Mandatory if the password is not already provided in jdbc_url]
+#
+# Password used by db_username to access to the database.
+#
+# Note: No password encryption can be done in this configuration file for the
+# moment.
+db_password =
+
+############
+# METADATA #
+############
+
+# Metadata fetching method.
+#
+# The value of this key defines the way the library will get the list of all
+# schemas, tables and columns to publish and all their metadata (e.g. utype,
+# description, type, ...).
+#
+# In its current state, the library proposes three methods:
+# 1/ Parse a TableSet XML document and load its content into the database
+# schema TAP_SCHEMA (note: this schema is first erased and rebuilt by the
+# library).
+# 2/ Get all metadata from the database schema TAP_SCHEMA.
+# 3/ Build yourself the metadata of your service by creating an extension of
+# tap.metadata.TAPMetadata. This extension must have either an empty
+# constructor or a constructor with exactly 3 parameters of type
+# UWSFileManager, TAPFactory and TAPLog ; if both constructor are
+# provided, only the one with parameters will be used.
+#
+# For the two first methods, it is also possible to specify an extension of
+# tap.metadata.TAPMetadata which will wrap a default TAPMetadata objects created
+# using the specified methods (i.e. XML tableset or TAP_SCHEMA). In this way, it
+# is possible to get the "default" metadata from an XML file or the database
+# and then add/remove/modify some of them, or to change the output of the
+# 'tables' resource. The extension of tap.metadata.TAPMetadata must have at
+# least one constructor with the following parameters: (TAPMetadata) or
+# (TAPMetadata, UWSFileManager, TAPFactory, TAPLog).
+#
+# Allowed values: xml, xml {myTAPMetadata}, db, db {myTAPMetadata}
+# or a full class name (between {}).
+metadata =
+
+# [Mandatory if the value of "metadata" is "xml".]
+#
+# Local file path to the TableSet XML document.
+#
+# The XML document must implement the schema TableSet defined by VODataService.
+# The file path must be either an absolute local file path or a file path
+# relative to WebContent (i.e. the web application directory in which there are
+# WEB-INF and META-INF).
+metadata_file =
+
+#########
+# FILES #
+#########
+
+# Type of the file manager.
+#
+# Accepted values are: local (to manage files on the local system). You can also
+# add another way to manage files by providing the name (within brackets: {...})
+# of a class implementing TAPFileManager and having at least one constructor
+# with only a java.util.Properties parameter.
+#
+# Allowed values: local, a class name.
+file_manager = local
+
+# Local file path of the directory in which all TAP files (logs, errors, job
+# results, backup, ...) must be.
+#
+# The file path must be either an absolute local directory path or a directory
+# path relative to WebContent (i.e. the web application directory in which there
+# are WEB-INF and META-INF).
+file_root_path =
diff --git a/src/tap/formatter/VOTableFormat.java b/src/tap/formatter/VOTableFormat.java
index c641a6f9..c870007a 100644
--- a/src/tap/formatter/VOTableFormat.java
+++ b/src/tap/formatter/VOTableFormat.java
@@ -415,6 +415,13 @@ protected void writeHeader(final VOTableVersion votVersion, final TAPExecutionRe
out.newLine();
}
+ // Append the fixed ADQL query, if any: [OPTIONAL]
+ String fixedQuery = execReport.fixedQuery;
+ if (adqlQuery != null){
+ out.write("