Documentation under construction
The SMS module has been developed for the Cyclos software, as explained in the page Architecture. It is however possible to create 'custom commands' (operations). There can be two main reasons who you would want to create custom commands:
This page will describe in detail how to create custom commands.
Note: In order to develop custom operations you will need solid knowledge of SMS messaging, Java, Cyclos, and the SMS module.
The code used with the examples can be downloaded at the section: Example code.
The example command permits retrieving information from other members registered in Cyclos. In the example you can search for a profile field of a specific member. This is just as example and might not be of use for live systems.
The format of the command consists of four words divided by spaces:
When the Driver will receive a message from the number 098654321 with message text Minfo 7410 099123456 the following will happen:
In oder to create a custom command the following steps need to be done:
The error handling can be implemented with the following classes:
TODO En el ambiente de desarrollo necesitará las siguientes librerias (todas ellas disponibles en una distribucion del módulo AIO):
controller-driver-xx.xx.xx.jarcontroller-xx.xx.xx.jarcyclos_3.6_client.jar (Web Service interfaces for Cyclos)The command class will need to extend the class nl.strohalm.cyclos.controller.command.Command which is available in the library controller-xx.xx.xx.jar.
Note: The command object is a SINGLETON within a the scope of the cyclosInstance ( see controller context configuration). Because of the fact that the class is singleton it cannot contain any variables that can change their value when receiving method calls (with the exception of the SET methods that can be used during the initialization of the controller, (see also controller context configuration).
If you want to send SMS messages (notifications or responses) from a command class please refer to the section Send SMS messages. Make sure the methods of the command class are implemented correctly (see next section)
executeCommandprotected void executeCommand(String[] commandParameters, DriverMessage driverMessage, CyclosInstanceConfig cyclosInstance)
This method will be called by the Controller when a message is received which first word(s) match with one of the aliases configured in the configuration of the custom command. The method will receive the following parameters:
commandParameters - The words (aliases) received that define the command, (e.g. 'pay').driverMessage - All data concerning the received message (message text, origin and destination number, Driver identification, indentificador de traza??, etc.)cyclosInstance - The configuration of the cyclos instance that processes the command (permitting access to internationalization keys, Web Service clients etc.).getRequiredI18NKeysprotected String[] getRequiredI18NKeys()
This method will return the internationalization keys that are related to the operation command. For the implementataiton the following points are important:
command.<command_name>.regularExpression, command.memberNotRegistered, and error.template are used for all commands and therefore it is not necessary to specify them in this list.command.<command_name>.help.withConf, and command.<command_name>.help.withoutConf.command.<command_name>.confirmation.getErrorCodespublic Set<Integer> getErrorCodes(CodeSupportedError error) -
When an CodeSupportedError is generated it will return all possible error codes that were generated by it. It will return one of the error codes defined in Error codes enum (TODO:change link) when it receives the value CodeSupportedError.CUSTOM_COMMAND_ERROR as parameter.
getHelpParameterspublic String[] getHelpParameters(CyclosInstanceConfig cyclosInstance)
Returns a list of parameters which can be used to substitute in the internationalization the keys command.<command_name>.help.withConf, y command.<command_name>.help.withoutConf (for more details please see Operations translation keys). Generally, the first element of the returned list is the shortest alias to the command.
isUsePinpublic boolean isUsePin().Defines if the command requires a PIN. PIN is enabled by default. The following points need to be considered.
getRequiredConfigParametersprotected CommandConfigParameter[] getRequiredConfigParameters()
Allows to define extra parameters required for the execution of a command. In case the extra parameters, or configured parameters in Config.xml file are missing, the SMS module will not be initialized, indicating the missing parameter as error cause.
package nl.strohalm.cyclos.controller.command.minfo; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import nl.strohalm.cyclos.controller.command.Command; import nl.strohalm.cyclos.controller.command.CommandConfigParameter; import nl.strohalm.cyclos.controller.config.CyclosInstanceConfig; import nl.strohalm.cyclos.controller.exception.CodeSupportedError; import nl.strohalm.cyclos.controller.exception.CommandException; import nl.strohalm.cyclos.controller.exception.NoCodedError; import nl.strohalm.cyclos.controller.validation.ParamValidator; import nl.strohalm.cyclos.driver.DriverMessage; import nl.strohalm.cyclos.webservices.access.CheckCredentialsParameters; import nl.strohalm.cyclos.webservices.access.CredentialsStatus; import nl.strohalm.cyclos.webservices.members.MemberResultPage; import nl.strohalm.cyclos.webservices.members.MemberSearchParameters; import nl.strohalm.cyclos.webservices.model.FieldValueVO; import nl.strohalm.cyclos.webservices.model.MemberVO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Text message syntax: COMMAND pin principal [customField]
* This command allows retrieve information related to a member's custom field */ public class MemberInfoCommand extends Command { private static final Log logger = LogFactory.getLog(MemberInfoCommand.class); static final String SMS_TYPE = "MEMBER_INFO"; static { logger.info("****** MEMBER INFO SAMPLE CUSTOM COMMAND. VERSION: 1.0 ******"); } /** * @return the code assigned to a (supported) built-in error. */ static Integer getErrorCode(final CodeSupportedError error) { switch (error) { case INVALID_PARAMETERS_COUNT: return 901; case UNKNOWN_COMMAND_ERROR: return 902; case ORIGINATE_MEMBER_BLOCKED_CREDENTIALS: return 903; case ORIGINATE_MEMBER_INVALID_CREDENTIALS: return 904; default: return null; } } /** * All returned codes must exist as error.<ERROR_TYPE>.code key in
* errors__<language>_<country>_<variant>.properties file * @see nl.strohalm.cyclos.controller.exception.IErrorCodeDescriptor#getErrorCodes(nl.strohalm.cyclos.controller.exception.CodeSupportedError) */ @Override public Set<Integer> getErrorCodes(final CodeSupportedError error) { if (CodeSupportedError.CUSTOM_COMMAND_ERROR == error) { final Set<Integer> codes = new HashSet<Integer>(); for (final MemberInfoErrorCode customCode : MemberInfoErrorCode.values()) { codes.add(customCode.code()); } return codes; } else { final Integer code = getErrorCode(error); if (code != null) { return Collections.singleton(code); } else { return Collections.emptySet(); } } } @Override public String[] getHelpParameters(final CyclosInstanceConfig cyclosInstance) { return new String[] { getShortestAlias(cyclosInstance) }; } /** * Command implementation * @see nl.strohalm.cyclos.controller.command.Command#executeCommand(java.lang.String[], nl.strohalm.cyclos.driver.DriverMessage, * nl.strohalm.cyclos.controller.config.CyclosInstanceConfig) */ @Override protected void executeCommand(final String[] params, final DriverMessage message, final CyclosInstanceConfig cyclosInstance) { final ParamValidator<MemberInfoParameter> validator = validate(params, message, cyclosInstance); final String pin = validator.getValue(MemberInfoParameter.PIN); final String principal = validator.getValue(MemberInfoParameter.PRINCIPAL); final String customFieldName = validator.getValue(MemberInfoParameter.CUSTOM_FIELD_NAME); checkMemberCredential(cyclosInstance, message.getMessage().getFrom(), pin); final String customFieldValue = getCustomFieldValue(principal, customFieldName, cyclosInstance); final String[] msgTextParam = new String[] { principal, customFieldName, customFieldValue }; facade.asyncNotifyToMember(message.getMessage().getFrom(), message.getMessage().getTraceData(), cyclosInstance, SMS_TYPE, "command." + name + ".response", msgTextParam); } @Override protected CommandConfigParameter[] getRequiredConfigParameters() { return MemberInfoConfigParameter.values(); } /** * The specified key must exist in cyclosInstance_<language>_<country>_<variant>.properties file * @see nl.strohalm.cyclos.controller.command.Command#getRequiredI18NKeys() */ @Override protected String[] getRequiredI18NKeys() { return new String[] { "command." + name + ".help.withConf", // response to help message if you are using requestConfirmation "command." + name + ".help.withoutConf", // response to help message if you are not using requestConfirmation "command." + name + ".response", // message to response this command "command." + name + ".confirmation" // message to response this command }; } /** * Check if given credentials are valid for CyclosInstance Member with phoneCustonFields equals originatorPhone.
* if not, then exit trough CommandException or IllegalStateException * @throws CommandException, IllegalStateException */ private void checkMemberCredential(final CyclosInstanceConfig cyclosInstance, final String originatorPhone, final String credentials) { CodeSupportedError errorType; final String searchByPrincipalType = cyclosInstance.getMemberSettings().getPhoneCustomField(); final CheckCredentialsParameters params = new CheckCredentialsParameters(); params.setPrincipalType(searchByPrincipalType); params.setPrincipal(originatorPhone); params.setCredentials(credentials); final CredentialsStatus status = cyclosWsManager.checkCredentials(cyclosInstance.getName(), params); switch (status) { case VALID: // ok break; case BLOCKED: errorType = CodeSupportedError.ORIGINATE_MEMBER_BLOCKED_CREDENTIALS; throw new CommandException(errorType, getErrorCode(errorType), String.format("The credentials are BLOCKED for member (originatePhone: %1$s).", originatorPhone)); case INVALID: // check if member is registered (is not good idea give this detail, but we are making a command example) final MemberSearchParameters mSearchParam = new MemberSearchParameters(); final List<FieldValueVO> searchFields = new ArrayList<FieldValueVO>(); searchFields.add(new FieldValueVO(searchByPrincipalType, originatorPhone)); mSearchParam.setFields(searchFields); final MemberResultPage searchResult = cyclosWsManager.searchMember(cyclosInstance.getName(), mSearchParam); if (searchResult.getTotalCount() == 0) { throw new CommandException(NoCodedError.ORIGINATE_MEMBER_NOT_FOUND, String.format("Member (originatePhone: %1$s) not found.", originatorPhone)); } else { // the member exist then the problem is the credential errorType = CodeSupportedError.ORIGINATE_MEMBER_INVALID_CREDENTIALS; throw new CommandException(errorType, getErrorCode(errorType), String.format("The credentials are INVALID for member (originatePhone: %1$s).", originatorPhone)); } default: throw new IllegalStateException(String.format("Unsupported credentials status result: %1$s", status)); } } /** * @return the customFieldToReturn value for member with principal * @throws MemberInfoException */ private String getCustomFieldValue(final String principal, final String customFieldName, final CyclosInstanceConfig cyclosInstance) { final String msgParamPrincipalType = cyclosInstance.getMemberSettings().getMsgParamPrincipalType(); final MemberSearchParameters memberSearchParam = new MemberSearchParameters(); memberSearchParam.setShowCustomFields(true); final FieldValueVO fieldValue = new FieldValueVO(msgParamPrincipalType, principal); memberSearchParam.setFields(Collections.singletonList(fieldValue)); final MemberResultPage result = cyclosWsManager.searchMember(cyclosInstance.getName(), memberSearchParam); if (CollectionUtils.isNotEmpty(result.getMembers())) { final MemberVO member = result.getMembers().get(0); final FieldValueVO clientCustomFieldValue = (FieldValueVO) CollectionUtils.find(member.getFields(), new Predicate() { @Override public boolean evaluate(final Object arg0) { final FieldValueVO fieldValue = (FieldValueVO) arg0; return fieldValue.getField().equals(customFieldName); } }); if (clientCustomFieldValue == null) { final String exceptionMessage = "Member was found but it doesn't have a custom field with name: " + customFieldName + " or the custom field value is empty. Member principal: " + principal; throw new MemberInfoException(MemberInfoErrorCode.CUSTOM_FIELD_NOT_FOUND, exceptionMessage); } return clientCustomFieldValue.getValue(); } else { // it does not find a member final String exceptionMessage = "Can not find a member for " + msgParamPrincipalType + ": " + principal; throw new MemberInfoException(MemberInfoErrorCode.MEMBER_NOT_FOUND, exceptionMessage); } } private ParamValidator<MemberInfoParameter> validate(final String[] params, final DriverMessage message, final CyclosInstanceConfig cyclosInstance) { final String defaultCFName = getConfig(cyclosInstance.getName()).getValue(MemberInfoConfigParameter.DEFAULT_CUSTOM_FIELD_NAME); final ParamValidator<MemberInfoParameter> validator = ParamValidator.newInstance("memberInfo"); validator.param(MemberInfoParameter.PIN).required(); validator.param(MemberInfoParameter.PRINCIPAL).required(); validator.param(MemberInfoParameter.CUSTOM_FIELD_NAME).defaultValue(defaultCFName); validator.setErrorHandler(MemberInfoValidationHandler.instance(), true); validator.validate(params); infoExecutingCommandParams(logger, cyclosInstance.getName(), message, validator, MemberInfoParameter.PIN); return validator; } }
SMS_TYPE constantThis constant defines the type of error code. See getErrorSmsTypeCode.
MemberInfoCommand.executeCommand methodDuring the execution process of the example command the following steps will be performed.
validate();checkMemberCredential()memberCustomFieldValue()facade.asyncNotifyToMember()MemberInfoCommand.getRequiredI18NKeys methodFor the MemberInfo operations the following keys are required in the file: cyclosInstance_en_US_MINFO.properties:
- command.mInfo.regularExpression
- command.mInfo.help.withConf
- command.mInfo.help.withoutConf
- command.mInfo.response
- command.mInfo.confirmation
The key command.mInfo.regularExpression is a regular expression that identies the message text that needs to be processed by the custom command. The key command.<commandName>.regularExpression is requird by the controller for all the commands that are defined within the controller configuratin.
The keys command.mInfo.help.withConf, command.mInfo.help.withoutConf, command.mInfo.response, and command.mInfo.confirmation are specific for the custom command, as well as the key .regularExpression) the need to be defined in the internationalization files.
If during the initialization process some keys are missing in the file cyclosInstance_en_US_MINFO.properties the SMS module will not start and generate an error indicating what key is missing.
Every command has an (obligatory) error handling class that extends the class nl.strohalm.cyclos.controller.command.CommandErrorHandler. The error handling class will handle errors (normally sending notifications) when exceptions TODO del tipo CommandException ocurran en la ejecución del comando personalizado.
Note: The command object is a SINGLETON within a the scope of the cyclosInstance ( see controller context configuration). Because of the fact that the class is singleton it cannot contain any variables that can change their value when receiving method calls (with the exception of the SET methods that can be used during the initialization of the controller, (see also controller context configuration).
There are two types of errorHandler exceptions:
protected void handleException(final CommandException e, final CyclosInstanceConfig cyclosInstance)
This method will be called every time the associated command generates an exception of the type CommandException (and not the standard exceptions that are handled by the handleException(CommandException) class). The doHandleException method will be responsible to take necessary error handling actions. Generally this action will be to send a notification to the user indicating that the operation could not be processed and informing an error code. But the method could take other actions like a call to an external application. When sending a notification the text will be retrieved from an internationalization key, see Error translation keys.
When you generate a Custom exception class a CodeSupportedError.CUSTOM_COMMAND_ERROR (enum) and an error code needs to be passed. Those arguments will permit to use internationalization (see Error translation keys) for the return notifications. The internationalization keys will be of the type * error.CUSTOM_COMMAND_ERROR.<codeNumber> (see example).
public void handleException(final CommandException e, final CyclosInstanceConfig cyclosInstance)
This method is called before the doHandleException is called, it only handles exceptions of the type NoCodedError.ORIGINATE_MEMBER_NOT_FOUND or NoCodedError.ORIGINATE_MEMBER_INVALID_CHANNEL. These errors do not need to generate a response message and are just used for logging.
If you want to have total control when a CommandException is generated you can overwrite it (in stead of implementing the doHandleException).
public void handleException(final CommandParameterException e, final CyclosInstanceConfig cyclosInstance)
This method has a similar behavior as handleException(CommandException) but related to parameters exceptions (class CommandParameterException).
The exception CommandParameterException extends the class CommandException too, and is launched when a command generates a validation error related to wrong/missing parameters. This exception will provide information about the position of the parameter that caused the exception.
protected String getErrorSmsTypeCode()
This method returns the message code type (see New SmsType).
It is possible to send messages from a CommandErrorHandler with the following methods:
The same methods can be used to send response message from a Command class.
Note: If a message is sent directly via Driver it will not be logged in Cyclos. This means that it won't be possible to charge a member for these messages.
package nl.strohalm.cyclos.controller.command.minfo;
import nl.strohalm.cyclos.controller.command.CommandErrorHandler;
import nl.strohalm.cyclos.controller.config.CyclosInstanceConfig;
import nl.strohalm.cyclos.controller.exception.CommandException;
public class MemberInfoErrorHandler extends CommandErrorHandler {
/**
* Custom the handler for CodeSupportedError.CUSTOM_COMMAND_ERROR
* @see nl.strohalm.cyclos.controller.command.CommandErrorHandler#doHandleException(nl.strohalm.cyclos.controller.exception.CommandException,
* nl.strohalm.cyclos.controller.config.CyclosInstanceConfig)
*/
@Override
protected void doHandleException(final CommandException e, final CyclosInstanceConfig cyclosInstance) {
// customize the way to process a CUSTOM_COMMAND_ERROR type
if (e.getErrorCode() == MemberInfoErrorCode.MEMBER_NOT_FOUND.code()) {
// the member principal value (given in text message)
facade.notifyErrorToMember(e.getDriverMessage(), cyclosInstance, getErrorSmsTypeCode(), e.getError(), e.getErrorCode(), new String[] { e.getCommandParameters()[1] });
} else if (e.getErrorCode() == MemberInfoErrorCode.CUSTOM_FIELD_NOT_FOUND.code()) {
String msgParam = null;
if (e.getCommandParameters().length == 3) {
// the principal customField (was given in text message)
msgParam = e.getCommandParameters()[2];
} else {
// using principal customField with default value (config.xml)
msgParam = e.getCommand().getConfig(cyclosInstance.getName()).getValue(MemberInfoConfigParameter.DEFAULT_CUSTOM_FIELD_NAME);
}
facade.notifyErrorToMember(e.getDriverMessage(), cyclosInstance, getErrorSmsTypeCode(), e.getError(), e.getErrorCode(), new String[] { msgParam });
} else {
super.doHandleException(e, cyclosInstance);
}
}
@Override
protected String getErrorSmsTypeCode() {
return MemberInfoCommand.SMS_TYPE + "_ERROR";
}
}
The method doHandleException is overwritten and receives a Custom exception class and a MemberInfoException. The implementation class contains the error handling code of the custom command (see Error codes, and doHandleException):
A custom exception class will need to extend a CommandException. The custom class is responsible to pass the necessary information to the Error handling class. Besides the data that is already available in a CommandException, the custom exception class can included additional information related to the custom command.
Package:nl.strohalm.cyclos.controller.exception
The command class exception passes information about the error during the execution of the command (from the point where the command is launched to the error handling that processes it):
package nl.strohalm.cyclos.controller.command.minfo;
import nl.strohalm.cyclos.controller.exception.CodeSupportedError;
import nl.strohalm.cyclos.controller.exception.CommandException;
/**
* Base command exception for Member info custom command errors
*/
public class MemberInfoException extends CommandException {
private static final long serialVersionUID = 1L;
private MemberInfoErrorCode errorCode;
public MemberInfoException(final MemberInfoErrorCode errorCode) {
this(errorCode, null, null);
}
public MemberInfoException(final MemberInfoErrorCode errorCode, final String detailedMessage) {
this(errorCode, detailedMessage, null);
}
public MemberInfoException(final MemberInfoErrorCode errorCode, final String detailedMessage, final Throwable cause) {
super(CodeSupportedError.CUSTOM_COMMAND_ERROR, errorCode.code(), detailedMessage, cause);
this.errorCode = errorCode;
}
public MemberInfoException(final MemberInfoErrorCode errorCode, final Throwable cause) {
this(errorCode, null, cause);
}
public MemberInfoErrorCode getCustomCommandErrorCode() {
return errorCode;
}
}
Generally, the new personalized command requires the handling of specific error codes (which are not contained in the class nl.strohalm.cyclos.controller.exception.CodeSupportedError) in a particular form in the Error handler.
An exception which can be triggered from a personalized command is CodeSupportedError.CUSTOM_COMMAND_ERROR, but as many error codes as needed can be created. Make sure to use enumerates. An enumeration of error codes is a pragmatic way of keeping error codes mapped to an enumerated constant.
MemberInfoErrorCodepackage nl.strohalm.cyclos.controller.command.minfo;
/** * The followings are the codes assigned to the CodeSupportedError.CUSTOM_COMMAND_ERROR
* The intent of codifying the custom command error is to differentiate each internal error
* and allow the handler work properly. This command doesn't use the codes in the same way as the built-in commands does. * */ public enum MemberInfoErrorCode { CUSTOM_FIELD_NOT_FOUND(910), // The member does not have a custom field with specified name (or default name) MEMBER_NOT_FOUND(911); // Can not find (on Cyclos) a member with msgPrincipalType value received (in text message) public static MemberInfoErrorCode error(final Integer code) { if (code == null) { return null; } for (final MemberInfoErrorCode error : values()) { if (error.code == code) { return error; } } throw new IllegalArgumentException("Can't retrieve CustomCommandErrorCode. Unknown integer code: " + code); } private int code; private MemberInfoErrorCode(final int code) { this.code = code; } public int code() { return code; } }
The following steps need to be followed to in order to run a custom command in the module AIO:
controllerContext.xmlA configuration is required to be supplied to Spring, for the instantiation of objects for the personalized operation (command and error handler). Spring needs to instantiate the bean correspoding to the proper personalized command, its configuration would be similar to the following:
<bean id="<IdCommandBean>" class="<path to command class>">
<property name="facade" ref="facade"/>
<property name="langManager" ref="langManager"/>
<property name="sessionHandler" ref="sessionHandler"/>
<property name="name" value="<commandID>"/>
<property name="errorHandler" ref="<IdErrorHandlerBean>"/>
<property name="logUtils" ref="logUtils"/>
</bean>
The value of <commandID> is quite important, as this is the ID referenced for the configuration of the command in the config.xml (see Cyclos instances->commands->name)
Also, it is important to instantiate a bean for the error handling of the personalized command, its configuration would be similar to the following:
<bean id="<IdErrorHandlerBean>" class="<path to Error handler class>">
<property name="facade" ref="facade"/>
<property name="command" ref="<IdCommandBean>"/>
<property name="logUtils" ref="logUtils"/>
</bean>
controllerContext.xml in the example<?xml version="1.0" encoding="UTF-8"?> <beans default-autowire="byName" xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean name="messageProcessErrorHandler" class="nl.strohalm.cyclos.controller.facade.MessageProcessErrorHandlerImpl"> <property name="facade" ref="facade"/> <property name="controllerConfig" ref="controllerConfig"/> <property name="sessionHandler" ref="sessionHandler"/> <property name="langManager" ref="langManager"/> </bean> ' <bean id="mInfo" class="nl.strohalm.cyclos.controller.command.minfo.MemberInfoCommand"> <property name="facade" ref="facade"/> <property name="langManager" ref="langManager"/> <property name="sessionHandler" ref="sessionHandler"/> <property name="name" value="mInfo"/> <property name="errorHandler" ref="mInfoErrorHandler"/> <property name="logUtils" ref="logUtils"/> </bean> <bean id="mInfoErrorHandler" class="nl.strohalm.cyclos.controller.command.minfo.MemberInfoErrorHandler"> <property name="facade" ref="facade"/> <property name="command" ref="mInfo"/> <property name="logUtils" ref="logUtils"/> </bean> </beans>
config.xmlThe controller configuration file WebContent/WEB-INF/classes/config.xml has been modified to support the new customized command. Two little sections need to be considered:
variant for the element cyclosInstance - This change allows to add the customizations of the command needed in the file Webcontent/WEB-INF/classes/i18n/cyclosInstance_en_US_MINFO without having to change the English internationalization file included in the original distribution.<command ...> has been added - This is the configuration of the customized command with its attribute, please observe the parameter defaultCustomFieldName, being its value the personalized field which will be used by default.config.xml in the example<?xml version="1.0" encoding="UTF-8"?> <controller language="es" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd"> <cyclosInstances> <cyclosInstance name="cyclos" language="en" country="US" variant="MINFO"> <currencies> <currency symbol="units" default="true"> <alias name="U"/> <alias name="Un"/> <alias name="Unit"/> <alias name="Units"/> </currency> </currencies> <commands> <command name="confirm" requestConfirmation="false"/> <command name="requestPayment" requestConfirmation="false"/> <command name="accountDetails" requestConfirmation="false"> <parameter name="nameLen" value="0"/> <parameter name="pageSize" value="3"/> </command> <command name="performPayment" requestConfirmation="false"/> <command name="help" requestConfirmation="false"/> <command name="registration" requestConfirmation="false"> <parameter name="notifyErrorByDriver" value="false"/> <parameter name="useLoginName" value="true"/> <parameter name="defaultInitialGroup" value="new"/> <parameter name="groupPrefix" value="."/> <paramGroup name="groupAliases"> <parameter name="full" value="5"/> <parameter name="new" value="6"/> </paramGroup> </command> <command name="infoText" requestConfirmation="false"/> <command name="mInfo" requestConfirmation="false"> <parameter name="defaultCustomFieldName" value="address" /> </command> </commands> <driverRouting> <route fromProvider="*" toDriver="aioDriver" usedFromNumber="9999" default="true"/> </driverRouting> <connectionSettings rootUrl="http://localhost:8080/cyclos" disableCNCheck="true" connectionTimeout="120000" readTimeout="120000" trustAllCert="true"/> <memberSettings phoneCustomField="mobilePhone" msgParamPrincipalType="mobilePhone" providerCustomField="provider" notifyNotRegisteredUser="false"> <principalSettings regexp="^(\d){7,9}$"/> </memberSettings> </cyclosInstance> </cyclosInstances> <driverInstances> <driverInstance name="aioDriver"> <cyclosRouting> <route fromTargetNumber="9999" toCyclos="cyclos"/> </cyclosRouting> <connectionSettings connectionTimeout="120000" readTimeout="120000" disableCNCheck="true" trustAllCert="true"/> </driverInstance> </driverInstances> <globalSettings responseInvalidMessages="false"/> <databaseSettings username="root" password=""> <url>jdbc:mysql://localhost/cyclos3_sms_aio</url> <driverClassName>com.mysql.jdbc.Driver</driverClassName> <querydslTemplatesClassName>com.mysema.query.sql.MySQLTemplates</querydslTemplatesClassName> </databaseSettings> <sessionSettings removeExpiredDelayInSeconds="120"> <control timeoutInSeconds="300" maxWrongTries="10"/> <confirmation timeoutInSeconds="300" maxWrongTries="3" maxWrongPinTries="4" useKeyFromDictionaryFirst="true" keyLength="4" pendingsByCommand="3"/> </sessionSettings> </controller>
cyclosInstance.propertiesThe message texts (or components of the message texts) the command will be used are internationalized through the cyclosInstance.properties file (see Internationalization files).
The translation keys need to be defined (as a String Array) in the method Command.getRequiredI18NKeys (ver #cyclosInstance_en_US_MINFO.properties_in_the_example).
cyclosInstance_en_US_MINFO.properties in the exampleIn our example, the following keys are internationalized:
command.mInfo.regularExpression MemberInfocommand.getRequiredI18NKeys()
- command.minfo.response
- command.mInfo.confirmation
- command.mInfo.help.withConf
command.mInfo.help.withoutConfcommand.help.helpcommand.help.help=For information about SMS operations. Send a message with the word help followed by one of the following operation commands\: acinfo, minfo, pay, rq, reg, info.
command.mInfo.regularExpression=(\\s*)(minfo)(\\s+.*)*$
command.mInfo.response={1} for {0}: {2}
command.mInfo.help.withoutConf=Member info command. Send a message with\: {0} pin principal [field_name]
command.mInfo.help.withConf=Member info command. Send a message with\: {0} principal [field_name]
command.mInfo.confirmation=Please confirm request for member info\: {0}
error.propertiesThe error texts (or components of error texts) the command is issuing are to be internationalized through the errors.properties file (see Internationalization files).
In this file(s), all keys with codes which can be triggered during the execution of the customized commands need to be specified.
errors_en_US_MINFO.properties in the exampleOn top of the specific error codes triggered with the MINFO command, also the key error.COMMAND_NOT_FOUND has been customized in the case that the configuration variation notifyNotRegisteredUser="true" has been set (see Cyclos instances->memberSettings->notifyNotRegisteredUser).
error.COMMAND_NOT_FOUND=Invalid command. Send a message with the word help followed by one of the following\: acinfo, minfo, pay, rq, reg, info.
error.CUSTOM_COMMAND_ERROR.910=Can not find the custom field {0}, or field value is empty. Member info command failed
error.CUSTOM_COMMAND_ERROR.911=Can not find the member for principal {0}. Member info command failed
error.INVALID_PARAMETERS_COUNT.901=Invalid message format. Member info command failed.
error.ORIGINATE_MEMBER_BLOCKED_CREDENTIALS.903=Your password is blocked. Member info command failed.
error.ORIGINATE_MEMBER_INVALID_CREDENTIALS.904=Invalid password. Member info command failed.
error.UNKNOWN_COMMAND_ERROR.902=Unknown error processing message. Member info command failed.
To allow cyclos to classify the MT messages the system sends (to a mobile phone), in cyclos the following configurations need to be done:
sms_types table of the cyclos database the entries reflecting the text message type which will be sent by the system (of course in relationship to the personalized command).sms.type.<CustomCommandSmsType>.description
sms.type.<CustomCommandSmsType>_ERROR.descriptioncyclos3.sms_types tableTo support our example, the following instructions have been issued on the cyclos database (the CustomCommandSmsType=MEMBER_INFO was defined in MemberInfoCommand class):
- insert into sms_types set id="13", code="MEMBER_INFO", order_index="12"
- insert into sms_types set id="14", code="MEMBER_INFO_ERROR", order_index="13"
Also, at cyclos->adminMenu->Translation->Aplication the following keys have been defined: