Commit 91a53de4 authored by gmantele's avatar gmantele
Browse files

[TAP] Merge with the branch tapConfig => First developments to support a TAP configuration file.

parents 021054e4 0db34a50
Loading
Loading
Loading
Loading
+27 −1
Original line number Diff line number Diff line
@@ -34,13 +34,30 @@
		<condition><not><isset property="SERVLET-API"/></not></condition>
	</fail>
	
	<fail message="The property JUNIT-API must be set! It provides the path toward a directory or a JAR which contains all classes needed to use JUnit.">
		<condition><not><isset property="JUNIT-API"/></not></condition>
	</fail>
	
	<path id="tap.classpath">
		<pathelement location="${cosJar}" />
		<pathelement location="${jsonJar}" />
		<pathelement location="${stilJar}" />
		<pathelement location="${POSTGRES}" />
		<pathelement location="${SERVLET-API}" />
	</path>
	
	<!-- Define the classpath which includes the junit.jar and the classes after compiling-->
	<path id="junit.class.path">
		<pathelement location="${cosJar}" />
		<pathelement location="${jsonJar}" />
		<pathelement location="${stilJar}" />
		<pathelement location="${POSTGRES}" />
		<pathelement location="${SERVLET-API}" />
		
		<pathelement path="${JUNIT-API}" />
		<pathelement location="bin" />
	</path>
	
	<echo>TAP LIBRARY VERSION = ${version}</echo>
	
	<!-- BUILD ALL TASK -->
@@ -52,7 +69,16 @@
	<target name="cleanAll" depends="clean,cleanJavadoc" description="Delete all files generated by this ANT file for the set version." />
			
	<!-- LIB & SOURCES -->
	<target name="clean" description="Delete the JARs for the library (classes) and for its sources for the set version.">
	<target name="junitValidation" description="Executes all JUnit tests before building the library and stop ANT at any error.">
			<junit printsummary="on" fork="yes" haltonfailure="yes">
				<classpath refid="junit.class.path" />
				<test name="tap.config.AllTests" outfile="testReports">
					<formatter type="plain" usefile="yes" />
				</test>
			</junit>
		</target>
		
		<target name="clean" depends="junitValidation" description="Delete the JARs for the library (classes) and for its sources for the set version.">
		<delete file="${libJarFile}" failonerror="false" />
		<delete file="${srcJarFile}" failonerror="false" />
		<delete dir="${compileDir}" failonerror="false" />
