/// <summary> /// Generates a client function for the <paramref name="funcName"/>. Optional <paramref name="typeMapping"/> string /// may be used to specify data omission and reordering options. /// </summary> /// <returns>The generated client function.</returns> /// <param name="serviceName">Service that contains the wrapped function.</param> /// <param name="functionName">Name of the function that should be wrapped.</param> public virtual ClientFunction GenerateClientFunction(string serviceName, string functionName) { if (!SinTD.SINFONIServices.ContainsService(serviceName)) { throw new ServiceNotRegisteredException(serviceName); } var service = SinTD.SINFONIServices.GetService(serviceName); if (!service.ContainsServiceFunction(functionName)) { throw new ServiceNotRegisteredException(functionName); } return((ClientFunction) delegate(object[] parameters) { SINFONIService registeredService = SinTD.SINFONIServices.GetService(serviceName); ServiceFunctionDescription registeredServiceFunction = registeredService.GetServiceFunction(functionName); if (!registeredServiceFunction.CanBeCalledWithParameters(parameters)) { throw new ParameterMismatchException( "Could not call Service Function " + serviceName + "." + functionName + ". The provided parameters can not be mapped to the parameters specified in the IDL."); } object[] callParameters = new object[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { SinTDType expectedParameterType = registeredServiceFunction.Parameters.ElementAt(i).Value; callParameters[i] = expectedParameterType.AssignValuesFromObject(parameters[i]); } return CallClientFunction(serviceName + "." + functionName, callParameters); }); }
/// <summary> /// Creates a SinTDTyped object and stores it under the name specified in the IDL for the corresponding parameter. /// The created Parameter is added to the list of parameters for the currently parsed service function object. /// </summary> /// <param name="param">String defining name and type of the parameter</param> /// <param name="functionDescription">Service Function object to which the parameter should be added</param> private void createParameterForServiceFunction(string param, ServiceFunctionDescription functionDescription) { string[] values = splitDeclarationInNameAndType(param); SinTDType paramType = getSinTDType(values[0].Trim()); string paramName = values[1].Trim(); functionDescription.Parameters.Add(paramName, paramType); }
public void CallShouldBeValidForArrayParameters() { SinTDArray parameterArray = new SinTDArray(); parameterArray.elementType = i32; var serviceFunction = new ServiceFunctionDescription("arrayFunction", new SinTDType("void")); serviceFunction.Parameters.Add("arrayParam", parameterArray); service.serviceFunctions.Add("arrayFunction", serviceFunction); var clientFunction = connection.GenerateClientFunction("service", "arrayFunction"); Assert.DoesNotThrow(() => clientFunction(new int[] {1, 2, 3, 4})); }
public void CallShouldBeValidForMapParameters() { SinTDMap parameterMap = new SinTDMap(); parameterMap.elementType = i32; parameterMap.keyType = SinTD_string; var serviceFunction = new ServiceFunctionDescription("mapFunction", new SinTDType("void")); serviceFunction.Parameters.Add("mapParam", parameterMap); service.serviceFunctions.Add("mapFunction", serviceFunction); var clientFunction = connection.GenerateClientFunction("service", "mapFunction"); Assert.DoesNotThrow(() => clientFunction(new Dictionary<string, int> { {"first", 1}, {"second", 2} })); }
/// <summary> /// Parses parameters given for a service function in the IDL. /// </summary> /// <param name="parameterDefinition">String defining all parameters of a service funtion in the IDL</param> /// <param name="functionDescription">The ServiceFunction object for which the parameters are created</param> private void parseParameters(string parameterDefinition, ServiceFunctionDescription functionDescription) { if (parameterDefinition.Length == 0) { return; } // Both different parameters and key-and-value definitions of maps are separated by commata. // Replace the commata separating the parameters by another delimiter to avoid ambiguity and still // allow the MapParser to work correctly parameterDefinition = replaceMapKeyValueDelimiter(parameterDefinition); string[] parameters = parameterDefinition.Split(';'); foreach (string param in parameters) { createParameterForServiceFunction(param.Trim(), functionDescription); } }
/// <summary> /// Interprets a line of the IDL as service function definition and creates one of it from the entry /// </summary> /// <param name="line">Line as read from the IDL</param> /// <param name="lineNumber">Line number within IDL</param> private void parseServiceFunctionDefinition(string line, int lineNumber) { // Check syntax of the line. If the parameter list cannot be retrieved correctly from the // paranthese, or the definition is not finished by a semicolon, throw exception if (!(line.Contains(';') && line.Contains('(') && line.Contains(')'))) { throw new IDLParseException(line, lineNumber); } int indexOfOpenPar = line.IndexOf('('); int indexOfClosePar = line.IndexOf(')'); string parameterDefinition = line.Substring(indexOfOpenPar + 1, indexOfClosePar - (indexOfOpenPar + 1)); string nameAndType = line.Substring(0, indexOfOpenPar); ServiceFunctionDescription newServiceFunction = createTypedServiceFunction(nameAndType); parseParameters(parameterDefinition, newServiceFunction); currentlyParsedService.serviceFunctions.Add(newServiceFunction.Name, newServiceFunction); }
/// <summary> /// Is called when Connection receives a message that is identified as service call. Upon receiving a call, /// SINFONI will check whether the called service exists, what parameters it expects and which local function /// implements the service. If the service exists and the parameter types match, the local function called /// and a call-reply object with the result is sent back to the client /// </summary> /// <param name="callMessage">The deserialized message object that was received by the connection</param> private void HandleCall(IMessage callMessage) { int callID = callMessage.ID; string methodName = callMessage.MethodName; string[] serviceDescription = methodName.Split('.'); Delegate nativeMethod = null; lock (registeredFunctions) { if (registeredFunctions.ContainsKey(methodName)) { nativeMethod = registeredFunctions[methodName]; } } if (nativeMethod != null) { object[] parameters; try { var args = callMessage.Parameters; var callbacks = callMessage.Callbacks; var paramInfo = new List <ParameterInfo>(nativeMethod.Method.GetParameters()); parameters = ConvertParameters(methodName, args, callbacks, paramInfo); } catch (Exception e) { SendException(callID, e.Message); return; } if (!IsOneWay(methodName)) { object returnValue = null; object exception = null; bool success = true; try { // Super Evil Hack Here! Existing unit tests assume that WSJON serializes in a fixed format that // originates from serializing the native types correctly. Also, the tests do not take into account // any SinTD from any IDL. To make them work, we have to pretend that there is no ServiceRegistry // maintaining any service description, but bypass type check and automatic SinTD Conversion // by setting service Registry to null if (SinTD == null) { returnValue = nativeMethod.DynamicInvoke(parameters); } else { ServiceFunctionDescription service = SinTD.SINFONIServices .GetService(serviceDescription[0]) .GetServiceFunction(serviceDescription[1]); returnValue = service.ReturnType.AssignValuesFromObject(nativeMethod.DynamicInvoke(parameters)); } } catch (Exception e) { exception = e; success = false; } SendResponse(callID, nativeMethod, success, returnValue, exception); } else { nativeMethod.DynamicInvoke(parameters); } } else { SendException(callID, "Method " + methodName + " is not registered"); return; } }
/// <summary> /// Parses parameters given for a service function in the IDL. /// </summary> /// <param name="parameterDefinition">String defining all parameters of a service funtion in the IDL</param> /// <param name="functionDescription">The ServiceFunction object for which the parameters are created</param> private void parseParameters(string parameterDefinition, ServiceFunctionDescription functionDescription) { if (parameterDefinition.Length == 0) return; // Both different parameters and key-and-value definitions of maps are separated by commata. // Replace the commata separating the parameters by another delimiter to avoid ambiguity and still // allow the MapParser to work correctly parameterDefinition = replaceMapKeyValueDelimiter(parameterDefinition); string[] parameters = parameterDefinition.Split(';'); foreach (string param in parameters) { createParameterForServiceFunction(param.Trim(), functionDescription); } }
public void CallShouldBeValidForStructParameters() { var serviceFunction = new ServiceFunctionDescription("structFunction", new SinTDType("void")); serviceFunction.Parameters.Add("structParam", intStruct); service.serviceFunctions.Add("structFunction", serviceFunction); var clientFunction = connection.GenerateClientFunction("service", "structFunction"); Assert.DoesNotThrow(() => clientFunction(new testStruct { x = 1, y = 1 })); }
public void Setup() { SinTDInstance = new SinTD(); SinTDInstance.SINFONIServices = new ServiceRegistry(); i32 = SinTDInstance.GetSinTDType("i32"); SinTD_string = SinTDInstance.GetSinTDType("string"); intStruct = new SinTDStruct("intStruct"); intStruct.members["x"] = i32; intStruct.members["y"] = i32; serviceFunction = new ServiceFunctionDescription("function", new SinTDType("void")); serviceFunction.Parameters.Add("intParameter", i32); serviceFunction.Parameters.Add("stringParameter", SinTD_string); service = new SINFONIService("service"); service.serviceFunctions.Add("function", serviceFunction); SinTDInstance.SINFONIServices.services.Add("service", service); connection = new TestConnection(); connection.SinTD = SinTDInstance; }