Skip to content
ConfigurableServiceConnection.java 64.3 KiB
Newer Older
	 * @throws TAPException	If the corresponding TAP configuration property is wrong.
	 */
	private void initUserIdentifier(final Properties tapConfig) throws TAPException{
		// Get the property value:
		String propValue = getProperty(tapConfig, KEY_USER_IDENTIFIER);
		if (propValue != null)
			userIdentifier = newInstance(propValue, KEY_USER_IDENTIFIER, UserIdentifier.class);
	/**
	 * Initialize the list of all allowed coordinate systems.
	 * @param tapConfig	The content of the TAP configuration file.
	 * @throws TAPException	If the corresponding TAP configuration properties are wrong.
	 */
	private void initCoordSys(final Properties tapConfig) throws TAPException{
		// Get the property value:
		String propValue = getProperty(tapConfig, KEY_COORD_SYS);

		// NO VALUE => ALL COORD SYS ALLOWED!
		if (propValue == null)
			lstCoordSys = null;

		// "NONE" => ALL COORD SYS FORBIDDEN (= no coordinate system expression is allowed)!
		else if (propValue.equalsIgnoreCase(VALUE_NONE))
			lstCoordSys = new ArrayList<String>(0);

		// "ANY" => ALL COORD SYS ALLOWED (= any coordinate system is allowed)!
		else if (propValue.equalsIgnoreCase(VALUE_ANY))
			lstCoordSys = null;

		// OTHERWISE, JUST THE ALLOWED ONE ARE LISTED:
		else{
			// split all the list items:
			String[] items = propValue.split(",");
			if (items.length > 0){
				lstCoordSys = new ArrayList<String>(items.length);
				for(String item : items){
					item = item.trim();
					// empty item => ignored
					if (item.length() <= 0)
						continue;
					// "NONE" is not allowed inside a list => error!
					else if (item.toUpperCase().equals(VALUE_NONE))
						throw new TAPException("The special value \"" + VALUE_NONE + "\" can not be used inside a list! It MUST be used in replacement of a whole list to specify that no value is allowed.");
					// "ANY" is not allowed inside a list => error!
					else if (item.toUpperCase().equals(VALUE_ANY))
						throw new TAPException("The special value \"" + VALUE_ANY + "\" can not be used inside a list! It MUST be used in replacement of a whole list to specify that any value is allowed.");
					// parse the coordinate system regular expression in order to check it:
					else{
						try{
							STCS.buildCoordSysRegExp(new String[]{item});
							lstCoordSys.add(item);
						}catch(ParseException pe){
							throw new TAPException("Incorrect coordinate system regular expression (\"" + item + "\"): " + pe.getMessage(), pe);
						}
					}
				}
				// if finally no item has been specified, consider it as "any coordinate system allowed":
				if (lstCoordSys.size() == 0)
					lstCoordSys = null;
			}else
				lstCoordSys = null;
		}
	}

	/**
	 * Initialize the list of all allowed ADQL geometrical functions.
	 * @param tapConfig	The content of the TAP configuration file.
	 * @throws TAPException	If the corresponding TAP configuration properties are wrong.
	 */
	private void initADQLGeometries(final Properties tapConfig) throws TAPException{
		// Get the property value:
		String propValue = getProperty(tapConfig, KEY_GEOMETRIES);

		// NO VALUE => ALL FCT ALLOWED!
		if (propValue == null)
			geometries = null;

		// "NONE" => ALL FCT FORBIDDEN (= none of these functions are allowed)!
		else if (propValue.equalsIgnoreCase(VALUE_NONE))
			geometries = new ArrayList<String>(0);

		// "ANY" => ALL FCT ALLOWED (= all of these functions are allowed)!
		else if (propValue.equalsIgnoreCase(VALUE_ANY))
			geometries = null;

		// OTHERWISE, JUST THE ALLOWED ONE ARE LISTED:
		else{
			// split all the list items:
			String[] items = propValue.split(",");
			if (items.length > 0){
				geometries = new ArrayList<String>(items.length);
				for(String item : items){
					item = item.trim();
					// empty item => ignored
					if (item.length() <= 0)
						continue;
					// if it is a name of known ADQL geometrical function, add it to the list:
					else if (item.toUpperCase().matches(GEOMETRY_REGEXP))
						geometries.add(item.toUpperCase());
					// "NONE" is not allowed inside a list => error!
					else if (item.toUpperCase().equals(VALUE_NONE))
						throw new TAPException("The special value \"" + VALUE_NONE + "\" can not be used inside a list! It MUST be used in replacement of a whole list to specify that no value is allowed.");
					// "ANY" is not allowed inside a list => error!
					else if (item.toUpperCase().equals(VALUE_ANY))
						throw new TAPException("The special value \"" + VALUE_ANY + "\" can not be used inside a list! It MUST be used in replacement of a whole list to specify that any value is allowed.");
					// unknown value => error!
					else
						throw new TAPException("Unknown ADQL geometrical function: \"" + item + "\"!");
				}
				// if finally no item has been specified, consider it as "all functions allowed":
				if (geometries.size() == 0)
					geometries = null;
			}else
				geometries = null;
		}
	}

	private final String REGEXP_SIGNATURE = "(\\([^()]*\\)|[^,])*";

	private final String REGEXP_CLASSPATH = "\\{[^{}]*\\}";

	private final String REGEXP_DESCRIPTION = "\"((\\\\\"|[^\"])*)\"";

	private final String REGEXP_UDF = "\\[\\s*(" + REGEXP_SIGNATURE + ")\\s*(,\\s*(" + REGEXP_CLASSPATH + ")?\\s*(,\\s*(" + REGEXP_DESCRIPTION + ")?\\s*)?)?\\]";

	private final String REGEXP_UDFS = "\\s*(" + REGEXP_UDF + ")\\s*(,(.*))?";
	private final int GROUP_SIGNATURE = 2;
	private final int GROUP_CLASSPATH = 5;
	private final int GROUP_DESCRIPTION = 8;
	private final int GROUP_NEXT_UDFs = 11;

	/**
	 * Initialize the list of all known and allowed User Defined Functions.
	 * @param tapConfig	The content of the TAP configuration file.
	 * @throws TAPException	If the corresponding TAP configuration properties are wrong.
	 */
	private void initUDFs(final Properties tapConfig) throws TAPException{
		// Get the property value:
		String propValue = getProperty(tapConfig, KEY_UDFS);

		// NO VALUE => NO UNKNOWN FCT ALLOWED!
		if (propValue == null || propValue.trim().length() == 0)
			udfs = new ArrayList<FunctionDef>(0);

		// "NONE" => NO UNKNOWN FCT ALLOWED (= none of the unknown functions are allowed)!
		else if (propValue.equalsIgnoreCase(VALUE_NONE))
			udfs = new ArrayList<FunctionDef>(0);

		// "ANY" => ALL UNKNOWN FCT ALLOWED (= all of the unknown functions are allowed)!
		else if (propValue.equalsIgnoreCase(VALUE_ANY))
			udfs = null;

		// OTHERWISE, JUST THE ALLOWED ONE ARE LISTED:
		else{

			Pattern patternUDFS = Pattern.compile(REGEXP_UDFS);
			String udfList = propValue;
			int udfOffset = 1;
			while(udfList != null){
				Matcher matcher = patternUDFS.matcher(udfList);
				if (matcher.matches()){

					// Fetch the signature, classpath and description:
					String signature = matcher.group(GROUP_SIGNATURE),
							classpath = matcher.group(GROUP_CLASSPATH),
							description = matcher.group(GROUP_DESCRIPTION);

					// If no signature...
					boolean ignoreUdf = false;
					if (signature == null || signature.length() == 0){
						// ...BUT a class name => error
						if (classpath != null)
							throw new TAPException("Missing UDF declaration! (position in the property " + KEY_UDFS + ": " + (udfOffset + matcher.start(GROUP_SIGNATURE)) + "-" + (udfOffset + matcher.end(GROUP_SIGNATURE)) + ")");
						// ... => ignore this item
						else
							ignoreUdf = true;
					}
					if (!ignoreUdf){
						// Add the new UDF in the list:
						try{
							// resolve the function signature:
							FunctionDef def = FunctionDef.parse(signature);
							// resolve the class name:
							if (classpath != null){
								if (isClassName(classpath)){
									Class<? extends UserDefinedFunction> fctClass = null;
									try{
										// fetch the class:
										fctClass = fetchClass(classpath, KEY_UDFS, UserDefinedFunction.class);
										// set the class inside the UDF definition:
										def.setUDFClass(fctClass);
									}catch(TAPException te){
										throw new TAPException("Invalid class name for the UDF definition \"" + def + "\": " + te.getMessage() + " (position in the property " + KEY_UDFS + ": " + (udfOffset + matcher.start(GROUP_CLASSPATH)) + "-" + (udfOffset + matcher.end(GROUP_CLASSPATH)) + ")", te);
									}catch(IllegalArgumentException iae){
										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 + ": " + (udfOffset + matcher.start(GROUP_CLASSPATH)) + "-" + (udfOffset + matcher.end(GROUP_CLASSPATH)) + ")");
									}
								}else
									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 + ": " + (udfOffset + matcher.start(GROUP_CLASSPATH)) + "-" + (udfOffset + matcher.end(GROUP_CLASSPATH)) + ")");
							// set the description if any:
							if (description != null)
								def.description = description;
							// add the UDF:
							udfs.add(def);
						}catch(ParseException pe){
							throw new TAPException("Wrong UDF declaration syntax: " + pe.getMessage() + " (position in the property " + KEY_UDFS + ": " + (udfOffset + matcher.start(GROUP_SIGNATURE)) + "-" + (udfOffset + matcher.end(GROUP_SIGNATURE)) + ")", pe);
						}
					// Prepare the next iteration (i.e. the other UDFs):
					udfList = matcher.group(GROUP_NEXT_UDFs);
					if (udfList != null && udfList.trim().length() == 0)
						udfList = null;
					udfOffset += matcher.start(GROUP_NEXT_UDFs);
				}else
					throw new TAPException("Wrong UDF declaration syntax: \"" + udfList + "\"! (position in the property " + KEY_UDFS + ": " + udfOffset + "-" + (propValue.length() + 1) + ")");
			}
	@Override
	public String getProviderName(){
		return providerName;
	}

	@Override
	public String getProviderDescription(){
		return serviceDescription;
	}

	@Override
	public boolean isAvailable(){
		return isAvailable;
	}

	@Override
	public String getAvailability(){
		return availability;
	}

	@Override
	public void setAvailable(boolean isAvailable, String message){
		this.isAvailable = isAvailable;
		availability = message;
	}

	@Override
	public int[] getRetentionPeriod(){
		return retentionPeriod;
	}

	/**
	 * <p>Set the default retention period.</p>
	 * <p>This period is set by default if the user did not specify one before the execution of his query.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function will apply the given retention period only if legal compared to the currently set maximum value.
	 * 	In other words, if the given value is less or equals to the current maximum retention period.
	 * </em></p>
	 * @param period	New default retention period (in seconds).
	 * @return	<i>true</i> if the given retention period has been successfully set, <i>false</i> otherwise.
	 */
	public boolean setDefaultRetentionPeriod(final int period){
		if ((retentionPeriod[1] <= 0) || (period > 0 && period <= retentionPeriod[1])){
			retentionPeriod[0] = period;
			return true;
		}else
			return false;
	}

	/**
	 * <p>Set the maximum retention period.</p>
	 * <p>This period limits the default retention period and the retention period specified by a user.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function may reduce the default retention period if the current default retention period is bigger
	 * 	to the new maximum retention period. In a such case, the default retention period is set to the
	 * 	new maximum retention period.
	 * </em></p>
	 * @param period	New maximum retention period (in seconds).
	 */
	public void setMaxRetentionPeriod(final int period){
		// Decrease the default retention period if it will be bigger than the new maximum retention period:
		if (period > 0 && (retentionPeriod[0] <= 0 || period < retentionPeriod[0]))
			retentionPeriod[0] = period;
		// Set the new maximum retention period:
		retentionPeriod[1] = period;
	}

	@Override
	public int[] getExecutionDuration(){
		return executionDuration;
	}

	/**
	 * <p>Set the default execution duration.</p>
	 * <p>This duration is set by default if the user did not specify one before the execution of his query.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function will apply the given execution duration only if legal compared to the currently set maximum value.
	 * 	In other words, if the given value is less or equals to the current maximum execution duration.
	 * </em></p>
	 * @param duration	New default execution duration (in milliseconds).
	 * @return	<i>true</i> if the given execution duration has been successfully set, <i>false</i> otherwise.
	 */
	public boolean setDefaultExecutionDuration(final int duration){
		if ((executionDuration[1] <= 0) || (duration > 0 && duration <= executionDuration[1])){
			executionDuration[0] = duration;
	/**
	 * <p>Set the maximum execution duration.</p>
	 * <p>This duration limits the default execution duration and the execution duration specified by a user.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function may reduce the default execution duration if the current default execution duration is bigger
	 * 	to the new maximum execution duration. In a such case, the default execution duration is set to the
	 * 	new maximum execution duration.
	 * </em></p>
	 * @param duration	New maximum execution duration (in milliseconds).
	 */
	public void setMaxExecutionDuration(final int duration){
		// Decrease the default execution duration if it will be bigger than the new maximum execution duration:
		if (duration > 0 && (executionDuration[0] <= 0 || duration < executionDuration[0]))
			executionDuration[0] = duration;
		// Set the new maximum execution duration:
		executionDuration[1] = duration;
	public Iterator<OutputFormat> getOutputFormats(){
		return outputFormats.iterator();
	public OutputFormat getOutputFormat(final String mimeOrAlias){
		if (mimeOrAlias == null || mimeOrAlias.trim().isEmpty())
			return null;

			if ((f.getMimeType() != null && f.getMimeType().equalsIgnoreCase(mimeOrAlias)) || (f.getShortMimeType() != null && f.getShortMimeType().equalsIgnoreCase(mimeOrAlias)))
				return f;
		}
	/**
	 * <p>Add the given {@link OutputFormat} in the list of output formats supported by the TAP service.</p>
	 * <p><b>Warning:
	 * 	No verification is done in order to avoid duplicated output formats in the list.
	 * 	NULL objects are merely ignored silently.
	 * </b></p>
	 * @param newOutputFormat	New output format.
	 */
	public void addOutputFormat(final OutputFormat newOutputFormat){
		if (newOutputFormat != null)
			outputFormats.add(newOutputFormat);
	 * @param mimeOrAlias	Full or short MIME type of the output format to remove.
	 * @return	<i>true</i> if the specified format has been found and successfully removed from the list,
	 *        	<i>false</i> otherwise.
	 */
	public boolean removeOutputFormat(final String mimeOrAlias){
		if (of != null)
			return outputFormats.remove(of);
		else
			return false;
	public int[] getOutputLimit(){
		return outputLimits;
	 * <p>This limit is set by default if the user did not specify one before the execution of his query.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function will apply the given output limit only if legal compared to the currently set maximum value.
	 * 	In other words, if the given value is less or equals to the current maximum output limit.
	 * </em></p>
	 * @param limit	New default output limit (in number of rows).
	 * @return	<i>true</i> if the given output limit has been successfully set, <i>false</i> otherwise.
	 */
	public boolean setDefaultOutputLimit(final int limit){
		if ((outputLimits[1] <= 0) || (limit > 0 && limit <= outputLimits[1])){
			outputLimits[0] = limit;
			return true;
		}else
			return false;
	 * <p>This output limit limits the default output limit and the output limit specified by a user.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function may reduce the default output limit if the current default output limit is bigger
	 * 	to the new maximum output limit. In a such case, the default output limit is set to the
	 * 	new maximum output limit.
	 * </em></p>
	 * @param limit	New maximum output limit (in number of rows).
	 */
	public void setMaxOutputLimit(final int limit){
		// Decrease the default output limit if it will be bigger than the new maximum output limit:
		if (limit > 0 && (outputLimits[0] <= 0 || limit < outputLimits[0]))
			outputLimits[0] = limit;
		// Set the new maximum output limit:
		outputLimits[1] = limit;
	public final LimitUnit[] getOutputLimitType(){
		return new LimitUnit[]{LimitUnit.rows,LimitUnit.rows};
	}

	@Override
	public Collection<String> getCoordinateSystems(){
	public boolean uploadEnabled(){
		return isUploadEnabled;
	public void setUploadEnabled(final boolean enabled){
		isUploadEnabled = enabled;
	}

	@Override
	public int[] getUploadLimit(){
	}

	@Override
	public LimitUnit[] getUploadLimitType(){
	 * @param type	Unit of upload limit (rows or bytes).
	 */
	public void setUploadLimitType(final LimitUnit type){
		if (type != null)
			uploadLimitTypes = new LimitUnit[]{type,type};
	}

	 * <p><em><b>Important note:</b>
	 * 	This function will apply the given upload limit only if legal compared to the currently set maximum value.
	 * 	In other words, if the given value is less or equals to the current maximum upload limit.
	 * </em></p>
	 * @param limit	New default upload limit.
	 * @return	<i>true</i> if the given upload limit has been successfully set, <i>false</i> otherwise.
	 */
	public boolean setDefaultUploadLimit(final int limit){
		try{
			if ((uploadLimits[1] <= 0) || (limit > 0 && LimitUnit.compare(limit, uploadLimitTypes[0], uploadLimits[1], uploadLimitTypes[1]) <= 0)){
				uploadLimits[0] = limit;
				return true;
			}
		}catch(TAPException e){}
		return false;
	}

	 * <p>This upload limit limits the default upload limit.</p>
	 * <p><em><b>Important note:</b>
	 * 	This function may reduce the default upload limit if the current default upload limit is bigger
	 * 	to the new maximum upload limit. In a such case, the default upload limit is set to the
	 * 	new maximum upload limit.
	 * </em></p>
	 * @param limit	New maximum upload limit.
	 */
	public void setMaxUploadLimit(final int limit){
			// Decrease the default output limit if it will be bigger than the new maximum output limit:
			if (limit > 0 && (uploadLimits[0] <= 0 || LimitUnit.compare(limit, uploadLimitTypes[1], uploadLimits[0], uploadLimitTypes[0]) < 0))
				uploadLimits[0] = limit;
			// Set the new maximum output limit:
			uploadLimits[1] = limit;
		}catch(TAPException e){}
	public int getMaxUploadSize(){
	/**
	 * <p>Set the maximum size of a VOTable files set that can be uploaded in once.</p>
	 * <p><b>Warning:
	 * 	This size can not be negative or 0. If the given value is in this case, nothing will be done
	 * 	and <i>false</i> will be returned.
	 * 	On the contrary to the other limits, no "unlimited" limit is possible here ; only the
	 * 	maximum value can be set (i.e. maximum positive integer value).
	 * </b></p>
	 * @param maxSize	New maximum size (in bytes).
	 * @return	<i>true</i> if the size has been successfully set, <i>false</i> otherwise.
	 */
	public boolean setMaxUploadSize(final int maxSize){
		// No "unlimited" value possible there:
		if (maxSize <= 0)
			return false;

		// Otherwise, set the maximum upload file size:
		maxUploadSize = maxSize;
		return true;
	}

	@Override
	public int getNbMaxAsyncJobs(){
		return maxAsyncJobs;
	}

	@Override
	public UserIdentifier getUserIdentifier(){
	}

	@Override
	public TAPMetadata getTAPMetadata(){
	@Override
	public int[] getFetchSize(){
		return fetchSize;
	}