Commit c790223e authored by gmantele's avatar gmantele
Browse files

[TAP] Add a 3rd possibility to set metadata in the TAP configuration file:...

[TAP] Add a 3rd possibility to set metadata in the TAP configuration file: give an extension of TAPMetadata having either an empty constructor or a constructor with a UWSFileManager, a TAPFactory and a TAPLog. & Rename function TAPConfiguration.isClassPath(String) into TAPConfiguration.isClassName(String).
parent 5c07880a
Loading
Loading
Loading
Loading
+65 −34
Original line number Diff line number Diff line
@@ -48,12 +48,14 @@ import static tap.config.TAPConfiguration.VALUE_VOTABLE;
import static tap.config.TAPConfiguration.VALUE_XML;
import static tap.config.TAPConfiguration.fetchClass;
import static tap.config.TAPConfiguration.getProperty;
import static tap.config.TAPConfiguration.isClassPath;
import static tap.config.TAPConfiguration.isClassName;
import static tap.config.TAPConfiguration.newInstance;
import static tap.config.TAPConfiguration.parseLimit;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -174,7 +176,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
		// 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 {...}.");
			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 name between {...}.");
		else
			fileManagerType = fileManagerType.trim();

@@ -238,7 +240,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
		// Get the fetching method to use:
		String metaFetchType = getProperty(tapConfig, KEY_METADATA);
		if (metaFetchType == null)
			throw new TAPException("The property \"" + KEY_METADATA + "\" is missing! It is required to create a TAP Service. Two possible values: " + VALUE_XML + " (to get metadata from a TableSet XML document) or " + VALUE_DB + " (to fetch metadata from the database schema TAP_SCHEMA).");
			throw new TAPException("The property \"" + KEY_METADATA + "\" is missing! It is required to create a TAP Service. Three possible values: " + VALUE_XML + " (to get metadata from a TableSet XML document), " + VALUE_DB + " (to fetch metadata from the database schema TAP_SCHEMA) or the name (between {}) of a class extending TAPMetadata.");

		TAPMetadata metadata = null;

@@ -277,6 +279,52 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
					tapFactory.freeConnection(conn);
			}
		}
		// MANUAL ~ TAPMETADATA CLASS
		else if (isClassName(metaFetchType)){
			/* 1. Get the metadata */
			// get the class:
			Class<? extends TAPMetadata> metaClass = fetchClass(metaFetchType, KEY_METADATA, TAPMetadata.class);
			if (metaClass == TAPMetadata.class)
				throw new TAPException("Wrong class for the property \"" + KEY_METADATA + "\": \"" + metaClass.getName() + "\"! The class provided in this property MUST EXTEND tap.metadata.TAPMetadata.");
			try{
				// get one of the expected constructors:
				try{
					// (UWSFileManager, TAPFactory, TAPLog):
					Constructor<? extends TAPMetadata> constructor = metaClass.getConstructor(UWSFileManager.class, TAPFactory.class, TAPLog.class);
					// create the TAP metadata:
					metadata = constructor.newInstance(fileManager, tapFactory, logger);
				}catch(NoSuchMethodException nsme){
					// () (empty constructor):
					Constructor<? extends TAPMetadata> constructor = metaClass.getConstructor();
					// create the TAP metadata:
					metadata = constructor.newInstance();
				}
			}catch(NoSuchMethodException nsme){
				throw new TAPException("Missing constructor tap.metadata.TAPMetadata() or tap.metadata.TAPMetadata(uws.service.file.UWSFileManager, tap.TAPFactory, tap.log.TAPLog)! See the value \"" + metaFetchType + "\" of the property \"" + KEY_METADATA + "\".");
			}catch(InstantiationException ie){
				throw new TAPException("Impossible to create an instance of an abstract class: \"" + metaClass.getName() + "\"! See the value \"" + metaFetchType + "\" of the property \"" + KEY_METADATA + "\".");
			}catch(InvocationTargetException ite){
				if (ite.getCause() != null){
					if (ite.getCause() instanceof TAPException)
						throw (TAPException)ite.getCause();
					else
						throw new TAPException(ite.getCause());
				}else
					throw new TAPException(ite);
			}catch(Exception ex){
				throw new TAPException("Impossible to create an instance of tap.metadata.TAPMetadata as specified in the property \"" + KEY_METADATA + "\": \"" + metaFetchType + "\"!", ex);
			}

			/* 2. Update the database */
			DBConnection conn = null;
			try{
				conn = tapFactory.getConnection("SET_TAP_SCHEMA");
				conn.setTAPSchema(metadata);
			}finally{
				if (conn != null)
					tapFactory.freeConnection(conn);
			}
		}
		// INCORRECT VALUE => ERROR!
		else
			throw new TAPException("Unsupported value for the property \"" + KEY_METADATA + "\": \"" + metaFetchType + "\"! Only two values are allowed: " + VALUE_XML + " (to get metadata from a TableSet XML document) or " + VALUE_DB + " (to fetch metadata from the database schema TAP_SCHEMA).");