+115 −2
Original line number Diff line number Diff line
@@ -50,10 +50,10 @@ public interface ServiceConnection {
	 * List of possible limit units.
	 * 
	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
	 * @version 2.0 (10/2014)
	 * @version 2.0 (01/2015)
	 */
	public static enum LimitUnit{
		rows("row"), bytes("byte");
		rows("row"), bytes("byte"), kilobytes("kilobyte"), megabytes("megabyte"), gigabytes("gigabyte");

		private final String str;

@@ -61,6 +61,119 @@ public interface ServiceConnection {
			this.str = str;
		}

		/**
		 * Tells whether the given unit has the same type (bytes or rows).
		 * 
		 * @param anotherUnit	A unit.
		 * 
		 * @return				true if the given unit has the same type, false otherwise.
		 * 
		 * @since 1.1
		 */
		public boolean isCompatibleWith(final LimitUnit anotherUnit){
			if (this == rows)
				return anotherUnit == rows;
			else
				return anotherUnit != rows;
		}

		/**
		 * Gets the factor to convert into bytes the value expressed in this unit.
		 * <i>Note: if this unit is not a factor of bytes, 1 is returned (so that the factor does not affect the value).</i>
		 * 
		 * @return The factor need to convert a value expressed in this unit into bytes, or 1 if not a bytes derived unit.
		 * 
		 * @since 1.1
		 */
		public long bytesFactor(){
			switch(this){
				case bytes:
					return 1;
				case kilobytes:
					return 1000;
				case megabytes:
					return 1000000;
				case gigabytes:
					return 1000000000l;
				default:
					return 1;
			}
		}

		/**
		 * Compares the 2 given values (each one expressed in the given unit).
		 * Conversions are done internally in order to make a correct comparison between the 2 limits.
		 * 
		 * @param leftLimit	Value/Limit of the comparison left part.
		 * @param leftUnit	Unit of the comparison left part value.
		 * @param rightLimit	Value/Limit of the comparison right part.
		 * @param rightUnit		Unit of the comparison right part value.
		 * 
		 * @return the value 0 if x == y; a value less than 0 if x < y; and a value greater than 0 if x > y
		 * 
		 * @throws TAPException If the two given units are not compatible.
		 * 
		 * @see #isCompatibleWith(LimitUnit)
		 * @see #bytesFactor()
		 * @see Integer#compare(int, int)
		 * @see Long#compare(long, long)
		 * 
		 * @since 1.1
		 */
		public static int compare(final int leftLimit, final LimitUnit leftUnit, final int rightLimit, final LimitUnit rightUnit) throws TAPException{
			if (!leftUnit.isCompatibleWith(rightUnit))
				throw new TAPException("Limit units (" + leftUnit + " and " + rightUnit + ") are not compatible!");

			if (leftUnit == rows || leftUnit == rightUnit)
				return compare(leftLimit, rightLimit);
			else
				return compare(leftLimit * leftUnit.bytesFactor(), rightLimit * rightUnit.bytesFactor());
		}

		/**
		 * <p><i>(Strict copy of Integer.compare(int,int) of Java 1.7)</i></p>
		 * <p>
		 * 	Compares two {@code int} values numerically.
		 * 	The value returned is identical to what would be returned by:
		 * </p>
		 * <pre>
		 *    Integer.valueOf(x).compareTo(Integer.valueOf(y))
		 * </pre>
		 *
		 * @param  x the first {@code int} to compare
		 * @param  y the second {@code int} to compare
		 * @return the value {@code 0} if {@code x == y};
		 *         a value less than {@code 0} if {@code x < y}; and
		 *         a value greater than {@code 0} if {@code x > y}
		 * 
		 * @since 1.1
		 */
		private static int compare(int x, int y){
			return (x < y) ? -1 : ((x == y) ? 0 : 1);
		}

		/**
		 * <p><i>(Strict copy of Integer.compare(long,long) of Java 1.7)</i></p>
		 * <p>
		 * 	Compares two {@code long} values numerically.
		 * 	The value returned is identical to what would be returned by:
		 * </p>
		 * <pre>
		 *    Long.valueOf(x).compareTo(Long.valueOf(y))
		 * </pre>
		 *
		 * @param  x the first {@code long} to compare
		 * @param  y the second {@code long} to compare
		 * @return the value {@code 0} if {@code x == y};
		 *         a value less than {@code 0} if {@code x < y}; and
		 *         a value greater than {@code 0} if {@code x > y}
		 * 
		 * @since 1.1
		 */
		public static int compare(long x, long y){
			return (x < y) ? -1 : ((x == y) ? 0 : 1);
		}

		@Override
		public String toString(){
			return str;
+585 −0

File added.

Preview size limit exceeded, changes collapsed.

+206 −0
Original line number Diff line number Diff line
package tap.config;

import static tap.config.TAPConfiguration.DEFAULT_BACKUP_BY_USER;
import static tap.config.TAPConfiguration.DEFAULT_BACKUP_FREQUENCY;
import static tap.config.TAPConfiguration.KEY_BACKUP_BY_USER;
import static tap.config.TAPConfiguration.KEY_BACKUP_FREQUENCY;
import static tap.config.TAPConfiguration.KEY_DB_PASSWORD;
import static tap.config.TAPConfiguration.KEY_DB_USERNAME;
import static tap.config.TAPConfiguration.KEY_JDBC_DRIVER;
import static tap.config.TAPConfiguration.KEY_JDBC_URL;
import static tap.config.TAPConfiguration.KEY_SQL_TRANSLATOR;
import static tap.config.TAPConfiguration.VALUE_JDBC_DRIVERS;
import static tap.config.TAPConfiguration.VALUE_PGSPHERE;
import static tap.config.TAPConfiguration.VALUE_POSTGRESQL;
import static tap.config.TAPConfiguration.VALUE_USER_ACTION;
import static tap.config.TAPConfiguration.getProperty;

import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

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;
import adql.translator.JDBCTranslator;
import adql.translator.PgSphereTranslator;
import adql.translator.PostgreSQLTranslator;

public final class DefaultTAPFactory extends AbstractTAPFactory {

	private Class<? extends JDBCTranslator> translator;

	private final String driverPath;
	private final String dbUrl;
	private final String dbUser;
	private final String dbPassword;

	private boolean backupByUser;
	private long backupFrequency;

	public DefaultTAPFactory(ServiceConnection 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 = getProperty(tapConfig, KEY_JDBC_URL);
		if (jdbcDriver == null){
			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.equalsIgnoreCase(VALUE_PGSPHERE))
			translator = PgSphereTranslator.class;

		// case d.) a client defined ADQLTranslator (with the provided class path)
		else if (TAPConfiguration.isClassPath(sqlTranslator))
			translator = TAPConfiguration.fetchClass(sqlTranslator, KEY_SQL_TRANSLATOR, JDBCTranslator.class);

		// 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 dbConn = getConnection("0");
		freeConnection(dbConn);

		/* 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 a {@link JDBCTranslator} 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.
	 */
	protected JDBCTranslator createADQLTranslator() throws TAPException{
		try{
			return translator.getConstructor().newInstance();
		}catch(Exception ex){
			if (ex instanceof TAPException)
				throw (TAPException)ex;
			else
				throw new TAPException("Impossible to create a JDBCTranslator instance with the empty constructor of \"" + translator.getName() + "\" (see the property sql_translator) for the following reason: " + ex.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 getConnection(String jobID) throws TAPException{
		return new JDBCConnection(driverPath, dbUrl, dbUser, dbPassword, createADQLTranslator(), jobID, this.service.getLogger());
	}

	@Override
	public void freeConnection(DBConnection conn){
		try{
			((JDBCConnection)conn).getInnerConnection().close();
		}catch(SQLException se){
			service.getLogger().error("Can not close properly the connection \"" + conn.getID() + "\"!", se);
		}
	}

	@Override
	public int countFreeConnections(){
		return 2;	// 1 for /sync + 1 for /async
	}

	@Override
	public void destroy(){
		// Unregister the JDBC driver:
		try{
			DriverManager.deregisterDriver(DriverManager.getDriver(dbUrl));
		}catch(SQLException e){
			service.getLogger().warning("Can not deregister the JDBC driver manager!");
		}

		// TODO Nothing else to do!
	}

	/**
	 * 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{
		try{
			return (backupFrequency < 0) ? null : new DefaultTAPBackupManager(uws, backupByUser, backupFrequency);
		}catch(UWSException ex){
			throw new TAPException("Impossible to create a backup manager, because: " + ex.getMessage(), ex);
		}
	}

}
+271 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading