/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) COSYLAB - Control System Laboratory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*******************************************************************************/
package com.cosylab.acs.jms;
import com.codestreet.selector.Selector;
import com.codestreet.selector.ISelector;
import com.codestreet.selector.parser.Identifier;
import com.codestreet.selector.parser.InvalidSelectorException;
import com.codestreet.selector.parser.IValueProvider;
import com.codestreet.selector.parser.Result;
import com.codestreet.selector.parser.NumericValue;
/**
* This object is a message selector.
* It owns an SQL92 string describing an SQL statement and checkes
* if a message matches with that statement.
* To enhance the response time of the process, it uses ValueProvider
* method of selector.
*
* @see JMS specifications for further details
*
* @author acaproni
*
*/
public class ACSJMSMessageSelector {
/**
* The parser makes callbacks to the getValue method of
* this object whenever needs a value.
* I need to customize the value provider becase we need to check
* the content of the message. The package provides a ValueProvider
* for JMS messages that allows to use the dot notation to access
* the field of the message but I prefer this iplementation.
*
* @author acaproni
*
*/
private class ACSJMSValueProvider implements IValueProvider {
/**
* The message whose values are neeed by the parser
*/
ACSJMSMessage message;
/**
* Constructor
* @param jmsMessage The message whose values are neeed by the parser
*/
public ACSJMSValueProvider(ACSJMSMessage jmsMessage) {
this.message=jmsMessage;
}
/**
* Return the value of a property.
* It looks for the property in the body of the message, not in the header.
* JMS specification states that the properties to check should only be
* those of the header so we are not following the specs here (even if it
* is common use).
* It should be better at least to check ALSO in the header properties
*
* NOTE: Properties are object of type org.jacorb.orb.Any containing a String.
* This method tries to parse the String in order to return
* an object of the right type (true and false for booleans)
*
* @param identifier The identifier of the property (the key, it is a String)
* @param correlation The correlation object (not used)
* @return The value of the property or null if the property was not found
*/
public Object getValue(Object identifier, Object correlation) {
if (!(identifier instanceof Identifier)) {
throw new IllegalArgumentException("Wrong class "+identifier.getClass().getName()+" for identifier!");
}
String key = ((Identifier)identifier).getIdentifier().trim();
if (key.length()==0) {
throw new IllegalArgumentException("The identifier can't be empty!");
}
// Look for the key in the properties in the body of the message
Object retVal = null;
for(int i = 0; i < message.entity.properties.length; ++i) {
if (message.entity.properties[i].property_name.trim().equals(key)) {
retVal = message.entity.properties[i].property_value;
break;
}
}
if (retVal==null) {
return null;
} else {
/*
* We have the value now we try to build an object of the
* right type before returning the value
* This is done trying to build a Numeric object with the Any
* object and returning it in case of success.
* If the conversion to a NumericValue failed then the object is
* converted to a string an compared to true and false
* (for booleans) or returned as String.
*/
// Integer
try {
NumericValue num = new NumericValue((Integer)retVal);
return num;
} catch (Exception e) {}
// Long
try {
NumericValue num = new NumericValue((Long)retVal);
return num;
} catch (Exception e) {}
// Float
try {
NumericValue num = new NumericValue((Float)retVal);
return num;
} catch (Exception e) {}
// Double
try {
NumericValue num = new NumericValue((Double)retVal);
return num;
} catch (Exception e) {}
// Boolean
String val = retVal.toString().trim();
if (val.compareToIgnoreCase("true")==0) {
return true;
}
if (val.compareToIgnoreCase("false")==0) {
return false;
}
// String
return val;
}
}
}
/**
* The message selector string
* It is an SQL92 string as described in JMS specification
* All the messages that do not match with this statement
* are discarded, the others are sent to the listeners
* If the selector is null or empty it matches to true with all messages.
*/
private String selectorString;
/**
* The selector object
* @see com.codestreet.selector.ISelector
*/
private ISelector selector = null;
/**
* The constructor
*
* @param sqlSelectorString The SQL selector string (it can be
* null or empty)
*
* @throws InvalidSelectorException
* @see com.codestreet.selector.parser.InvalidSelectorException
*/
public ACSJMSMessageSelector(String sqlSelectorString) throws InvalidSelectorException {
setSelectorString(sqlSelectorString);
}
/**
*
* @return The (eventually null or empty) SQL selector string
*
*/
public String getSelectorString() {
return selectorString;
}
/**
* Set the new SQL selector string and create the instance of
* selector. selector is set to null if the string is null or empty.
*
*
* @param newSQLSelString The SQL92 selector string
* @throws InvalidSelectorException
*
* @see com.codestreet.selector.parser.InvalidSelectorException
*/
public void setSelectorString(String newSQLSelString) throws InvalidSelectorException {
this.selectorString=newSQLSelString;
if (selectorString==null) {
selector = null;
} else if (selectorString.trim().length()==0) {
selector = null;
} else {
selector = Selector.getInstance(selectorString);
}
}
/**
* Check if the message matches with the SQL selector string.
* The test passes also if the string is empty or null.
*
* @param message The message to check
* @return true if the message matches with the string
* false otherwise
*/
public boolean match(ACSJMSMessage message) {
if (selectorString==null) {
return true;
} else if (selectorString.length()==0) {
return true;
} else {
ACSJMSValueProvider valueProvider = new ACSJMSValueProvider(message);
Result result = selector.eval(valueProvider,null);
return result==Result.RESULT_TRUE;
}
}
}