From f8e64d37049b869662ef745b1f1e6f2a40e2ad24 Mon Sep 17 00:00:00 2001 From: gmantele <gmantele@ari.uni-heidelberg.de> Date: Fri, 4 Apr 2014 11:34:35 +0200 Subject: [PATCH] TAP+TEST: First steps with the TAP configuration file management (Factory, ServiceConnection, but not yet Metadata) + their JUnit tests + documentation (the html file + configuration file examples) --- src/tap/config/DefaultServiceConnection.java | 308 ++++++++++++ src/tap/config/DefaultTAPFactory.java | 172 +++++++ src/tap/config/TAPConfiguration.java | 161 ++++++ src/tap/config/gums_table.txt | 49 ++ src/tap/config/tap_configuration_file.html | 469 ++++++++++++++++++ src/tap/config/tap_full.properties | 255 ++++++++++ src/tap/config/tap_min.properties | 66 +++ test/tap/config/AllTests.java | 11 + .../config/TestDefaultServiceConnection.java | 268 ++++++++++ test/tap/config/TestDefaultTAPFactory.java | 300 +++++++++++ test/tap/config/tap_bad_db_name.properties | 64 +++ .../tap/config/tap_bad_jdbc_driver.properties | 64 +++ test/tap/config/tap_bad_password.properties | 64 +++ test/tap/config/tap_bad_translator.properties | 64 +++ test/tap/config/tap_bad_username.properties | 64 +++ test/tap/config/tap_fm_clp.properties | 94 ++++ test/tap/config/tap_incorrect_fm.properties | 64 +++ test/tap/config/tap_no_fm.properties | 64 +++ .../config/tap_no_jdbc_driver_1.properties | 64 +++ .../config/tap_no_jdbc_driver_2.properties | 64 +++ test/tap/config/tap_valid.properties | 64 +++ 21 files changed, 2793 insertions(+) create mode 100644 src/tap/config/DefaultServiceConnection.java create mode 100644 src/tap/config/DefaultTAPFactory.java create mode 100644 src/tap/config/TAPConfiguration.java create mode 100644 src/tap/config/gums_table.txt create mode 100644 src/tap/config/tap_configuration_file.html create mode 100644 src/tap/config/tap_full.properties create mode 100644 src/tap/config/tap_min.properties create mode 100644 test/tap/config/AllTests.java create mode 100644 test/tap/config/TestDefaultServiceConnection.java create mode 100644 test/tap/config/TestDefaultTAPFactory.java create mode 100644 test/tap/config/tap_bad_db_name.properties create mode 100644 test/tap/config/tap_bad_jdbc_driver.properties create mode 100644 test/tap/config/tap_bad_password.properties create mode 100644 test/tap/config/tap_bad_translator.properties create mode 100644 test/tap/config/tap_bad_username.properties create mode 100644 test/tap/config/tap_fm_clp.properties create mode 100644 test/tap/config/tap_incorrect_fm.properties create mode 100644 test/tap/config/tap_no_fm.properties create mode 100644 test/tap/config/tap_no_jdbc_driver_1.properties create mode 100644 test/tap/config/tap_no_jdbc_driver_2.properties create mode 100644 test/tap/config/tap_valid.properties diff --git a/src/tap/config/DefaultServiceConnection.java b/src/tap/config/DefaultServiceConnection.java new file mode 100644 index 0000000..f6a8b26 --- /dev/null +++ b/src/tap/config/DefaultServiceConnection.java @@ -0,0 +1,308 @@ +package tap.config; + +import static tap.config.TAPConfiguration.*; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.sql.ResultSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Properties; + +import tap.ServiceConnection; +import tap.TAPException; +import tap.TAPFactory; +import tap.file.LocalTAPFileManager; +import tap.file.TAPFileManager; +import tap.formatter.OutputFormat; +import tap.log.DefaultTAPLog; +import tap.log.TAPLog; +import tap.metadata.TAPMetadata; +import uws.UWSException; +import uws.service.UserIdentifier; + +public final class DefaultServiceConnection implements ServiceConnection<ResultSet> { + + private TAPFileManager fileManager; + + private TAPLog logger; + + private DefaultTAPFactory tapFactory; + + private final String providerName; + private final String serviceDescription; + + private boolean isAvailable = false; + private String availability = null; + + private int[] executionDuration = new int[2]; + private int[] retentionPeriod = new int[2]; + + public DefaultServiceConnection(final Properties tapConfig) throws NullPointerException, TAPException, UWSException{ + // 1. INITIALIZE THE FILE MANAGER: + initFileManager(tapConfig); + + // 2. CREATE THE LOGGER: + logger = new DefaultTAPLog(fileManager); + + // 3. BUILD THE TAP FACTORY: + tapFactory = new DefaultTAPFactory(this, tapConfig); + + // 4. SET ALL GENERAL SERVICE CONNECTION INFORMATION: + providerName = getProperty(tapConfig, KEY_PROVIDER_NAME); + serviceDescription = getProperty(tapConfig, KEY_SERVICE_DESCRIPTION); + availability = getProperty(tapConfig, KEY_DISABILITY_REASON); + initRetentionPeriod(tapConfig); + initExecutionDuration(tapConfig); + + // 5. MAKE THE SERVICE AVAILABLE (or not, depending on the property value): + String propValue = getProperty(tapConfig, KEY_IS_AVAILABLE); + isAvailable = (propValue == null) ? DEFAULT_IS_AVAILABLE : Boolean.parseBoolean(propValue); + } + + private void initFileManager(final Properties tapConfig) throws TAPException{ + // Read the desired file manager: + String fileManagerType = getProperty(tapConfig, KEY_FILE_MANAGER); + if (fileManagerType == null) + throw new TAPException("The property \"" + KEY_FILE_MANAGER + "\" is missing! It is required to create a TAP Service. Two possible values: " + VALUE_LOCAL + " or a class path between {...}."); + else + fileManagerType = fileManagerType.trim(); + + // LOCAL file manager: + if (fileManagerType.equalsIgnoreCase(VALUE_LOCAL)){ + // Read the desired root path: + String rootPath = getProperty(tapConfig, KEY_FILE_ROOT_PATH); + if (rootPath == null) + throw new TAPException("The property \"" + KEY_FILE_ROOT_PATH + "\" is missing! It is required to create a TAP Service. Please provide a path toward a directory which will contain all files related to the service."); + File rootFile = new File(rootPath); + + // Determine whether there should be one directory for each user: + String propValue = getProperty(tapConfig, KEY_DIRECTORY_PER_USER); + boolean oneDirectoryPerUser = (propValue == null) ? DEFAULT_DIRECTORY_PER_USER : Boolean.parseBoolean(propValue); + + // Determine whether there should be one directory for each user: + propValue = getProperty(tapConfig, KEY_GROUP_USER_DIRECTORIES); + boolean groupUserDirectories = (propValue == null) ? DEFAULT_GROUP_USER_DIRECTORIES : Boolean.parseBoolean(propValue); + + // Build the Local TAP File Manager: + try{ + fileManager = new LocalTAPFileManager(rootFile, oneDirectoryPerUser, groupUserDirectories); + }catch(UWSException e){ + throw new TAPException("The property \"" + KEY_FILE_ROOT_PATH + "\" (" + rootPath + ") is incorrect: " + e.getMessage()); + } + } + // CUSTOM file manager: + else{ + Class<TAPFileManager> classObj = fetchClass(fileManagerType, KEY_FILE_MANAGER, TAPFileManager.class); + if (classObj == null) + throw new TAPException("Unknown value for the propertie \"" + KEY_FILE_MANAGER + "\": \"" + fileManagerType + "\". Only two possible values: " + VALUE_LOCAL + " or a class path between {...}."); + + try{ + fileManager = classObj.getConstructor(Properties.class).newInstance(tapConfig); + }catch(InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e){ + throw new TAPException("Impossible to create a TAPFileManager instance with the constructor (java.util.Properties tapConfig) of \"" + classObj.getName() + "\" for the following reason: " + e.getMessage()); + } + } + } + + private void initRetentionPeriod(final Properties tapConfig){ + retentionPeriod = new int[2]; + + // Set the default period: + String propValue = getProperty(tapConfig, KEY_DEFAULT_RETENTION_PERIOD); + try{ + retentionPeriod[0] = (propValue == null) ? DEFAULT_RETENTION_PERIOD : Integer.parseInt(propValue); + }catch(NumberFormatException nfe){ + retentionPeriod[0] = DEFAULT_RETENTION_PERIOD; + } + + // Set the maximum period: + propValue = getProperty(tapConfig, KEY_MAX_RETENTION_PERIOD); + try{ + retentionPeriod[1] = (propValue == null) ? DEFAULT_RETENTION_PERIOD : Integer.parseInt(propValue); + }catch(NumberFormatException nfe){ + retentionPeriod[1] = DEFAULT_RETENTION_PERIOD; + } + + // The maximum period MUST be greater or equals than the default period. + // If not, the default period is set (so decreased) to the maximum period. + if (retentionPeriod[1] > 0 && retentionPeriod[1] < retentionPeriod[0]) + retentionPeriod[0] = retentionPeriod[1]; + } + + private void initExecutionDuration(final Properties tapConfig){ + executionDuration = new int[2]; + + // Set the default duration: + String propValue = getProperty(tapConfig, KEY_DEFAULT_EXECUTION_DURATION); + try{ + executionDuration[0] = (propValue == null) ? DEFAULT_EXECUTION_DURATION : Integer.parseInt(propValue); + }catch(NumberFormatException nfe){ + executionDuration[0] = DEFAULT_EXECUTION_DURATION; + } + + // Set the maximum duration: + propValue = getProperty(tapConfig, KEY_MAX_EXECUTION_DURATION); + try{ + executionDuration[1] = (propValue == null) ? DEFAULT_EXECUTION_DURATION : Integer.parseInt(propValue); + }catch(NumberFormatException nfe){ + executionDuration[1] = DEFAULT_EXECUTION_DURATION; + } + + // The maximum duration MUST be greater or equals than the default duration. + // If not, the default duration is set (so decreased) to the maximum duration. + if (executionDuration[1] > 0 && executionDuration[1] < executionDuration[0]) + executionDuration[0] = executionDuration[1]; + } + + @Override + public String getProviderName(){ + return providerName; + } + + @Override + public String getProviderDescription(){ + return serviceDescription; + } + + @Override + public boolean isAvailable(){ + return isAvailable; + } + + public void setAvailability(final boolean isAvailable){ + this.isAvailable = isAvailable; + } + + @Override + public String getAvailability(){ + return availability; + } + + public void setDisabilityReason(final String disabilityReason){ + availability = disabilityReason; + } + + @Override + public int[] getRetentionPeriod(){ + return retentionPeriod; + } + + public boolean setDefaultRetentionPeriod(final int period){ + if ((retentionPeriod[1] <= 0) || (period > 0 && period <= retentionPeriod[1])){ + retentionPeriod[0] = period; + return true; + }else + return false; + } + + public boolean setMaxRetentionPeriod(final int period){ + if (period <= 0 || (retentionPeriod[0] > 0 && period >= retentionPeriod[0])){ + retentionPeriod[1] = period; + return true; + }else + return false; + } + + @Override + public int[] getExecutionDuration(){ + return executionDuration; + } + + public boolean setDefaultExecutionDuration(final int period){ + if ((executionDuration[1] <= 0) || (period > 0 && period <= executionDuration[1])){ + executionDuration[0] = period; + return true; + }else + return false; + } + + public boolean setMaxExecutionDuration(final int period){ + if (period <= 0 || (executionDuration[0] > 0 && period >= executionDuration[0])){ + executionDuration[1] = period; + return true; + }else + return false; + } + + @Override + public int[] getOutputLimit(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public tap.ServiceConnection.LimitUnit[] getOutputLimitType(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public UserIdentifier getUserIdentifier(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean uploadEnabled(){ + // TODO Auto-generated method stub + return false; + } + + @Override + public int[] getUploadLimit(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public tap.ServiceConnection.LimitUnit[] getUploadLimitType(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public int getMaxUploadSize(){ + // TODO Auto-generated method stub + return 0; + } + + @Override + public TAPMetadata getTAPMetadata(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public Collection<String> getCoordinateSystems(){ + return null; + } + + @Override + public TAPLog getLogger(){ + return logger; + } + + @Override + public TAPFactory<ResultSet> getFactory(){ + return tapFactory; + } + + @Override + public TAPFileManager getFileManager(){ + return fileManager; + } + + @Override + public Iterator<OutputFormat<ResultSet>> getOutputFormats(){ + // TODO Auto-generated method stub + return null; + } + + @Override + public OutputFormat<ResultSet> getOutputFormat(String mimeOrAlias){ + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/tap/config/DefaultTAPFactory.java b/src/tap/config/DefaultTAPFactory.java new file mode 100644 index 0000000..dc1e380 --- /dev/null +++ b/src/tap/config/DefaultTAPFactory.java @@ -0,0 +1,172 @@ +package tap.config; + +import static tap.config.TAPConfiguration.*; + +import java.lang.reflect.InvocationTargetException; +import java.sql.ResultSet; +import java.util.Properties; + +import adql.translator.ADQLTranslator; +import adql.translator.PgSphereTranslator; +import adql.translator.PostgreSQLTranslator; +import tap.AbstractTAPFactory; +import tap.ServiceConnection; +import tap.TAPException; +import tap.backup.DefaultTAPBackupManager; +import tap.db.DBConnection; +import tap.db.JDBCConnection; +import uws.UWSException; +import uws.service.UWSService; +import uws.service.backup.UWSBackupManager; + +public final class DefaultTAPFactory extends AbstractTAPFactory<ResultSet> { + + private Class<? extends ADQLTranslator> translator; + + private final String driverPath; + private final String dbUrl; + private final String dbUser; + private final String dbPassword; + + private boolean backupByUser; + private long backupFrequency; + + @SuppressWarnings("unchecked") + public DefaultTAPFactory(ServiceConnection<ResultSet> service, final Properties tapConfig) throws NullPointerException, TAPException{ + super(service); + + /* 0. Extract the DB type and deduce the JDBC Driver path */ + String jdbcDriver = getProperty(tapConfig, KEY_JDBC_DRIVER); + String dbUrl = null; + if (jdbcDriver == null){ + dbUrl = getProperty(tapConfig, KEY_JDBC_URL); + if (dbUrl == null) + throw new TAPException("JDBC URL missing."); + else if (!dbUrl.startsWith(JDBCConnection.JDBC_PREFIX + ":")) + throw new TAPException("JDBC URL format incorrect! It MUST begins with " + JDBCConnection.JDBC_PREFIX + ":"); + else{ + String dbType = dbUrl.substring(JDBCConnection.JDBC_PREFIX.length() + 1); + if (dbType.indexOf(':') <= 0) + throw new TAPException("JDBC URL format incorrect! Database type name is missing."); + dbType = dbType.substring(0, dbType.indexOf(':')); + + jdbcDriver = VALUE_JDBC_DRIVERS.get(dbType); + if (jdbcDriver == null) + throw new TAPException("No JDBC driver known for the DBMS \"" + dbType + "\"!"); + } + } + + /* 1. Set the ADQLTranslator to use in function of the sql_translator property */ + String sqlTranslator = getProperty(tapConfig, KEY_SQL_TRANSLATOR); + // case a.) no translator specified + if (sqlTranslator == null || sqlTranslator.isEmpty()) + throw new TAPException("No SQL translator specified !"); + + // case b.) PostgreSQL translator + else if (sqlTranslator.equalsIgnoreCase(VALUE_POSTGRESQL)) + translator = PostgreSQLTranslator.class; + + // case c.) PgSphere translator + else if (sqlTranslator.equals(VALUE_PGSPHERE)) + translator = PgSphereTranslator.class; + + // case d.) a client defined ADQLTranslator (with the provided class path) + else if (sqlTranslator.charAt(0) == '{' && sqlTranslator.charAt(sqlTranslator.length() - 1) == '}'){ + sqlTranslator = sqlTranslator.substring(1, sqlTranslator.length() - 2); + try{ + translator = (Class<? extends ADQLTranslator>)ClassLoader.getSystemClassLoader().loadClass(sqlTranslator); + }catch(ClassNotFoundException cnfe){ + throw new TAPException("Unable to load the SQL Translator! The class specified by the property sql_translator (" + sqlTranslator + ") can not be found."); + }catch(ClassCastException cce){ + throw new TAPException("Unable to load the SQL Translator! The class specified by the property sql_translator (" + sqlTranslator + ") is not implementing adql.translator.ADQLTranslator."); + } + } + // case e.) unsupported value + else + throw new TAPException("Unsupported value for the property sql_translator: \"" + sqlTranslator + "\" !"); + + /* 2. Test the construction of the ADQLTranslator */ + createADQLTranslator(); + + /* 3. Store the DB connection parameters */ + this.driverPath = jdbcDriver; + this.dbUrl = dbUrl; + this.dbUser = getProperty(tapConfig, KEY_DB_USERNAME);; + this.dbPassword = getProperty(tapConfig, KEY_DB_PASSWORD); + + /* 4. Test the DB connection */ + DBConnection<ResultSet> dbConn = createDBConnection("0"); + dbConn.close(); + + /* 5. Set the UWS Backup Parameter */ + // BACKUP FREQUENCY: + String propValue = getProperty(tapConfig, KEY_BACKUP_FREQUENCY); + boolean isTime = false; + // determine whether the value is a time period ; if yes, set the frequency: + if (propValue != null){ + try{ + backupFrequency = Long.parseLong(propValue); + if (backupFrequency > 0) + isTime = true; + }catch(NumberFormatException nfe){} + } + // if the value was not a valid numeric time period, try to identify the different textual options: + if (!isTime){ + if (propValue != null && propValue.equalsIgnoreCase(VALUE_USER_ACTION)) + backupFrequency = DefaultTAPBackupManager.AT_USER_ACTION; + else + backupFrequency = DEFAULT_BACKUP_FREQUENCY; + } + // BACKUP BY USER: + propValue = getProperty(tapConfig, KEY_BACKUP_BY_USER); + backupByUser = (propValue == null) ? DEFAULT_BACKUP_BY_USER : Boolean.parseBoolean(propValue); + } + + /** + * Build an {@link ADQLTranslator} instance with the given class ({@link #translator} ; + * specified by the property sql_translator). If the instance can not be build, + * whatever is the reason, a TAPException MUST be thrown. + * + * Note: This function is called at the initialization of {@link DefaultTAPFactory} + * in order to check that a translator can be created. + * + * @see tap.TAPFactory#createADQLTranslator() + */ + @Override + public ADQLTranslator createADQLTranslator() throws TAPException{ + try{ + return translator.getConstructor().newInstance(); + }catch(InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e){ + throw new TAPException("Impossible to create an ADQLTranslator instance with the empty constructor of \"" + translator.getName() + "\" (see the property sql_translator) for the following reason: " + e.getMessage()); + } + } + + /** + * Build a {@link JDBCConnection} thanks to the database parameters specified + * in the TAP configuration file (the properties: jdbc_driver_path, db_url, db_user, db_password). + * + * @see tap.TAPFactory#createDBConnection(java.lang.String) + * @see JDBCConnection + */ + @Override + public DBConnection<ResultSet> createDBConnection(String jobID) throws TAPException{ + return new JDBCConnection(jobID, driverPath, dbUrl, dbUser, dbPassword, this.service.getLogger()); + } + + /** + * Build an {@link DefaultTAPBackupManager} thanks to the backup manager parameters specified + * in the TAP configuration file (the properties: backup_frequency, backup_by_user). + * + * Note: If the specified backup_frequency is negative, no backup manager is returned. + * + * @return null if the specified backup frequency is negative, or an instance of {@link DefaultTAPBackupManager} otherwise. + * + * @see tap.AbstractTAPFactory#createUWSBackupManager(uws.service.UWSService) + * @see DefaultTAPBackupManager + */ + @Override + public UWSBackupManager createUWSBackupManager(UWSService uws) throws TAPException, UWSException{ + return (backupFrequency < 0) ? null : new DefaultTAPBackupManager(uws, backupByUser, backupFrequency); + } + +} diff --git a/src/tap/config/TAPConfiguration.java b/src/tap/config/TAPConfiguration.java new file mode 100644 index 0000000..75c12d1 --- /dev/null +++ b/src/tap/config/TAPConfiguration.java @@ -0,0 +1,161 @@ +package tap.config; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Properties; + +import tap.TAPException; +import tap.backup.DefaultTAPBackupManager; + +public final class TAPConfiguration { + + /* FILE MANAGER KEYS */ + public final static String KEY_FILE_MANAGER = "file_manager"; + public final static String VALUE_LOCAL = "local"; + public final static String DEFAULT_FILE_MANAGER = VALUE_LOCAL; + public final static String KEY_FILE_ROOT_PATH = "file_root_path"; + public final static String KEY_DIRECTORY_PER_USER = "directory_per_user"; + public final static boolean DEFAULT_DIRECTORY_PER_USER = false; + public final static String KEY_GROUP_USER_DIRECTORIES = "group_user_directories"; + public final static boolean DEFAULT_GROUP_USER_DIRECTORIES = false; + public final static String KEY_DEFAULT_RETENTION_PERIOD = "default_retention_period"; + public final static String KEY_MAX_RETENTION_PERIOD = "max_retention_period"; + public final static int DEFAULT_RETENTION_PERIOD = 0; + + /* UWS BACKUP */ + public final static String KEY_BACKUP_FREQUENCY = "backup_frequency"; + public final static String VALUE_USER_ACTION = "user_action"; + public final static long DEFAULT_BACKUP_FREQUENCY = DefaultTAPBackupManager.MANUAL; // = "never" => no UWS backup manager + public final static String KEY_BACKUP_BY_USER = "backup_by_user"; + public final static boolean DEFAULT_BACKUP_BY_USER = false; + + /* EXECUTION DURATION */ + public final static String KEY_DEFAULT_EXECUTION_DURATION = "default_execution_duration"; + public final static String KEY_MAX_EXECUTION_DURATION = "max_execution_duration"; + public final static int DEFAULT_EXECUTION_DURATION = 0; + + /* DATABASE KEYS */ + public final static String KEY_JDBC_DRIVER = "jdbc_driver"; + public final static HashMap<String,String> VALUE_JDBC_DRIVERS = new HashMap<String,String>(4); + static{ + VALUE_JDBC_DRIVERS.put("oracle", "oracle.jdbc.OracleDriver"); + VALUE_JDBC_DRIVERS.put("postgresql", "org.postgresql.Driver"); + VALUE_JDBC_DRIVERS.put("mysql", "com.mysql.jdbc.Driver"); + VALUE_JDBC_DRIVERS.put("sqlite", "org.sqlite.JDBC"); + } + public final static String KEY_SQL_TRANSLATOR = "sql_translator"; + public final static String VALUE_POSTGRESQL = "postgres"; + public final static String VALUE_PGSPHERE = "pgsphere"; + public final static String KEY_JDBC_URL = "jdbc_url"; + public final static String KEY_DB_USERNAME = "db_username"; + public final static String KEY_DB_PASSWORD = "db_password"; + + /* PROVIDER KEYS */ + public final static String KEY_PROVIDER_NAME = "provider_name"; + public final static String KEY_SERVICE_DESCRIPTION = "service_description"; + + /* AVAILABILITY KEYS */ + public final static String KEY_IS_AVAILABLE = "is_available"; + public final static boolean DEFAULT_IS_AVAILABLE = true; + public final static String KEY_DISABILITY_REASON = "disability_reason"; + + /** + * Read the asked property from the given Properties object. + * - The returned property value is trimmed (no space at the beginning and at the end of the string). + * - If the value is empty (length=0), NULL is returned. + * + * @param prop List of property + * @param key Property whose the value is requested. + * + * @return Return property value. + */ + public final static String getProperty(final Properties prop, final String key){ + if (prop == null) + return null; + + String value = prop.getProperty(key); + if (value != null){ + value = value.trim(); + return (value.length() == 0) ? null : value; + } + + return value; + } + + /** + * Test whether a property value is a class path. + * Expected syntax: a non-empty string surrounded by brackets ('{' and '}'). + * + * Note: The class path itself is not checked! + * + * @param value Property value. + * + * @return <i>true</i> if the given value is formatted as a class path, <i>false</i> otherwise. + */ + public final static boolean isClassPath(final String value){ + return (value != null && value.length() > 2 && value.charAt(0) == '{' && value.charAt(value.length() - 1) == '}'); + } + + /** + * Fetch the class object corresponding to the classpath provided between brackets in the given value. + * + * @param value Value which is supposed to contain the classpath between brackets (see {@link #isClassPath(String)} for more details) + * @param propertyName Name of the property associated with the parameter "value". + * @param expectedType Type of the class expected to be returned ; it is also the type which parameterizes this function: C. + * + * @return The corresponding Class object. + * + * @throws TAPException If the classpath is incorrect or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType"). + * + * @see {@link #isClassPath(String)} + */ + @SuppressWarnings("unchecked") + public final static < C > Class<C> fetchClass(final String value, final String propertyName, final Class<C> expectedType) throws TAPException{ + if (!isClassPath(value)) + return null; + + String classPath = value.substring(1, value.length() - 1).trim(); + if (classPath.isEmpty()) + return null; + + try{ + Class<C> classObject = (Class<C>)ClassLoader.getSystemClassLoader().loadClass(classPath); + if (!expectedType.isAssignableFrom(classObject)) + throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") is not implementing " + expectedType.getName() + "."); + else + return classObject; + }catch(ClassNotFoundException cnfe){ + throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") can not be found."); + }catch(ClassCastException cce){ + throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") is not implementing " + expectedType.getName() + "."); + } + } + + public final static void main(final String[] args) throws Throwable{ + + FileInputStream configFileStream = null; + try{ + final File configFile = new File("src/ext/tap_min.properties"); + configFileStream = new FileInputStream(configFile); + + Properties config = new Properties(); + config.load(configFileStream); + + configFileStream.close(); + configFileStream = null; + + Enumeration<Object> keys = config.keys(); + String key; + while(keys.hasMoreElements()){ + key = keys.nextElement().toString(); + System.out.println("* " + key + " = " + config.getProperty(key)); + } + }finally{ + if (configFileStream != null) + configFileStream.close(); + } + } + +} diff --git a/src/tap/config/gums_table.txt b/src/tap/config/gums_table.txt new file mode 100644 index 0000000..9ec4968 --- /dev/null +++ b/src/tap/config/gums_table.txt @@ -0,0 +1,49 @@ +Name|DBType|JDBCType|TAPType|VOTableType +id|character varying(19)|varchar(19)|| +ra2|numeric(14,10)|numeric(14,10)|| +dec2|numeric(14,10)|numeric(14,10)|| +vmag|real|float4|| +gmag|real|float4|| +gbmag|real|float4|| +grmag|real|float4|| +gsmag|real|float4|| +ra|numeric(14,10)|numeric(14,10)|| +deg|numeric(14,10)|numeric(14,10)|| +r|double precision|float8|| +pmra|double precision|float8|| +pmde|double precision|float8|| +rv|double precision|float8|| +v_i|real|float4|| +av|real|float4|| +age|real|float4|| +alphafe|real|float4|| +balb|real|float4|| +e|real|float4|| +feh|real|float4|| +fi|smallint|int2|| +galb|real|float4|| +fm|smallint|int2|| +host|smallint|int2|| +i|real|float4|| +logg|real|float4|| +Omega|real|float4|| +mass|double precision|float8|| +mbol|real|float4|| +nc|smallint|int2|| +nt|smallint|int2|| +p|double precision|float8|| +omega|real|float4|| +t0|double precision|float8|| +phase|real|float4|| +pop|smallint|int2|| +beenv|double precision|float8|| +radius|double precision|float8|| +a|double precision|float8|| +teff|integer|int4|| +vamp|real|float4|| +vper|double precision|float8|| +vphase|real|float4|| +vtype|character varying(4)|varchar(4)|| +vsini|real|float4|| +recno|integer|int4|| +coord|spoint|spoint|| diff --git a/src/tap/config/tap_configuration_file.html b/src/tap/config/tap_configuration_file.html new file mode 100644 index 0000000..6aa6824 --- /dev/null +++ b/src/tap/config/tap_configuration_file.html @@ -0,0 +1,469 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <title>TAP configuration file</title> + <style type="text/css"> + p { text-align: justify; text-indent: 1em; } + table { + font-family:"Trebuchet MS", Arial, Helvetica, sans-serif; + width:100%; + border-collapse:collapse; + } + td, th { + font-size:1em; + border:1px solid #084B8A; + padding:3px 7px 2px 7px; + color: black; + background-color: #EFF5FB; + } + th, td[colspan="5"] { + font-size:1em; + text-align: left; + padding-top:5px; + padding-bottom:4px; + background-color:#045FB4; + color:#ffffff; + } + td[colspan="5"] { + font-style: italic; + } + tr.mandatory td { + color: black; + background-color: #CEE3F6; + } + th:nth-child(2), td:nth-child(2) { + font-weight: bold; + text-align: center; + max-width: 2em; + } + td:nth-child(5) { + font-family: monospace; + } + table ul { list-style-position:inside; padding: 0; margin: 0; } + table p { margin: 0; padding: 0; text-align: left; text-indent: 0; } + table p+p { padding-top: .5em; } + </style> + </head> + <body> + <h1>TAP Configuration File</h1> + <p> + All properties listed in the below table are all the possible TAP configuration properties. + Some of them are mandatory. If one of these properties is missing, the TAP Service will not able to start: + an error will be displayed immediately in the application server log and a HTTP 503 error will be sent when accessing the TAP URL. + </p> + <p>Besides, you should know that any property key not listed in this table will be ignored without error or warning message.</p> + <p> + However, any not allowed property value will generate a warning message in the application server log and the default value will be kept. + Thus, the TAP Service will be started and available but the desired configuration value will not be set. So, you should take a look + at the application server log every times you start the TAP Service! + </p> + + <p>Here is an empty minimum TAP configuration file: <a href="tap_min.properties">tap_min.properties</a> and a complete one: <a href="tap_full.properties">tap_full.properties</a>.</p> + + <p><i><u>Legend:</u> <b>M</b> means that the property is mandatory. If nothing is written for the second column, the property is optional.</i> + + <table> + <tr> + <th>Property</th> + <th></th> + <th>Type</th> + <th>Description</th> + <th>Example</th> + </tr> + + <tr><td colspan="5">General</td></tr> + <tr> + <td>service_home_page</td> + <td></td> + <td>text</td> + <td> + <p>Path to the page which will be the index/home page of the TAP Service.</p> + <p><i>A default home page - just listing the TAP resources - is set if none is provided.</i></p> + </td> + <td><ul><li>home.html</li><li>/home/foo/my_tap_homepage.jsp</li></ul></td> + </tr> + + <tr><td colspan="5">Provider</td></tr> + <tr> + <td>provider_name</td> + <td></td> + <td>text</td> + <td>Name of the provider of the TAP Service.</td> + <td><ul><li>ARI</li><li>Mr. Smith</li></ul></td> + </tr> + <tr> + <td>service_description</td> + <td></td> + <td>text</td> + <td>Description of the TAP Service.</td> + <td></td> + </tr> + + <tr><td colspan="5">Availability</td></tr> + <tr> + <td>is_available</td> + <td></td> + <td>boolean</td> + <td> + <p>Tells whether the service is up or down.</p> + <p>If no value is provided, the service will be made available.</p> + <p>A value different from "true" (whatever is the case), will be considered as "false".</p> + </td> + <td><ul><li>true <i>(default)</i></li><li>false</li></ul></td> + </tr> + <tr> + <td>disability_reason</td> + <td></td> + <td>text</td> + <td>Message returns to a client of this TAP Service when it is not available.</p></td> + <td>This TAP Service is temporarily unavailable for DB maintenance.</td> + </tr> + + <tr><td colspan="5">Database</td></tr> + <tr> + <td>jdbc_driver</td> + <td></td> + <td>text</td> + <td> + <p>JDBC driver path. By default, it is guessed in function of the database name provided + in the jdbc_url property. It <strong>MUST be provided if</strong> another DBMS is used or if the JDBC driver path + does not match the following ones:</p> + <ul> + <li><u>Oracle :</u> oracle.jdbc.OracleDriver</li> + <li><u>PostgreSQL:</u> org.postgresql.Driver</li> + <li><u>MySQL :</u> com.mysql.jdbc.Driver</li> + <li><u>SQLite :</u> org.sqlite.JDBC</li> + </ul> + </td> + <td>oracle.jdbc.driver.OracleDriver</td> + </tr> + <tr class="mandatory"> + <td>jdbc_url</td> + <td>M</td> + <td>text</td> + <td> + <p>It must be a JDBC driver URL.</p> + <p><em><u>Note:</u> 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.</em></p> + </td> + <td><ul><li>jdbc:postgresql:mydb</li><li>jdbc:postgresql://myserver:1234/mydb</li><li>jdbc:sqlite:Database.db</li></ul></td> + </tr> + <tr> + <td>db_username</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> the username is not already provided in jdbc_url</p> + <p>Username used to access to the database.</p> + </td> + <td></td> + </tr> + <tr> + <td>db_password</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> the password is not already provided in jdbc_url</p> + <p>Password used by db_username to access to the database.</p> + <p><em><u>Note:</u> No password encryption can be done in this configuration file for the moment.</em></p> + </td> + <td></td> + </tr> + <tr class="mandatory"> + <td>db_tables</td> + <td>M</td> + <td>text</td> + <td> + <p>List all tables that must be accessed thanks to this TAP Service.</p> + <p>Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema).</p> + <p>For each table, you can restrict the list of columns that you to expose via the TAP Service. + This list is not mandatory, but if provided it must be within parenthesis and comma separated.</p> + </td> + <td>schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...)</td> + </tr> + <tr class="mandatory"> + <td>sql_translator</td> + <td>M</td> + <td>text</td> + <td> + <p>The translator to use in order to translate ADQL to a SQL compatible with the used DBMS and its spatial extension.</p> + <p>The TAP library supports only Postgresql (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator + (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator and which have at least an empty constructor.</p> + </td> + <td><ul><li>postgres</li><li>pgsphere</li><li>{apackage.MyADQLTranslator}</li></ul></td> + </tr> + + <tr><td colspan="5">Files</td></tr> + <tr class="mandatory"> + <td>file_manager</td> + <td>M</td> + <td>text</td> + <td> + <p>Type of the file manager.</p> + <p>Accepted values are: local (to manage files on the local system). + You can also add another way to manage files by providing the path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a java.util.Properties parameter.</p> + </td> + <td><ul><li>local</li><li>{apackage.MyTAPFileManager}</li></ul></td> + </tr> + <tr class="mandatory"> + <td>file_root_path</td> + <td>M</td> + <td>text</td> + <td>File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be.</td> + <td></td> + </tr> + <tr> + <td>directory_per_user</td> + <td></td> + <td>boolean</td> + <td> + <p>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.</p> + <p><em>The default value is: true.</em></p> + </td> + <td><ul><li>true <i>(default)</i></li><li>false</li></ul></td> + </tr> + <tr> + <td>group_user_directories</td> + <td></td> + <td>boolean</td> + <td> + <p>Tells whether user directories must be grouped. If yes, directories are grouped by the first letter found in the user ID.</p> + <p><em>The default value is: false.</em></p> + </td> + <td><ul><li>true</li><li>false <i>(default)</i></li></ul></td> + </tr> + <tr> + <td>default_retention_period</td> + <td></td> + <td>integer</td> + <td> + <p>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.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over the default retention period: job results will be kept forever. Float values are not allowed.</p> + <p><em>By default query results are kept forever: default_retention_period=0.</em></p></td> + <td>86400 <em>(1 day)</em></td> + </tr> + <tr> + <td>max_retention_period</td> + <td></td> + <td>integer</td> + <td> + <p>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.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over the maximum retention period: the job results will be kept forever. Float values are not allowed.</p> + <p><em>By default query results are kept forever: max_retention_period=0.</em></p></td> + <td>604800 <em>(1 week)</em></td> + </tr> + <tr> + <td>irods_host</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> file_manager=irods</p> + <p>Host of the IRODS service.</p> + <td></td> + </tr> + <tr> + <td>irods_port</td> + <td></td> + <td>integer</td> + <td> + <p><strong>Mandatory if</strong> file_manager=irods</p> + <p>Port of the IRODS service located on the irodsHost host.</p> + <td></td> + </tr> + <tr> + <td>irods_user</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> file_manager=irods</p> + <p>Username to use in order to access the IRODS service located at irodsHost host.</p> + <td></td> + </tr> + <tr> + <td>irods_password</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> file_manager=irods</p> + <p>Password associated with the username used to access the IRODS service located at irodsHost host.</p> + <td></td> + </tr> + <tr> + <td>irods_zone</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> file_manager=irods</p> + <p>Zone of the IRODS service in which the TAP Service must manage its files.</p> + <td></td> + </tr> + <tr> + <td>irods_default_storage_resource</td> + <td></td> + <td>text</td> + <td> + <p><strong>Mandatory if</strong> file_manager=irods</p> + <p>Default storage resource of the IRODS service.</p> + <td></td> + </tr> + + <tr><td colspan="5">UWS Backup</td></tr> + <tr> + <td>backup_frequency</td> + <td></td> + <td>text or integer</td> + <td> + <p>Frequency at which the UWS service (that's to say, all its users and jobs) must be backuped.</p> + <p>Allowed values are: never (no backup will never be done), 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.</p> + <p><em>By default, no backup is done, so: backup_frequency=never.</em></p> + </td> + <td><ul><li>never <em>(default)</em></li><li>user_action</li><li>3600000 <em>(1 hour)</em></li></ul></td> + </tr> + <tr> + <td>backup_mode</td> + <td></td> + <td>text</td> + <td> + <p>Tells whether the backup must be one file for every user, or one file for each user. This second option should be chosen if your TAP Service is organizing its files by user directories ; see the property <em>directory_per_user</em>.</p> + <p>Allowed values are: user (one backup file for each user), whole (one file for all users ; may generates a big file).</p> + <p><em>The default mode is: whole.</em></p> + </td> + <td><ul><li>whole <em>(default)</em></li><li>user</li></ul></td> + </tr> + + <tr><td colspan="5">Query Execution</td></tr> + <tr> + <td>default_execution_duration</td> + <td></td> + <td>integer</td> + <td> + <p>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.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over the default execution duration: the execution could never end. Float values are not allowed.</p> + <p><em>By default, there is no restriction: default_execution_duration=0.</em></p> + </td> + <td>600000 <em>(10 minutes)</em></td> + </tr> + <tr> + <td>max_execution_duration</td> + <td></td> + <td>integer</td> + <td> + <p>Maximum time (in milliseconds) for query execution. The prefix "max" means here that the client can not set a time greater than this one.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over the maximum execution duration: the execution could never end. Float values are not allowed.</p> + <p><em>By default, there is no restriction: max_execution_duration=0.</em></p> + </td> + <td>3600000 <em>(1 hour)</em></td> + </tr> + + <tr><td colspan="5">Output</td></tr> + <tr> + <td>output_add_formats</td> + <td></td> + <td>text</td> + <td> + <p>Output formats for query results, in addition to the VOTable.</p> + <p>Allowed values are: json, csv, tsv, or a path (within brackets: {...}) toward a class which implements OutputFormat<ResultSet>.</p> + </td> + <td><ul><li>json</li><li>csv</li><li>tsv</li><li>{apackage.FooOutputFormat}</li></ul></td> + </tr> + <tr> + <td>output_default_limit</td> + <td></td> + <td>text</td> + <td> + <p>Default limit for the result output. The prefix "default" means here that this value will be set if the client does not provide one.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p> + <p>Obviously this limit MUST be less or equal than output_max_limit.</p> + <p><em>By default, there is no restriction: output_default_limit=0</em></p> + </td> + <td><ul><li>0 <em>(default)</em></li><li>20r</li><li>200kb</li><li>200kB</li></ul></td> + </tr> + <tr> + <td>output_max_limit</td> + <td></td> + <td>text</td> + <td> + <p>Maximum limit for the result output. The prefix "max" means here that the client can not set a limit greater than this one.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p> + <p>Obviously this limit MUST be greater or equal than output_default_limit.</p> + <p><em>By default, there is no restriction: output_max_limit=0</em></p> + </td> + <td><ul><li>0 <em>(default)</em></li><li>10000r</li><li>1Mb</li><li>1MB</li></ul></td> + </tr> + + <tr><td colspan="5">Upload</td></tr> + <tr> + <td>upload_enabled</td> + <td></td> + <td>boolean</td> + <td> + <p>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.</p> + <p>By default, the Upload is disabled: upload_enabled=false.</p> + </td> + <td><ul><li>false <em>(default)</em></li><li>true</li></ul></td> + </tr> + <tr> + <td>upload_default_db_limit</td> + <td></td> + <td>text</td> + <td> + <p>Default limit for the number of uploaded records that can be inserted inside the database. The prefix "default" means here that this value will be set if the client does not provide one.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p> + <p>Obviously this limit MUST be less or equal than upload_max_db_limit.</p> + <p><em>By default, there is no restriction: upload_default_db_limit=0</em></p> + </td> + <td><ul><li>0 <em>(default)</em></li><li>20r</li><li>200kb</li><li>200kB</li></ul></td> + </tr> + <tr> + <td>upload_max_db_limit</td> + <td></td> + <td>text</td> + <td> + <p>Maximum limit for the number of uploaded records that can be inserted inside the database. The prefix "max" means here that the client can not set a limit greater than this one.</p> + <p>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.</p> + <p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p> + <p>Obviously this limit MUST be greater or equal than upload_default_db_limit.</p> + <p><em>By default, there is no restriction: upload_max_db_limit=0</em></p> + </td> + <td><ul><li>0 <em>(default)</em></li><li>10000r</li><li>1Mb</li><li>1MB</li></ul></td> + </tr> + <tr> + <td>upload_max_file_size</td> + <td></td> + <td>text</td> + <td> + <p>Maximum allowed size for the uploaded file.</p> + <p>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 (except for the last character: "b"). No other storage unit is allowed.</p> + <p>A negative or null value means there is no restriction over this limit. Float values are not allowed.</p> + <p>In function of the chosen upload_max_db_limit type, upload_max_file_size should be greater in order to figure out the metadata part.</p> + <p><em>By default, there is no restriction: upload_max_file_size=0</em></p> + </td> + <td><ul><li>0 <em>(default)</em></li><li>2Mb</li><li>2MB</li></ul></td> + </tr> + </table> + </body> +</html> \ No newline at end of file diff --git a/src/tap/config/tap_full.properties b/src/tap/config/tap_full.properties new file mode 100644 index 0000000..4dccf39 --- /dev/null +++ b/src/tap/config/tap_full.properties @@ -0,0 +1,255 @@ +########################################################## +# FULL TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +# # +# See the TAP documentation for more details: ...TODO... # +########################################################## + +########### +# GENERAL # +########### + +# [OPTIONAL] +# Path to the page which will be the index/home page of the TAP Service. +service_home_page = + +############ +# PROVIDER # +############ + +# [OPTIONAL] +# Name of the provider of the TAP Service. +provider_name = ARI + +# [OPTIONAL] +# Description of the TAP Service. +tap_description = My TAP Service is so amazing! You should use it with your favorite TAP client. + +################ +# AVAILABILITY # +################ + +# [OPTIONAL] +# Tells whether the service is up or down. +# If no value is provided, the service will be made available. +# A value different from "true" (whatever is the case), will be considered as "false". +# Allowed values: true (default), false +is_available = true + +# [OPTIONAL] +# Message returns to a client of this TAP Service when it is not available. +disability_reason = + +############ +# DATABASE # +############ + +# [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 +#jdbc_driver = + +# [MANDATORY] +# It must be a JDBC driver URL. +# 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 = + +# [MANDATORY] +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = + +# [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 = + +# [MANDATORY] +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# [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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# [MANDATORY] +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +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 over the default retention period: job results will be kept forever. Float values are not allowed. +# Default value: 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 over the maximum retention period: the job results will be kept forever. Float values are not allowed. +# Default value: 0 (results kept forever). +max_retention_period = 0 + +# [OPTIONAL] +# Mandatory if file_manager=irods +# Host of the IRODS service. +#irods_host = + +# [OPTIONAL] +# Mandatory if file_manager=irods +# Port of the IRODS service located on the irodsHost host. +#irods_port = + +# [OPTIONAL] +# Mandatory if file_manager=irods +# Username to use in order to access the IRODS service located at irodsHost host. +#irods_user = + +# [OPTIONAL] +# Mandatory if file_manager=irods +# Password associated with the username used to access the IRODS service located at irodsHost host. +#irods_password = + +# [OPTIONAL] +# Mandatory if file_manager=irods +# Zone of the IRODS service in which the TAP Service must manage its files. +#irods_zone = + +# [OPTIONAL] +# Mandatory if file_manager=irods +# Default storage resource of the IRODS service. +#irods_default_storage_resource = + +############## +# UWS_BACKUP # +############## + +# [OPTIONAL] +# Frequency at which the UWS service (that's to say, all its users and jobs) must be backuped. +# Allowed values are: 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. +backup_frequency = never + +# [OPTIONAL] +# Tells whether the backup must be one file for every user, or one file for each user. This second option should be chosen if your TAP Service is organizing its files by user directories ; see the property directory_per_user. +# Allowed values are: user (one backup file for each user ; default), whole (one file for all users ; may generates a big file). +backup_mode = user + +################### +# 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 over the default execution duration: the execution could never end. Float values are not allowed. +# By 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 over the maximum execution duration: the execution could never end. Float values are not allowed. +# By default, there is no restriction: max_execution_duration=0. +max_execution_duration = 0 + +########## +# OUTPUT # +########## + +# [OPTIONAL] +# Output formats for query results, in addition to the VOTable. +# Allowed values are: json, csv, tsv, or a path (within brackets: {...}) toward a class which implements OutputFormat<ResultSet>. +output_add_formats = + +# [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 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 or null value means there is no restriction over this limit. Float values are not allowed. +# Obviously this limit MUST be less or equal than output_max_limit. +# By default, there is no restriction: output_default_limit=0 +output_default_limit = 0 + +# [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 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 or null value means there is no restriction over this limit. Float values are not allowed. +# Obviously this limit MUST be greater or equal than output_default_limit. +# By default, there is no restriction: output_max_limit=0 +output_max_limit = 0 + +########## +# 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. +# Allowed values: true (default), false. +upload_enabled = false + +# [OPTIONAL] +# Default limit for the number of uploaded records that can be inserted inside the database. The prefix "default" means here that this value will be set if the client does not provide one. +# 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 or null value means there is no restriction over this limit. Float values are not allowed. +# Obviously this limit MUST be less or equal than upload_max_db_limit. +# By default, there is no restriction: upload_default_db_limit=0 +upload_default_db_limit = 0 + +# [OPTIONAL] +# Maximum limit for the number of uploaded records that can be inserted inside the database. The prefix "max" means here that the client can not set a limit greater than this one. +# 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 or null value means there is no restriction over this limit. Float values are not allowed. +# Obviously this limit MUST be greater or equal than upload_default_db_limit. +# By default, there is no restriction: upload_max_db_limit=0 +upload_max_db_limit = 0 + +# [OPTIONAL] +# Maximum allowed size for the uploaded file. +# 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 (except for the last character: "b"). No other storage unit is allowed. +# A negative or null value means there is no restriction over this limit. Float values are not allowed. +# In function of the chosen upload_max_db_limit type, upload_max_file_size should be greater in order to figure out the metadata part. +# By default, there is no restriction: upload_max_file_size=0 +upload_max_file_size = 0 diff --git a/src/tap/config/tap_min.properties b/src/tap/config/tap_min.properties new file mode 100644 index 0000000..88e8e0a --- /dev/null +++ b/src/tap/config/tap_min.properties @@ -0,0 +1,66 @@ +########################################################## +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +# # +# See the TAP documentation for more details: ...TODO... # +########################################################## + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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 = + +# 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 = + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = diff --git a/test/tap/config/AllTests.java b/test/tap/config/AllTests.java new file mode 100644 index 0000000..0a6b943 --- /dev/null +++ b/test/tap/config/AllTests.java @@ -0,0 +1,11 @@ +package tap.config; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({TestDefaultServiceConnection.class,TestDefaultTAPFactory.class}) +public class AllTests { + +} diff --git a/test/tap/config/TestDefaultServiceConnection.java b/test/tap/config/TestDefaultServiceConnection.java new file mode 100644 index 0000000..c70a301 --- /dev/null +++ b/test/tap/config/TestDefaultServiceConnection.java @@ -0,0 +1,268 @@ +package tap.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static tap.config.TAPConfiguration.KEY_FILE_MANAGER; +import static tap.config.TAPConfiguration.VALUE_LOCAL; +import static tap.config.TAPConfiguration.fetchClass; +import static tap.config.TAPConfiguration.isClassPath; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; + +import org.junit.Before; +import org.junit.Test; + +import tap.ServiceConnection; +import tap.TAPException; +import tap.file.LocalTAPFileManager; +import uws.UWSException; + +public class TestDefaultServiceConnection { + + final String VALID_CONF_FILE = "bin/ext/test/tap_valid.properties"; + final String NO_FM_CONF_FILE = "bin/ext/test/tap_no_fm.properties"; + final String FM_CLASS_PATH_CONF_FILE = "bin/ext/test/tap_fm_clp.properties"; + final String INCORRECT_FM_CONF_FILE = "bin/ext/test/tap_incorrect_fm.properties"; + + private Properties validProp, noFmProp, fmClassPathProp, incorrectFmProp; + + @Before + public void setUp() throws Exception{ + // LOAD ALL PROPERTIES FILES NEEDED FOR ALL THE TESTS: + FileInputStream input = null; + try{ + validProp = new Properties(); + input = new FileInputStream(VALID_CONF_FILE); + validProp.load(input); + input.close(); + input = null; + + noFmProp = new Properties(); + input = new FileInputStream(NO_FM_CONF_FILE); + noFmProp.load(input); + input.close(); + input = null; + + fmClassPathProp = new Properties(); + input = new FileInputStream(FM_CLASS_PATH_CONF_FILE); + fmClassPathProp.load(input); + input.close(); + input = null; + + incorrectFmProp = new Properties(); + input = new FileInputStream(INCORRECT_FM_CONF_FILE); + incorrectFmProp.load(input); + input.close(); + input = null; + + }finally{ + if (input != null) + input.close(); + } + } + + /** + * CONSTRUCTOR TESTS + * * In general: + * - A valid configuration file builds successfully a fully functional + * + * * Over the file manager: + * - If no TAPFileManager is provided, an exception must be thrown. + * - If a classpath toward a valid TAPFileManager is provided, a functional DefaultServiceConnection must be successfully built. + * - An incorrect file manager value in the configuration file must generate an exception. + * + * Note: the good configuration of the TAPFactory built by the DefaultServiceConnection is tested in {@link TestDefaultTAPFactory}. + * + * @see DefaultServiceConnection#DefaultServiceConnection(Properties) + */ + @Test + public void testDefaultServiceConnectionProperties(){ + // Valid Configuration File: + try{ + ServiceConnection<?> connection = new DefaultServiceConnection(validProp); + assertNotNull(connection.getLogger()); + assertNotNull(connection.getFileManager()); + assertNotNull(connection.getFactory()); + assertTrue(connection.isAvailable()); + assertTrue(connection.getRetentionPeriod()[0] <= connection.getRetentionPeriod()[1]); + assertTrue(connection.getExecutionDuration()[0] <= connection.getExecutionDuration()[1]); + }catch(Exception e){ + fail("This MUST have succeeded because the property file is valid! \nCaught exception: " + getPertinentMessage(e)); + } + + // No File Manager: + try{ + new DefaultServiceConnection(noFmProp); + fail("This MUST have failed because no File Manager is specified!"); + }catch(Exception e){ + assertEquals(e.getClass(), TAPException.class); + assertEquals(e.getMessage(), "The property \"" + KEY_FILE_MANAGER + "\" is missing! It is required to create a TAP Service. Two possible values: " + VALUE_LOCAL + " or a class path between {...}."); + } + + // File Manager = Class Path: + try{ + ServiceConnection<?> connection = new DefaultServiceConnection(fmClassPathProp); + assertNotNull(connection.getLogger()); + assertNotNull(connection.getFileManager()); + assertNotNull(connection.getFactory()); + assertTrue(connection.isAvailable()); + + /* Retention periods and execution durations are different in this configuration file from the valid one (validProp). + * Max period and max duration are set in this file as less than respectively the default period and the default duration. + * In such situation, the default period/duration is set to the maximum one, in order to ensure that the maximum value is + * still greater or equals than the default one. So the max and default values must be equal there. + */ + assertTrue(connection.getRetentionPeriod()[0] == connection.getRetentionPeriod()[1]); + assertTrue(connection.getExecutionDuration()[0] == connection.getExecutionDuration()[1]); + }catch(Exception e){ + fail("This MUST have succeeded because the provided file manager is a class path valid! \nCaught exception: " + getPertinentMessage(e)); + } + + // Incorrect File Manager Value: + try{ + new DefaultServiceConnection(incorrectFmProp); + fail("This MUST have failed because an incorrect File Manager value has been provided!"); + }catch(Exception e){ + assertEquals(e.getClass(), TAPException.class); + assertEquals(e.getMessage(), "Unknown value for the propertie \"" + KEY_FILE_MANAGER + "\": \"foo\". Only two possible values: " + VALUE_LOCAL + " or a class path between {...}."); + } + } + + /** + * TEST isClassPath(String): + * - null, "", "{}", "an incorrect syntax" => FALSE must be returned + * - "{ }", "{ }", "{class.path}", "{ class.path }" => TRUE must be returned + * + * @see DefaultServiceConnection#isClassPath(String) + */ + @Test + public void testIsClassPath(){ + // NULL and EMPTY: + assertFalse(isClassPath(null)); + assertFalse(isClassPath("")); + + // EMPTY CLASSPATH: + assertFalse(isClassPath("{}")); + + // INCORRECT CLASSPATH: + assertFalse(isClassPath("incorrect class path ; missing {}")); + + // VALID CLASSPATH: + assertTrue(isClassPath("{class.path}")); + + // CLASSPATH VALID ONLY IN THE SYNTAX: + assertTrue(isClassPath("{ }")); + assertTrue(isClassPath("{ }")); + + // NOT TRIM CLASSPATH: + assertTrue(isClassPath("{ class.path }")); + } + + /** + * TEST getClass(String,String,String): + * - null, "", "{}", "an incorrect syntax", "{ }", "{ }" => NULL must be returned + * - "{java.lang.String}", "{ java.lang.String }" => a valid DefaultServiceConnection must be returned + * - "{mypackage.foo}", "{java.util.ArrayList}" (while a String is expected) => a TAPException must be thrown + */ + @Test + public void testGetClassStringStringString(){ + // NULL and EMPTY: + try{ + assertNull(fetchClass(null, KEY_FILE_MANAGER, String.class)); + }catch(TAPException e){ + fail("If a NULL value is provided as classpath: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e)); + } + try{ + assertNull(fetchClass("", KEY_FILE_MANAGER, String.class)); + }catch(TAPException e){ + fail("If an EMPTY value is provided as classpath: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e)); + } + + // EMPTY CLASSPATH: + try{ + assertNull(fetchClass("{}", KEY_FILE_MANAGER, String.class)); + }catch(TAPException e){ + fail("If an EMPTY classpath is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e)); + } + + // INCORRECT SYNTAX: + try{ + assertNull(fetchClass("incorrect class path ; missing {}", KEY_FILE_MANAGER, String.class)); + }catch(TAPException e){ + fail("If an incorrect classpath is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e)); + } + + // VALID CLASSPATH: + try{ + Class<String> classObject = fetchClass("{java.lang.String}", KEY_FILE_MANAGER, String.class); + assertNotNull(classObject); + assertEquals(classObject.getName(), "java.lang.String"); + }catch(TAPException e){ + fail("If a VALID classpath is provided: getClass(...) MUST return a Class object of the wanted type!\nCaught exception: " + getPertinentMessage(e)); + } + + // INCORRECT CLASSPATH: + try{ + fetchClass("{mypackage.foo}", KEY_FILE_MANAGER, String.class); + fail("This MUST have failed because an incorrect classpath is provided!"); + }catch(TAPException e){ + assertEquals(e.getClass(), TAPException.class); + assertEquals(e.getMessage(), "The class specified by the property " + KEY_FILE_MANAGER + " ({mypackage.foo}) can not be found."); + } + + // INCOMPATIBLE TYPES: + try{ + @SuppressWarnings("unused") + Class<String> classObject = fetchClass("{java.util.ArrayList}", KEY_FILE_MANAGER, String.class); + fail("This MUST have failed because a class of a different type has been asked!"); + }catch(TAPException e){ + assertEquals(e.getClass(), TAPException.class); + assertEquals(e.getMessage(), "The class specified by the property " + KEY_FILE_MANAGER + " ({java.util.ArrayList}) is not implementing " + String.class.getName() + "."); + } + + // CLASSPATH VALID ONLY IN THE SYNTAX: + try{ + assertNull(fetchClass("{ }", KEY_FILE_MANAGER, String.class)); + }catch(TAPException e){ + fail("If an EMPTY classpath is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e)); + } + try{ + assertNull(fetchClass("{ }", KEY_FILE_MANAGER, String.class)); + }catch(TAPException e){ + fail("If an EMPTY classpath is provided: getClass(...) MUST return null!\nCaught exception: " + getPertinentMessage(e)); + } + + // NOT TRIM CLASSPATH: + try{ + Class<?> classObject = fetchClass("{ java.lang.String }", KEY_FILE_MANAGER, String.class); + assertNotNull(classObject); + assertEquals(classObject.getName(), "java.lang.String"); + }catch(TAPException e){ + fail("If a VALID classpath is provided: getClass(...) MUST return a Class object of the wanted type!\nCaught exception: " + getPertinentMessage(e)); + } + } + + public static final String getPertinentMessage(final Exception ex){ + return (ex.getCause() == null || ex.getMessage().equals(ex.getCause().getMessage())) ? ex.getMessage() : ex.getCause().getMessage(); + } + + /** + * A TAPFileManager to test the load of a TAPFileManager from the configuration file with a class path. + * + * @author Grégory Mantelet (ARI) + * @version 11/2013 + * @see TestDefaultServiceConnection#testDefaultServiceConnectionProperties() + */ + public static class FileManagerTest extends LocalTAPFileManager { + public FileManagerTest(Properties tapConfig) throws UWSException{ + super(new File(tapConfig.getProperty("file_root_path")), true, false); + } + } + +} diff --git a/test/tap/config/TestDefaultTAPFactory.java b/test/tap/config/TestDefaultTAPFactory.java new file mode 100644 index 0000000..15d2653 --- /dev/null +++ b/test/tap/config/TestDefaultTAPFactory.java @@ -0,0 +1,300 @@ +package tap.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileInputStream; +import java.sql.ResultSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Properties; + +import org.junit.Before; +import org.junit.Test; +import org.postgresql.util.PSQLException; + +import tap.ServiceConnection; +import tap.TAPException; +import tap.TAPFactory; +import tap.db.DBException; +import tap.file.LocalTAPFileManager; +import tap.file.TAPFileManager; +import tap.formatter.OutputFormat; +import tap.log.DefaultTAPLog; +import tap.log.TAPLog; +import tap.metadata.TAPMetadata; +import uws.service.UWSService; +import uws.service.UserIdentifier; + +public class TestDefaultTAPFactory { + + final String VALID_CONF_FILE = "bin/ext/test/tap_valid.properties"; + final String NO_JDBC_1_CONF_FILE = "bin/ext/test/tap_no_jdbc_driver_1.properties"; + final String NO_JDBC_2_CONF_FILE = "bin/ext/test/tap_no_jdbc_driver_2.properties"; + final String BAD_JDBC_CONF_FILE = "bin/ext/test/tap_bad_jdbc_driver.properties"; + final String BAD_TRANSLATOR_CONF_FILE = "bin/ext/test/tap_bad_translator.properties"; + final String BAD_DB_NAME_CONF_FILE = "bin/ext/test/tap_bad_db_name.properties"; + final String BAD_USERNAME_CONF_FILE = "bin/ext/test/tap_bad_username.properties"; + final String BAD_PASSWORD_CONF_FILE = "bin/ext/test/tap_bad_password.properties"; + + private Properties validProp, noJdbcProp1, noJdbcProp2, badJdbcProp, + badTranslatorProp, badDBNameProp, badUsernameProp, badPasswordProp; + + private ServiceConnection<ResultSet> serviceConnection = null; + + @Before + public void setUp() throws Exception{ + // BUILD A FAKE SERVICE CONNECTION: + serviceConnection = new ServiceConnectionTest(); + + // LOAD ALL PROPERTIES FILES NEEDED FOR ALL THE TESTS: + FileInputStream input = null; + try{ + validProp = new Properties(); + input = new FileInputStream(VALID_CONF_FILE); + validProp.load(input); + input.close(); + input = null; + + noJdbcProp1 = new Properties(); + input = new FileInputStream(NO_JDBC_1_CONF_FILE); + noJdbcProp1.load(input); + input.close(); + input = null; + + noJdbcProp2 = new Properties(); + input = new FileInputStream(NO_JDBC_2_CONF_FILE); + noJdbcProp2.load(input); + input.close(); + input = null; + + badJdbcProp = new Properties(); + input = new FileInputStream(BAD_JDBC_CONF_FILE); + badJdbcProp.load(input); + input.close(); + input = null; + + badTranslatorProp = new Properties(); + input = new FileInputStream(BAD_TRANSLATOR_CONF_FILE); + badTranslatorProp.load(input); + input.close(); + input = null; + + badDBNameProp = new Properties(); + input = new FileInputStream(BAD_DB_NAME_CONF_FILE); + badDBNameProp.load(input); + input.close(); + input = null; + + badUsernameProp = new Properties(); + input = new FileInputStream(BAD_USERNAME_CONF_FILE); + badUsernameProp.load(input); + input.close(); + input = null; + + badPasswordProp = new Properties(); + input = new FileInputStream(BAD_PASSWORD_CONF_FILE); + badPasswordProp.load(input); + input.close(); + input = null; + + }finally{ + if (input != null) + input.close(); + } + } + + @Test + public void testDefaultServiceConnection(){ + // Correct Parameters: + try{ + TAPFactory<ResultSet> factory = new DefaultTAPFactory(serviceConnection, validProp); + assertNotNull(factory.createADQLTranslator()); + assertNotNull(factory.createDBConnection("0")); + assertNull(factory.createUWSBackupManager(new UWSService(factory, new LocalTAPFileManager(new File("."))))); + }catch(Exception ex){ + fail(getPertinentMessage(ex)); + } + + // No JDBC Driver but the database type is known: + try{ + new DefaultTAPFactory(serviceConnection, noJdbcProp1); + }catch(Exception ex){ + fail(getPertinentMessage(ex)); + } + + // No JDBC Driver but the database type is UNKNOWN: + try{ + new DefaultTAPFactory(serviceConnection, noJdbcProp2); + fail("This MUST have failed because no JDBC Driver has been successfully guessed from the database type!"); + }catch(Exception ex){ + assertEquals(ex.getClass(), TAPException.class); + assertTrue(ex.getMessage().matches("No JDBC driver known for the DBMS \"[^\\\"]*\"!")); + } + + // Bad JDBC Driver: + try{ + new DefaultTAPFactory(serviceConnection, badJdbcProp); + fail("This MUST have failed because the provided JDBC Driver doesn't exist!"); + }catch(Exception ex){ + assertEquals(ex.getClass(), DBException.class); + assertTrue(ex.getMessage().matches("Impossible to find the JDBC driver \"[^\\\"]*\" !")); + } + + // Bad Translator: + try{ + new DefaultTAPFactory(serviceConnection, badTranslatorProp); + fail("This MUST have failed because the provided SQL translator is incorrect!"); + }catch(Exception ex){ + assertEquals(ex.getClass(), TAPException.class); + assertTrue(ex.getMessage().matches("Unsupported value for the property sql_translator: \"[^\\\"]*\" !")); + } + + // Bad DB Name: + try{ + new DefaultTAPFactory(serviceConnection, badDBNameProp); + fail("This MUST have failed because the provided database name is incorrect!"); + }catch(Exception ex){ + assertEquals(ex.getClass(), DBException.class); + assertTrue(ex.getMessage().matches("Impossible to establish a connection to the database \"[^\\\"]*\" !")); + assertEquals(ex.getCause().getClass(), PSQLException.class); + assertTrue(ex.getCause().getMessage().matches("FATAL: database \"[^\\\"]*\" does not exist")); + } + + // Bad DB Username: ABORTED BECAUSE THE BAD USERNAME IS NOT DETECTED FOR THE DB WHICH HAS THE SAME NAME AS THE USERNAME ! + // TODO CREATE A NEW DB USER WHICH CAN ACCESS TO THE SAME DB AND TEST AGAIN ! + /*try { + new DefaultTAPFactory(serviceConnection, badUsernameProp); + fail("This MUST have failed because the provided database username is incorrect!"); + } catch (Exception ex) { + ex.printStackTrace(); + assertEquals(ex.getClass(), DBException.class); + assertEquals(ex.getMessage().substring(0,54), "Impossible to establish a connection to the database \""); + }*/ + + // Bad DB Password: + try{ + new DefaultTAPFactory(serviceConnection, badPasswordProp); + fail("This MUST have failed because the provided database password is incorrect!"); + }catch(Exception ex){ + assertEquals(ex.getClass(), DBException.class); + assertTrue(ex.getMessage().matches("Impossible to establish a connection to the database \"[^\\\"]*\" !")); + assertEquals(ex.getCause().getClass(), PSQLException.class); + assertTrue(ex.getCause().getMessage().matches("FATAL: password authentication failed for user \"[^\\\"]*\"")); + } + } + + public static final String getPertinentMessage(final Exception ex){ + return (ex.getCause() == null || ex.getMessage().equals(ex.getCause().getMessage())) ? ex.getMessage() : ex.getCause().getMessage(); + } + + public static class ServiceConnectionTest implements ServiceConnection<ResultSet> { + + private TAPLog logger = new DefaultTAPLog((TAPFileManager)null); + + @Override + public String getProviderName(){ + return null; + } + + @Override + public String getProviderDescription(){ + return null; + } + + @Override + public boolean isAvailable(){ + return true; + } + + @Override + public String getAvailability(){ + return null; + } + + @Override + public int[] getRetentionPeriod(){ + return null; + } + + @Override + public int[] getExecutionDuration(){ + return null; + } + + @Override + public int[] getOutputLimit(){ + return null; + } + + @Override + public tap.ServiceConnection.LimitUnit[] getOutputLimitType(){ + return null; + } + + @Override + public UserIdentifier getUserIdentifier(){ + return null; + } + + @Override + public boolean uploadEnabled(){ + return false; + } + + @Override + public int[] getUploadLimit(){ + return null; + } + + @Override + public tap.ServiceConnection.LimitUnit[] getUploadLimitType(){ + return null; + } + + @Override + public int getMaxUploadSize(){ + return 0; + } + + @Override + public TAPMetadata getTAPMetadata(){ + return null; + } + + @Override + public Collection<String> getCoordinateSystems(){ + return null; + } + + @Override + public TAPLog getLogger(){ + return logger; + } + + @Override + public TAPFactory<ResultSet> getFactory(){ + return null; + } + + @Override + public TAPFileManager getFileManager(){ + return null; + } + + @Override + public Iterator<OutputFormat<ResultSet>> getOutputFormats(){ + return null; + } + + @Override + public OutputFormat<ResultSet> getOutputFormat(String mimeOrAlias){ + return null; + } + } + +} diff --git a/test/tap/config/tap_bad_db_name.properties b/test/tap/config/tap_bad_db_name.properties new file mode 100644 index 0000000..f4d4c75 --- /dev/null +++ b/test/tap/config/tap_bad_db_name.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:foo + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_bad_jdbc_driver.properties b/test/tap/config/tap_bad_jdbc_driver.properties new file mode 100644 index 0000000..77c5f8b --- /dev/null +++ b/test/tap/config/tap_bad_jdbc_driver.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +jdbc_driver = foo + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_bad_password.properties b/test/tap/config/tap_bad_password.properties new file mode 100644 index 0000000..0b1e637 --- /dev/null +++ b/test/tap/config/tap_bad_password.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:foo + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = foo + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_bad_translator.properties b/test/tap/config/tap_bad_translator.properties new file mode 100644 index 0000000..56f0bff --- /dev/null +++ b/test/tap/config/tap_bad_translator.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = foo + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_bad_username.properties b/test/tap/config/tap_bad_username.properties new file mode 100644 index 0000000..6817f06 --- /dev/null +++ b/test/tap/config/tap_bad_username.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = foo + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_fm_clp.properties b/test/tap/config/tap_fm_clp.properties new file mode 100644 index 0000000..0e0b033 --- /dev/null +++ b/test/tap/config/tap_fm_clp.properties @@ -0,0 +1,94 @@ +################################################################ +# TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = {ext.test.TestDefaultServiceConnection$FileManagerTest} + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap + +# 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 over the default retention period: job results will be kept forever. Float values are not allowed. +# Default value: 0 (results kept forever). +default_retention_period = 86400 + +# 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 over the maximum retention period: the job results will be kept forever. Float values are not allowed. +# Default value: 0 (results kept forever). +max_retention_period = 43200 + +################### +# 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 over the default execution duration: the execution could never end. Float values are not allowed. +# By default, there is no restriction: default_execution_duration=0. +default_execution_duration = 3600000 + +# [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 over the maximum execution duration: the execution could never end. Float values are not allowed. +# By default, there is no restriction: max_execution_duration=0. +max_execution_duration = 600000 \ No newline at end of file diff --git a/test/tap/config/tap_incorrect_fm.properties b/test/tap/config/tap_incorrect_fm.properties new file mode 100644 index 0000000..ba2e9b9 --- /dev/null +++ b/test/tap/config/tap_incorrect_fm.properties @@ -0,0 +1,64 @@ +################################################################ +# TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = foo + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_no_fm.properties b/test/tap/config/tap_no_fm.properties new file mode 100644 index 0000000..92d62ee --- /dev/null +++ b/test/tap/config/tap_no_fm.properties @@ -0,0 +1,64 @@ +################################################################ +# TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_no_jdbc_driver_1.properties b/test/tap/config/tap_no_jdbc_driver_1.properties new file mode 100644 index 0000000..2d5b5cc --- /dev/null +++ b/test/tap/config/tap_no_jdbc_driver_1.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_no_jdbc_driver_2.properties b/test/tap/config/tap_no_jdbc_driver_2.properties new file mode 100644 index 0000000..7d463fb --- /dev/null +++ b/test/tap/config/tap_no_jdbc_driver_2.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:foo:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = gmantele + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap diff --git a/test/tap/config/tap_valid.properties b/test/tap/config/tap_valid.properties new file mode 100644 index 0000000..9315a53 --- /dev/null +++ b/test/tap/config/tap_valid.properties @@ -0,0 +1,64 @@ +################################################################ +# MINIMUM VALID TAP CONFIGURATION FILE # +# # +# TAP Version: 1.0 # +# Date: 20 Nov. 2013 # +# Author: Gregory Mantelet (ARI) # +################################################################ + +############ +# DATABASE # +############ + +# 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 +#jdbc_driver = + +# It must be a JDBC driver URL. +# 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:postgresql:gmantele + +# Mandatory if the username is not already provided in jdbc_url +# Username used to access to the database. +db_user = foo + +# 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 = pwd + +# List all tables that must be accessed thanks to this TAP Service. +# +# Table names must be separated by a comma. A table name may explicitly specify the schema (if not, the table will be considered as part of the "public" schema). +# +# For each table, you can restrict the list of columns that you to expose via the TAP Service. This list is not mandatory, but if provided it must be +# within parenthesis and comma separated. +# +# Example: schema1.*, schema2.table1, table2, table3(col1, col2, col4, ...) +db_tables = + +# 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 (without spatial extension) and PgSphere for the moment. But you can provide your own SQL translator +# (even if it does not have spatial features), by providing a path to a class (within brackets: {...}) that implements ADQLTranslator (for instance: {apackage.MyADQLTranslator}) +# and which have at least an empty constructor. +# Allowed values: postgres, pgsphere, a class path +sql_translator = postgres + +######### +# 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 path (within brackets: {...}) to a class implementing TAPFileManager and having at least one constructor with only a +# java.util.Properties parameter. +# +# Allowed values: local, a class path. +file_manager = local + +# File path of the directory in which all TAP files (logs, errors, job results, backup, ...) must be. +file_root_path = bin/ext/test/tap -- GitLab