@@ -450,7 +498,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
					hasVotableFormat = true;
			}
			// custom OutputFormat
			else if (isClassPath(f))
			else if (isClassName(f))
				outputFormats.add(TAPConfiguration.newInstance(f, KEY_OUTPUT_FORMATS, OutputFormat.class, new Class<?>[]{ServiceConnection.class}, new Object[]{this}));
			// unknown format
			else
@@ -583,25 +631,8 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
	private void initUserIdentifier(final Properties tapConfig) throws TAPException{
		// Get the property value:
		String propValue = getProperty(tapConfig, KEY_USER_IDENTIFIER);
		if (propValue == null)
			return;

		// Check the value is a class path:
		if (!isClassPath(propValue))
			throw new TAPException("Class path expected for the property \"" + KEY_USER_IDENTIFIER + "\", instead of: \"" + propValue + "\"!");

		// Fetch the class:
		Class<? extends UserIdentifier> c = fetchClass(propValue, KEY_USER_IDENTIFIER, UserIdentifier.class);

		// Create an instance with the empty constructor:
		try{
			userIdentifier = c.getConstructor().newInstance();
		}catch(Exception e){
			if (e instanceof TAPException)
				throw (TAPException)e;
			else
				throw new TAPException("Impossible to create a UserIdentifier instance with the empty constructor of \"" + c.getName() + "\" (see the property user_identifier) for the following reason: " + e.getMessage());
		}
		if (propValue != null)
			userIdentifier = newInstance(propValue, KEY_USER_IDENTIFIER, UserIdentifier.class);
	}

	private void initADQLGeometries(final Properties tapConfig) throws TAPException{
@@ -708,17 +739,17 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
							within_params = true;
							buf.append(c);
							break;
						case '{': /* start of a classpath */
						case '{': /* start of a class name */
							within_classpath = true;
							buf.append(c);
							break;
						case ',': /* separation between the signature and the classpath */
						case ',': /* separation between the signature and the class name */
							// count commas within this item:
							if (++nbComma > 1)
								// if more than 1, throw an error:
								throw new TAPException("Wrong UDF declaration syntax: only two items (signature and classpath) can be given within brackets. (position in the property " + KEY_UDFS + ": " + ind + ")");
								throw new TAPException("Wrong UDF declaration syntax: only two items (signature and class name) can be given within brackets. (position in the property " + KEY_UDFS + ": " + ind + ")");
							else{
								// end of the signature and start of the class path:
								// end of the signature and start of the class name:
								signature = buf.toString();
								buf.delete(0, buf.length());
								posSignature[1] = ind;
@@ -739,7 +770,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {

							// no signature...
							if (signature == null || signature.length() == 0){
								// ...BUT a classpath => error
								// ...BUT a class name => error
								if (classpath != null)
									throw new TAPException("Missing UDF declaration! (position in the property " + KEY_UDFS + ": " + posSignature[0] + "-" + posSignature[1] + ")");
								// ... => ignore this item
@@ -751,9 +782,9 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
							try{
								// resolve the function signature:
								FunctionDef def = FunctionDef.parse(signature);
								// resolve the class path:
								// resolve the class name:
								if (classpath != null){
									if (isClassPath(classpath)){
									if (isClassName(classpath)){
										Class<? extends UserDefinedFunction> fctClass = null;
										try{
											// fetch the class:
@@ -761,12 +792,12 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
											// set the class inside the UDF definition:
											def.setUDFClass(fctClass);
										}catch(TAPException te){
											throw new TAPException("Invalid class path for the UDF definition \"" + def + "\": " + te.getMessage() + " (position in the property " + KEY_UDFS + ": " + posClassPath[0] + "-" + posClassPath[1] + ")", te);
											throw new TAPException("Invalid class name for the UDF definition \"" + def + "\": " + te.getMessage() + " (position in the property " + KEY_UDFS + ": " + posClassPath[0] + "-" + posClassPath[1] + ")", te);
										}catch(IllegalArgumentException iae){
											throw new TAPException("Invalid class path for the UDF definition \"" + def + "\": missing a constructor with a single parameter of type ADQLOperand[] " + (fctClass != null ? "in the class \"" + fctClass.getName() + "\"" : "") + "! (position in the property " + KEY_UDFS + ": " + posClassPath[0] + "-" + posClassPath[1] + ")");
											throw new TAPException("Invalid class name for the UDF definition \"" + def + "\": missing a constructor with a single parameter of type ADQLOperand[] " + (fctClass != null ? "in the class \"" + fctClass.getName() + "\"" : "") + "! (position in the property " + KEY_UDFS + ": " + posClassPath[0] + "-" + posClassPath[1] + ")");
										}
									}else
										throw new TAPException("Invalid class path for the UDF definition \"" + def + "\": \"" + classpath + "\" is not a class path (or is not surrounding by {} as expected in this property file)! (position in the property " + KEY_UDFS + ": " + posClassPath[0] + "-" + posClassPath[1] + ")");
										throw new TAPException("Invalid class name for the UDF definition \"" + def + "\": \"" + classpath + "\" is not a class name (or is not surrounding by {} as expected in this property file)! (position in the property " + KEY_UDFS + ": " + posClassPath[0] + "-" + posClassPath[1] + ")");
								}
								// add the UDF:
								udfs.add(def);
@@ -794,7 +825,7 @@ public final class ConfigurableServiceConnection implements ServiceConnection {
						case ',':
							break;
						default:
							throw new TAPException("Wrong UDF declaration syntax: unexpected character at position " + ind + " in the property " + KEY_UDFS + ": \"" + c + "\"! A UDF declaration must have one of the following syntaxes: \"[signature]\" or \"[signature,{classpath}]\".");
							throw new TAPException("Wrong UDF declaration syntax: unexpected character at position " + ind + " in the property " + KEY_UDFS + ": \"" + c + "\"! A UDF declaration must have one of the following syntaxes: \"[signature]\" or \"[signature,{className}]\".");
					}
				}
			}
+2 −2
Original line number Diff line number Diff line
@@ -139,8 +139,8 @@ public final class ConfigurableTAPFactory extends AbstractTAPFactory {
		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))
		// case d: a client defined ADQLTranslator (with the provided class name)
		else if (TAPConfiguration.isClassName(sqlTranslator))
			translator = TAPConfiguration.fetchClass(sqlTranslator, KEY_SQL_TRANSLATOR, JDBCTranslator.class);

		// case e: unsupported value
+2 −2
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ import static tap.config.TAPConfiguration.KEY_HOME_PAGE;
import static tap.config.TAPConfiguration.KEY_HOME_PAGE_MIME_TYPE;
import static tap.config.TAPConfiguration.TAP_CONF_PARAMETER;
import static tap.config.TAPConfiguration.getProperty;
import static tap.config.TAPConfiguration.isClassPath;
import static tap.config.TAPConfiguration.isClassName;
import static tap.config.TAPConfiguration.newInstance;

import java.io.File;
@@ -117,7 +117,7 @@ public class ConfigurableTAPServlet extends HttpServlet {
		String propValue = getProperty(tapConf, KEY_HOME_PAGE);
		if (propValue != null){
			// If it is a class path, replace the current home page by an instance of this class:
			if (isClassPath(propValue)){
			if (isClassName(propValue)){
				try{
					tap.setHomePage(newInstance(propValue, KEY_HOME_PAGE, HomePage.class, new Class<?>[]{TAP.class}, new Object[]{tap}));
				}catch(TAPException te){
+17 −45
Original line number Diff line number Diff line
package tap.config;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

@@ -144,35 +141,35 @@ public final class TAPConfiguration {
	}

	/**
	 * Test whether a property value is a class path.
	 * Test whether a property value is a class name.
	 * Expected syntax: a non-empty string surrounded by brackets ('{' and '}').
	 * 
	 * Note: The class path itself is not checked!
	 * Note: The class name 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.
	 * @return <i>true</i> if the given value is formatted as a class name, <i>false</i> otherwise.
	 */
	public final static boolean isClassPath(final String value){
	public final static boolean isClassName(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. 
	 * Fetch the class object corresponding to the class name 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 value			Value which is supposed to contain the class name between brackets (see {@link #isClassName(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").
	 * @throws TAPException	If the class name is incorrect or if its type is not compatible with the parameterized type C (represented by the parameter "expectedType").
	 * 
	 * @see {@link #isClassPath(String)}
	 * @see {@link #isClassName(String)}
	 */
	@SuppressWarnings("unchecked")
	public final static < C > Class<? extends C> fetchClass(final String value, final String propertyName, final Class<C> expectedType) throws TAPException{
		if (!isClassPath(value))
		if (!isClassName(value))
			return null;

		String classPath = value.substring(1, value.length() - 1).trim();
@@ -182,13 +179,13 @@ public final class TAPConfiguration {
		try{
			Class<? extends C> classObject = (Class<? extends C>)Class.forName(classPath);
			if (!expectedType.isAssignableFrom(classObject))
				throw new TAPException("The class specified by the property " + propertyName + " (" + value + ") is not implementing " + expectedType.getName() + ".");
				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.");
			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() + ".");
			throw new TAPException("The class specified by the property \"" + propertyName + "\" (" + value + ") is not implementing " + expectedType.getName() + ".");
		}
	}

@@ -197,7 +194,7 @@ public final class TAPConfiguration {
	 * 
	 * <p>The instance is created using the empty constructor of the specified class.</p>
	 * 
	 * @param value			Value which is supposed to contain the classpath between brackets (see {@link #isClassPath(String)} for more details)
	 * @param value			Value which is supposed to contain the class name between brackets (see {@link #isClassName(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.
	 * 
@@ -208,7 +205,7 @@ public final class TAPConfiguration {
	 *                     	or if the specified class has no empty constructor
	 *                     	or if an error occurred while calling this constructor.
	 * 
	 * @see {@link #isClassPath(String)}
	 * @see {@link #isClassName(String)}
	 * @see #fetchClass(String, String, Class)
	 */
	public final static < C > C newInstance(final String propValue, final String propName, final Class<C> expectedType) throws TAPException{
@@ -223,7 +220,7 @@ public final class TAPConfiguration {
	 * 	The number and types of given parameters MUST match exactly to the list of parameter types.
	 * </p>
	 * 
	 * @param value			Value which is supposed to contain the classpath between brackets (see {@link #isClassPath(String)} for more details)
	 * @param value			Value which is supposed to contain the class name between brackets (see {@link #isClassName(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.
	 * @param pTypes		List of each constructor parameter type. Each type MUST be exactly the type declared in the class constructor to select. <i>NULL or empty array if no parameter.</i>
@@ -236,12 +233,12 @@ public final class TAPConfiguration {
	 *                     	or if the constructor with the specified parameters can not be found
	 *                     	or if an error occurred while calling this constructor.
	 * 
	 * @see {@link #isClassPath(String)}
	 * @see {@link #isClassName(String)}
	 * @see #fetchClass(String, String, Class)
	 */
	public final static < C > C newInstance(final String propValue, final String propName, final Class<C> expectedType, final Class<?>[] pTypes, final Object[] parameters) throws TAPException{
		// Ensure the given name is a class name specification:
		if (!isClassPath(propValue))
		if (!isClassName(propValue))
			throw new TAPException("Class name expected for the property \"" + propName + "\" instead of: \"" + propValue + "\"! The specified class must extend/implement " + expectedType.getName() + ".");

		Class<? extends C> classObj = null;
@@ -378,29 +375,4 @@ public final class TAPConfiguration {
		return new Object[]{((numValue <= 0) ? -1 : numValue),unit};
	}

	public final static void main(final String[] args) throws Throwable{

		FileInputStream configFileStream = null;
		try{
			final File configFile = new File("src/tap/config/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();
		}
	}

}
+13 −11

File changed.

Preview size limit exceeded, changes collapsed.

Loading