Skip to content
Snippets Groups Projects
Commit 8102b083 authored by gmantele's avatar gmantele
Browse files

[TAP] Auto-detect the RegTAP datamodel from the schemas and tables declared

in TAP_SCHEMA.

RegTAP is detected successfully if the schema 'rr' exists (case sensitive)
and contains at least the following tables (names also case sensitive):
	- capability
	- interface
	- intf_param
	- relationship
	- res_date
	- res_detail
	- res_role
	- res_schema
	- res_subject
	- res_table
	- resource
	- table_column
	- validation

The table name can be prefixed by 'rr' (case sensitive) or not. For instance:
'rr.capability' and 'capability' are both detected successfully.

All these constraints (including the case sensitive one) are based on the
requirements of the RegTAP-1.0 standard document. They are set in order
to not declare the RegTAP DM by accident AND to provide a first low
validation of the RegTAP schema and tables. Low validation because
columns (as well as datatype, utypes, indices and UDF functions) are
never checked.

This commit resolves the GitHub issue #51
parent 09ee79c4
No related branches found
No related tags found
No related merge requests found
...@@ -24,6 +24,7 @@ import java.io.IOException; ...@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
...@@ -38,7 +39,6 @@ import javax.servlet.http.HttpServletResponse; ...@@ -38,7 +39,6 @@ import javax.servlet.http.HttpServletResponse;
import adql.db.DBTable; import adql.db.DBTable;
import adql.db.DBType; import adql.db.DBType;
import adql.db.DBType.DBDatatype; import adql.db.DBType.DBDatatype;
import tap.db.JDBCConnection;
import tap.metadata.TAPTable.TableType; import tap.metadata.TAPTable.TableType;
import tap.resource.Capabilities; import tap.resource.Capabilities;
import tap.resource.TAPResource; import tap.resource.TAPResource;
...@@ -52,18 +52,27 @@ import uws.UWSToolBox; ...@@ -52,18 +52,27 @@ import uws.UWSToolBox;
* This list also corresponds to the TAP resource "/tables".</p> * This list also corresponds to the TAP resource "/tables".</p>
* *
* <p> * <p>
* Only schemas are stored in this object. So that's why only schemas can be added and removed * Only schemas are stored in this object. So that's why only schemas can be
* from this class. However, {@link TAPSchema} objects are listing tables, whose the object * added and removed from this class. However, {@link TAPSchema} objects are
* representation is listing columns. So to add tables, you must first embed them in a schema. * listing tables, whose the object representation is listing columns. So to
* add tables, you must first embed them in a schema.
* </p> * </p>
* *
* <p> * <p>
* All metadata have two names: one to use in ADQL queries and the other to use when really querying * All metadata have two names: one to use in ADQL queries and the other to use
* the database. This is very useful to hide the real complexity of the database and propose * when really querying the database. This is very useful to hide the real
* a simpler view of the query-able data. It is particularly useful if a schema does not exist in the * complexity of the database and propose a simpler view of the query-able
* database but has been added in the TAP schema for more logical separation on the user point of view. * data. It is particularly useful if a schema does not exist in the database
* In a such case, the schema would have an ADQL name but no DB name (NULL value ; which is possible only * but has been added in the TAP schema for more logical separation on the user
* with {@link TAPSchema} objects). * point of view. In a such case, the schema would have an ADQL name but no DB
* name (NULL value ; which is possible only with {@link TAPSchema} objects).
* </p>
*
* <p>
* This class lets also detect the ObsCore and RegTAP data models, thanks to
* the functions {@link #getObsCoreTable()} and {@link #getRegTAPSchema()}
* which return resp. the ObsCore table and the RegTAP schema if matching the
* IVOA specification.
* </p> * </p>
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
...@@ -401,11 +410,12 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour ...@@ -401,11 +410,12 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
* This function is case sensitive only on the schema name * This function is case sensitive only on the schema name
* (i.e. <code>ivoa</code>) which must be defined in full lower case. * (i.e. <code>ivoa</code>) which must be defined in full lower case.
* The table name (i.e. <code>ObsCore</code>) will be found whatever * The table name (i.e. <code>ObsCore</code>) will be found whatever
* the case it is written in. * the case it is written in and whether it is prefixed or not.
* </p> * </p>
* *
* @return Description of the ObsCore table, * @return Description of the ObsCore table,
* or <code>NULL</code> if this table is not provided by this TAP service. * or <code>NULL</code> if this table is not provided by this TAP
* service.
* *
* @since 2.1 * @since 2.1
*/ */
...@@ -413,13 +423,51 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour ...@@ -413,13 +423,51 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
TAPSchema ivoaSchema = getSchema("ivoa"); TAPSchema ivoaSchema = getSchema("ivoa");
if (ivoaSchema != null){ if (ivoaSchema != null){
for(TAPTable t : ivoaSchema){ for(TAPTable t : ivoaSchema){
if (t.getADQLName().equalsIgnoreCase("obscore")) if (t.getADQLName().matches("(?i)(ivoa\\.)?obscore"))
return t; return t;
} }
} }
return null; return null;
} }
/**
* Get the description of the RegTAP schema, if it is defined.
*
* <p>
* This function is case sensitive only on the schema name
* (i.e. <code>rr</code>) which must be defined in full lower case.
* The tables name (e.g. <code>capability</code>) will be searched case
* sensitively as well. However, it does not matter if the table name is
* prefixed or not.
* </p>
*
* @return Description of the RegTAP schema,
* or <code>NULL</code> if RegTAP is not supported in this TAP
* service.
*
* @since 2.1
*/
public TAPSchema getRegTAPSchema(){
// Get the RegTAP schema if existing:
TAPSchema rrSchema = getSchema("rr");
// If there is one, check its content:
if (rrSchema != null){
// search for all mandatory tables of RegTAP:
HashSet<String> matchedTables = new HashSet<String>(13);
for(TAPTable t : rrSchema){
if (t.getADQLName().matches("(rr\\.)?(capability|interface|intf_param|relationship|res_date|res_detail|res_role|res_schema|res_subject|res_table|resource|table_column|validation)"))
matchedTables.add(t.getADQLName());
}
// if all columns have been found, return the RegTAP schema:
if (matchedTables.size() == 13)
return rrSchema;
}
// NULL, if no schema rr or if it does match the IVOA specification:
return null;
}
/** /**
* Get the number of all tables contained in this TAP metadata set. * Get the number of all tables contained in this TAP metadata set.
* *
...@@ -895,7 +943,7 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour ...@@ -895,7 +943,7 @@ public class TAPMetadata implements Iterable<TAPSchema>, VOSIResource, TAPResour
/** /**
* Get the minimum definition of the table TAP_SCHEMA.coosys as expected by * Get the minimum definition of the table TAP_SCHEMA.coosys as expected by
* the library (see {@link JDBCConnection#getTAPSchema()}. * the library (see {@link tap.db.JDBCConnection#getTAPSchema()}.
* *
* @return The created definition of TAP_SCHEMA.coosys. * @return The created definition of TAP_SCHEMA.coosys.
* *
......
...@@ -39,6 +39,7 @@ import tap.error.DefaultTAPErrorWriter; ...@@ -39,6 +39,7 @@ import tap.error.DefaultTAPErrorWriter;
import tap.formatter.OutputFormat; import tap.formatter.OutputFormat;
import tap.log.TAPLog; import tap.log.TAPLog;
import tap.metadata.TAPMetadata; import tap.metadata.TAPMetadata;
import tap.metadata.TAPSchema;
import tap.metadata.TAPTable; import tap.metadata.TAPTable;
import uk.ac.starlink.votable.VOSerializer; import uk.ac.starlink.votable.VOSerializer;
import uws.UWSException; import uws.UWSException;
...@@ -55,7 +56,7 @@ import uws.service.log.UWSLog.LogLevel; ...@@ -55,7 +56,7 @@ import uws.service.log.UWSLog.LogLevel;
* <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p> * <p>At its creation it is creating and configuring the other resources in function of the given description of the TAP service.</p>
* *
* @author Gr&eacute;gory Mantelet (CDS;ARI) * @author Gr&eacute;gory Mantelet (CDS;ARI)
* @version 2.1 (03/2017) * @version 2.1 (09/2017)
*/ */
public class TAP implements VOSIResource { public class TAP implements VOSIResource {
...@@ -634,6 +635,7 @@ public class TAP implements VOSIResource { ...@@ -634,6 +635,7 @@ public class TAP implements VOSIResource {
* <p>Currently, only the following DMs are natively supported:</p> * <p>Currently, only the following DMs are natively supported:</p>
* <ul> * <ul>
* <li>Obscore (1.0 and PR-1.1)</li> * <li>Obscore (1.0 and PR-1.1)</li>
* <li>RegTAP (1.0)</li>
* </ul> * </ul>
* *
* <p> * <p>
...@@ -653,7 +655,10 @@ public class TAP implements VOSIResource { ...@@ -653,7 +655,10 @@ public class TAP implements VOSIResource {
* @since 2.1 * @since 2.1
*/ */
protected void appendDataModels(final StringBuffer xml, final String linePrefix){ protected void appendDataModels(final StringBuffer xml, final String linePrefix){
// ObsCore:
appendObsCoreDM(xml, linePrefix); appendObsCoreDM(xml, linePrefix);
// RegTAP:
appendRegTAPDM(xml, linePrefix);
} }
/** /**
...@@ -676,8 +681,8 @@ public class TAP implements VOSIResource { ...@@ -676,8 +681,8 @@ public class TAP implements VOSIResource {
* If not, the Obscore table will be declared as Obscore 1.0. * If not, the Obscore table will be declared as Obscore 1.0.
* </p> * </p>
* *
* @param xml The <code>/capabilities</code> in-progress content in which * @param xml The <code>/capabilities</code> in-progress content
* Obscore-DM should be declared if found. * in which Obscore-DM should be declared if found.
* @param linePrefix Tabulations/Spaces that should prefix all lines * @param linePrefix Tabulations/Spaces that should prefix all lines
* (for human readability). * (for human readability).
* *
...@@ -714,6 +719,41 @@ public class TAP implements VOSIResource { ...@@ -714,6 +719,41 @@ public class TAP implements VOSIResource {
} }
} }
/**
* <p>Append the RegTAP DM declaration in the given {@link StringBuffer}
* if a schema <code>rr</code> can be found in <code>TAP_SCHEMA</code>
* with all its required tables.</p>
*
* <p>
* This function has no effect if the schema <code>rr</code> or its
* mandatory children tables can not be found. The research is done
* case sensitively by {@link TAPMetadata#getRegTAPSchema()}.
* </p>
*
* <p>
* If there is a valid schema <code>rr</code>, this function
* detects automatically which version of RegTAP is implemented. For the
* moment only one is supported: RegTAP-1.0.
* </p>
*
* @param xml The <code>/capabilities</code> in-progress content
* in which RegTAP-DM should be declared if found.
* @param linePrefix Tabulations/Spaces that should prefix all lines
* (for human readability).
*
* @see TAPMetadata#getRegTAPTable()
*
* @since 2.1
*/
protected void appendRegTAPDM(final StringBuffer xml, final String linePrefix){
// Try to get the RegTAP schema definition:
TAPSchema regtap = service.getTAPMetadata().getRegTAPSchema();
// If there is one, determine the supported DM version and declare it:
if (regtap != null)
xml.append(linePrefix + "<dataModel ivo-id=\"ivo://ivoa.net/std/RegTAP#1.0\">Registry 1.0</dataModel>\n");
}
/* ************************************* */ /* ************************************* */
/* MANAGEMENT OF THIS RESOURCE'S CONTENT */ /* MANAGEMENT OF THIS RESOURCE'S CONTENT */
/* ************************************* */ /* ************************************* */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment