From 6f154e19c59ff751bb5880dc9a11c0ca13968a9e Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Thu, 1 Sep 2016 15:06:13 +0200 Subject: [PATCH] [TAP] Let specify a mapping for TAP_SCHEMA between ADQL and DB names. This is very helpful if TAP_SCHEMA (or some of its tables and their columns) has a different name in the database. The mapping can be specified to JDBCConnection or directly in the configuration file. --- .../config/ConfigurableServiceConnection.java | 45 +++++++--- src/tap/config/tap_configuration_file.html | 27 +++++- src/tap/config/tap_full.properties | 23 ++++- src/tap/db/JDBCConnection.java | 86 ++++++++++++++++++- 4 files changed, 161 insertions(+), 20 deletions(-) diff --git a/src/tap/config/ConfigurableServiceConnection.java b/src/tap/config/ConfigurableServiceConnection.java index 341f577..fff3625 100644 --- a/src/tap/config/ConfigurableServiceConnection.java +++ b/src/tap/config/ConfigurableServiceConnection.java @@ -86,13 +86,19 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.Properties; +import adql.db.FunctionDef; +import adql.db.STCS; +import adql.parser.ParseException; +import adql.query.operand.function.UserDefinedFunction; import tap.ServiceConnection; import tap.TAPException; import tap.TAPFactory; import tap.db.DBConnection; +import tap.db.JDBCConnection; import tap.formatter.FITSFormat; import tap.formatter.HTMLFormat; import tap.formatter.JSONFormat; @@ -111,10 +117,6 @@ import uws.service.UserIdentifier; import uws.service.file.LocalUWSFileManager; import uws.service.file.UWSFileManager; import uws.service.log.UWSLog.LogLevel; -import adql.db.FunctionDef; -import adql.db.STCS; -import adql.parser.ParseException; -import adql.query.operand.function.UserDefinedFunction; /** * <p>Concrete implementation of {@link ServiceConnection}, fully parameterized with a TAP configuration file.</p> @@ -125,7 +127,7 @@ import adql.query.operand.function.UserDefinedFunction; * </p> * * @author Grégory Mantelet (ARI) - * @version 2.1 (02/2016) + * @version 2.1 (09/2016) * @since 2.0 */ public final class ConfigurableServiceConnection implements ServiceConnection { @@ -329,7 +331,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { * <p> * If not an absolute URI, the given path may be either relative or absolute. A relative path is always considered * as relative from the Web Application directory (supposed to be given in 2nd parameter). - * </p> + * </p> * * @param filePath URI/Path/Name of the file to get. * @param webAppRootPath Web Application directory local path. @@ -482,7 +484,22 @@ public final class ConfigurableServiceConnection implements ServiceConnection { else if (metaFetchType.equalsIgnoreCase(VALUE_DB)){ DBConnection conn = null; try{ + // get a db connection: conn = tapFactory.getConnection("GET_TAP_SCHEMA"); + + // fetch and set the ADQL<->DB mapping for all standard TAP_SCHEMA items: + if (conn instanceof JDBCConnection){ + HashMap<String,String> dbMapping = new HashMap<String,String>(10); + // fetch the mapping from the Property file: + for(String key : tapConfig.stringPropertyNames()){ + if (key.trim().startsWith("TAP_SCHEMA") && tapConfig.getProperty(key) != null && tapConfig.getProperty(key).trim().length() > 0) + dbMapping.put(key.trim(), tapConfig.getProperty(key)); + } + // set the mapping into the DB connection: + ((JDBCConnection)conn).setDBMapping(dbMapping); + } + + // fetch TAP_SCHEMA: metadata = conn.getTAPSchema(); }finally{ if (conn != null) @@ -970,7 +987,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { Object[] limit = parseLimit(propValue, KEY_UPLOAD_MAX_FILE_SIZE, true); if (((Integer)limit[0]).intValue() <= 0) limit[0] = new Integer(TAPConfiguration.DEFAULT_UPLOAD_MAX_FILE_SIZE); - // ...check that the unit is correct (bytes): + // ...check that the unit is correct (bytes): if (!LimitUnit.bytes.isCompatibleWith((LimitUnit)limit[1])) throw new TAPException("The maximum upload file size " + KEY_UPLOAD_MAX_FILE_SIZE + " (here: " + propValue + ") can not be expressed in a unit different from bytes (B, kB, MB, GB)!"); // ...set the max file size: @@ -1135,10 +1152,12 @@ public final class ConfigurableServiceConnection implements ServiceConnection { char c; int ind = 0; short nbComma = 0; - boolean within_item = false, within_params = false, within_classpath = false; + boolean within_item = false, within_params = false, + within_classpath = false; StringBuffer buf = new StringBuffer(); String signature, classpath; - int[] posSignature = new int[]{-1,-1}, posClassPath = new int[]{-1,-1}; + int[] posSignature = new int[]{-1,-1}, + posClassPath = new int[]{-1,-1}; signature = null; classpath = null; @@ -1307,7 +1326,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { * In other words, if the given value is less or equals to the current maximum retention period. * </em></p> * - * @param period New default retention period (in seconds). + * @param period New default retention period (in seconds). * * @return <i>true</i> if the given retention period has been successfully set, <i>false</i> otherwise. */ @@ -1355,7 +1374,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { * In other words, if the given value is less or equals to the current maximum execution duration. * </em></p> * - * @param duration New default execution duration (in milliseconds). + * @param duration New default execution duration (in milliseconds). * * @return <i>true</i> if the given execution duration has been successfully set, <i>false</i> otherwise. */ @@ -1451,7 +1470,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { * In other words, if the given value is less or equals to the current maximum output limit. * </em></p> * - * @param limit New default output limit (in number of rows). + * @param limit New default output limit (in number of rows). * * @return <i>true</i> if the given output limit has been successfully set, <i>false</i> otherwise. */ @@ -1546,7 +1565,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection { * In other words, if the given value is less or equals to the current maximum upload limit. * </em></p> * - * @param limit New default upload limit. + * @param limit New default upload limit. * * @return <i>true</i> if the given upload limit has been successfully set, <i>false</i> otherwise. */ diff --git a/src/tap/config/tap_configuration_file.html b/src/tap/config/tap_configuration_file.html index 5edc7fa..df438c9 100644 --- a/src/tap/config/tap_configuration_file.html +++ b/src/tap/config/tap_configuration_file.html @@ -302,6 +302,31 @@ </td> <td><ul><li>/home/foo/my_metadata.xml</li><li>my_metadata.xml</li><li>WEB-INF/my_metadata.xml</li></ul></td> </tr> + <tr class="optional"> + <td class="done">TAP_SCHEMA...</td> + <td></td> + <td>text</td> + <td> + <p><strong>Only used if <code>metadata = db</code></strong>.</p> + <p>Mapping between TAP_SCHEMA ADQL names and their names in the database.</p> + <p> + <b>Any property named exactly (case sensitive) like TAP_SCHEMA items</b> will be considered + as a mapping between its ADQL name and its DB name. + </p> + <p> + The property value MUST be NOT qualified. Just the item name is required. + The value will be used as provided (with the same case). + </p> + </td> + <td> + <ul> + <li>TAP_SCHEMA = myTAPSchema</li> + <li>TAP_SCHEMA.tables = tap_tables</li> + <li>TAP_SCHEMA.columns.column_name = name</li> + <li>...</li> + </ul> + </td> + </tr> <tr><td colspan="5">Files</td></tr> <tr class="mandatory"> @@ -758,7 +783,7 @@ tap.AbstractTAPFactory or tap.config.ConfigurableTAPFactory. </p> <p><em>By default, the default TAPFactory (tap.config.ConfigurableTAPFactory) is used and may use all properties related to the backup management, - the database access and the ADQL translation.</em></p> + the database access, the TAP_SCHEMA mapping and the ADQL translation.</em></p> </td> <td>{aPackage.MyTAPFactory}</td> </tr> diff --git a/src/tap/config/tap_full.properties b/src/tap/config/tap_full.properties index 770f78e..5bbedbf 100644 --- a/src/tap/config/tap_full.properties +++ b/src/tap/config/tap_full.properties @@ -2,7 +2,7 @@ # FULL TAP CONFIGURATION FILE # # # # TAP Version: 2.1 # -# Date: 18 July 2016 # +# Date: 1 Sept. 2016 # # Author: Gregory Mantelet (ARI) # # # ########################################################## @@ -158,14 +158,29 @@ db_password = # 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 = +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 = +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). +# +# TAP_SCHEMA = ######### # FILES # @@ -568,5 +583,5 @@ additional_resources = # 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 and the ADQL translation. +# the database access, the TAP_SCHEMA mapping and the ADQL translation. tap_factory = diff --git a/src/tap/db/JDBCConnection.java b/src/tap/db/JDBCConnection.java index 15cda97..d65886f 100644 --- a/src/tap/db/JDBCConnection.java +++ b/src/tap/db/JDBCConnection.java @@ -177,7 +177,7 @@ import uws.service.log.UWSLog.LogLevel; * </i></p> * * @author Grégory Mantelet (CDS;ARI) - * @version 2.1 (07/2016) + * @version 2.1 (09/2016) * @since 2.0 */ public class JDBCConnection implements DBConnection { @@ -302,6 +302,17 @@ public class JDBCConnection implements DBConnection { * <p><i>Note 2: if this feature is enabled (i.e. has a value > 0), the AutoCommit will be disabled.</i></p> */ protected int fetchSize = DEFAULT_FETCH_SIZE; + /* TAP_SCHEMA MAPPING */ + + /** Mapping of the TAP_SCHEMA items between their ADQL name and their name in the database. + * <p><b>IMPORTANT:</b> + * Keys of the map MUST be the full ADQL name of an item (e.g. TAP_SCHEMA, TAP_SCHEMA.tables, TAP_SCHEMA.columns.ucd). + * Values MUST be the name of the corresponding item in the database. + * Keys and values are case sensitive. + * </p> + * @since 2.1 */ + protected Map<String,String> dbMapping = null; + /** * <p>Creates a JDBC connection to the specified database and with the specified JDBC driver. * This connection is established using the given user name and password.<p> @@ -792,6 +803,77 @@ public class JDBCConnection implements DBConnection { } } + /** + * Let specify for all item of the standard TAP_SCHEMA a different name in the database. + * <p><i> + * For instance: if in the database "TAP_SCHEMA" is called "MY_TAP_SCHEMA". + * </i></p> + * + * <p><b>IMPORTANT:</b> + * TAP_SCHEMA items (i.e. keys in the map) MUST be fully qualified ADQL names (e.g. TAP_SCHEMA.columns.name). + * The values MUST be single database names (i.e. no catalogue, schema or table prefix). + * Both keys and values are case sensitive. + * </p> + * + * <p><i>Note:</i> + * TAP_SCHEMA items keeping the same name in the database than in ADQL do not need to + * be listed in the given map. + * </p> + * + * @param mapping Mapping between ADQL names and DB names. + * If <code>null</code>, DB names will be considered equals to the ADQL names. + * + * @since 2.1 + */ + public void setDBMapping(final Map<String,String> mapping){ + if (mapping == null) + dbMapping = null; + else{ + if (dbMapping == null) + dbMapping = new HashMap<String,String>(mapping.size()); + else + dbMapping.clear(); + dbMapping.putAll(mapping); + if (dbMapping.size() == 0) + dbMapping = null; + } + } + + /** + * Get the standard definition of TAP_SCHEMA with eventually DB names provided by the set mapping (see {@link #setDBMapping(Map)}). + * + * @return The standard schema as it should be detected in the database. + * + * @since 2.1 + */ + protected TAPSchema getStdSchema(){ + TAPSchema tap_schema = TAPMetadata.getStdSchema(supportsSchema); + + if (dbMapping != null){ + // Update the TAP_SCHEMA DB name, if needed: + if (dbMapping.containsKey(tap_schema.getADQLName())) + tap_schema.setDBName(dbMapping.get(tap_schema.getADQLName())); + + // For each table... + for(TAPTable t : tap_schema){ + // ...update the table DB name, if needed: + if (dbMapping.containsKey(t.getFullName())) + t.setDBName(dbMapping.get(t.getFullName())); + + // For each column... + String fullName; + for(DBColumn c : t){ + fullName = t.getFullName() + "." + c.getADQLName(); + // ...update the column DB name, if needed: + if (dbMapping.containsKey(fullName)) + ((TAPColumn)c).setDBName(dbMapping.get(fullName)); + } + } + } + + return tap_schema; + } + /* ************************************ */ /* GETTING TAP_SCHEMA FROM THE DATABASE */ /* ************************************ */ @@ -822,7 +904,7 @@ public class JDBCConnection implements DBConnection { TAPMetadata metadata = new TAPMetadata(); // Get the definition of the standard TAP_SCHEMA tables: - TAPSchema tap_schema = TAPMetadata.getStdSchema(supportsSchema); + TAPSchema tap_schema = getStdSchema(); // LOAD ALL METADATA FROM THE STANDARD TAP TABLES: try{ -- GitLab