Why don't change lifecycle state of mule runtime? - web-services

I want to save some informaion to database when I send a request to mule service.
for this issue: I wrote below config in xml file:
<mule xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:mule-ss="http://www.mulesoft.org/schema/mule/spring-security"
xmlns:ss="http://www.springframework.org/schema/security" xmlns:jdbc="http://www.mulesoft.org/schema/mule/jdbc"
xmlns:spring="http://www.springframework.org/schema/beans" version="CE-3.3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/spring-security http://www.mulesoft.org/schema/mule/spring-security/3.3/mule-spring-security.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.mulesoft.org/schema/mule/jdbc http://www.mulesoft.org/schema/mule/jdbc/current/mule-jdbc.xsd ">
<spring:beans>
<spring:bean id="Initializer" name="Initializer" class="org.mule.example.scripting.IpClient" doc:name="Bean"/>
</spring:beans>
<notifications>
<notification event="COMPONENT-MESSAGE"/>
<notification-listener ref="Initializer"/>
</notifications>
<configuration doc:name="Configuration">
<expression-language>
<global-functions>
def parseIp(fullIp) {
return
fullIp.substring(fullIp.indexOf('/') + 1, fullIp.indexOf(':'))
}
</global-functions>
</expression-language>
</configuration>
<http:connector name="httpConnector" doc:name="HTTP\HTTPS">
<service-overrides sessionHandler="org.mule.session.NullSessionHandler" />
</http:connector>
<mule-ss:security-manager>
<mule-ss:delegate-security-provider
name="memory-dao" delegate-ref="authenticationManager" />
</mule-ss:security-manager>
<spring:beans>
<ss:authentication-manager alias="authenticationManager">
<ss:authentication-provider>
<ss:user-service id="userService">
<ss:user name="weather" password="weather" authorities="ROLE_ADMIN" />
</ss:user-service>
</ss:authentication-provider>
</ss:authentication-manager>
</spring:beans>
<flow name="Serive_test" doc:name="Serive_test">
<http:inbound-endpoint host="localhost" port="8089"
path="service/local-weather" exchange-pattern="request-response"
doc:name="HTTP">
<mule-ss:http-security-filter realm="mule-realm" />
</http:inbound-endpoint>
<async doc:name="Async">
<set-variable variableName="remoteClientAddress"
value="#[parseIp(message.inboundProperties['MULE_REMOTE_CLIENT_ADDRESS'])]"
doc:name="Variable" />
<message-properties-transformer
doc:name="myproperty" scope="session">
<add-message-property key="message.payload.remoteClientAddress"
value="#[parseIp(message.inboundProperties['MULE_REMOTE_CLIENT_ADDRESS'])]" />
</message-properties-transformer>
<component doc:name="classTest" class="org.mule.example.scripting.IpClient" />
</async>
<cxf:proxy-service service="Weather" doc:name="Weather_webservice"
wsdlLocation="http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl" namespace="http://ws.cdyne.com/WeatherWS/"
payload="envelope"></cxf:proxy-service>
<copy-properties propertyName="SOAPAction" doc:name="Property"></copy-properties>
<cxf:proxy-client doc:name="Weather_webservice"
payload="envelope" />
<outbound-endpoint address="http://wsf.cdyne.com/WeatherWS/Weather.asmx"
exchange-pattern="request-response" doc:name="HTTP"></outbound-endpoint>
</flow>
and IpClient class:
public class IpClient implements Callable,ModelNotificationListener<ModelNotification> {
#Override
public void onNotification(ModelNotification notification) {
// TODO Auto-generated method stub
System.out.println("Notification order event: " + notification.getActionName() );
if(notification.getAction() == ModelNotification.MODEL_DISPOSED || notification.getAction() == ModelNotification.MODEL_STOPPED){
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
ConnectDB c = new ConnectDB("localhost:3306", "accounting", "root", "");
String sql = " INSERT INTO weather (ip, date, CurrentState) VALUES (?,?,?) ";
PreparedStatement ps = null;
try {
ps = c.getConnnection().prepareStatement(sql);
ps.setString(1, "127.0.0.1");
ps.setString(2, dateFormat.format(date).toString());
ps.setString(3, notification.getActionName());
ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
c.close();
}
}
}
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
MuleMessage msg = eventContext.getMessage();
String remClient = msg.getProperty("remoteClientAddress", PropertyScope.INVOCATION);
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
ConnectDB c = new ConnectDB("localhost:3306", "accounting", "root", "");
String sql = " INSERT INTO weather (ip, date, CurrentState) VALUES (?,?,?) ";
PreparedStatement ps = c.getConnnection().prepareStatement(sql);
ps.setString(1, remClient);
ps.setString(2, dateFormat.format(date).toString());
ps.setString(3, msg.getMuleContext().getLifecycleManager().getCurrentPhase());
ps.executeUpdate();
c.close();
return msg.getPayload();
}
}
this program just works correctly if I start to run service. for example my service run before suddenly it was dissposed(for example my wsdl(http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl doesn't work)). my log program saved all records in state of start and after dissposing it works similar. if I stoped my service and run it again it works correctly and it saves all records in dissposed mode.
I don't know how to change my program that it saves correctly in runtime.

The (ill named) Initializer bean is not used as a <component> anywhere in the configuration so there's no way its onCall method will ever get called.
If your intention with:
<notification event="COMPONENT-MESSAGE"/>
is to have the Initializer called for each component invocation you then have to make it implement ComponentMessageNotificationListener (like you made it implement ModelNotificationListener).

Related

Spring Trace Id and Span ID in GCP Cloud Run request log

I am using spring cloud GCP logging and trace in my GCP cloud run application
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<include resource="org/springframework/cloud/gcp/logging/logback-appender.xml"/>
<root level="INFO">
<appender-ref ref="STACKDRIVER"/>
</root>
</configuration>
After adding this I can see that logs are grouped
Now I have two questions
Can we have the trace ID and and span ID printed in the request log it self.
After adding stack driver, the spring default console pattern is not shown. All the logs are printed in raw format. They do not have thread-name, span id, appName etc.
Can these two be achieved?
Update
Trying to enhance the log
#Component
public class NukTraceIdLoggingEnhancer extends TraceIdLoggingEnhancer {
#Override
public void enhanceLogEntry(LogEntry.Builder builder) {
super.enhanceLogEntry(builder);
LogEntry.Builder newDummyBuilder = builder;
LogEntry logEntry = newDummyBuilder.build();
Payload<?> payload = logEntry.getPayload();
if (logEntry.getPayload() instanceof JsonPayload) {
JsonPayload jsonPayload = (JsonPayload) payload;
String content = (String) ((JsonPayload) payload).getDataAsMap().get("message");
content = "abc" + logEntry.getSpanId() + ", " + logEntry.getTrace() + content;
jsonPayload.getDataAsMap().replace("message", content);
builder.setPayload(jsonPayload);
}
}
#Override
public void enhanceLogEntry(LogEntry.Builder builder, ILoggingEvent e) {
super.enhanceLogEntry(builder, e);
LogEntry.Builder newDummyBuilder = builder;
LogEntry logEntry = newDummyBuilder.build();
Payload<?> payload = logEntry.getPayload();
if (logEntry.getPayload() instanceof JsonPayload) {
JsonPayload jsonPayload = (JsonPayload) payload;
String content = (String) ((JsonPayload) payload).getDataAsMap().get("message");
content = "XYZ" + logEntry.getSpanId() + ", " + logEntry.getTrace() + content;
jsonPayload.getDataAsMap().replace("message", content);
builder.setPayload(jsonPayload);
}
}
}
logback-spring.xml
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<property name="STACKDRIVER_LOG_NAME" value="${STACKDRIVER_LOG_NAME:-spring.log}"/>
<property name="STACKDRIVER_LOG_FLUSH_LEVEL" value="${STACKDRIVER_LOG_FLUSH_LEVEL:-WARN}"/>
<appender name="MY_STACKDRIVER" class="com.google.cloud.spring.logging.LoggingAppender">
<log>${STACKDRIVER_LOG_NAME}</log> <!-- Optional : default spring.log -->
<loggingEventEnhancer>abc.configuration.NukTraceIdLoggingEnhancer</loggingEventEnhancer>
<flushLevel>INFO</flushLevel> <!-- Optional : default ERROR -->
</appender>
<root level="INFO">
<appender-ref ref="MY_STACKDRIVER"/>
</root>
</configuration>

Custom Provider for AWS SSM using Microsoft.Configuration.ConfigurationBuilders

I seem to be stuck at developing a custom Key/Value pair provider for Amazon's System Manager Parameter Store (SSM) using NETFramework 4.7.1 that utilizes Microsoft.Configuration.ConfigurationBuilders.
The implementation:
using System;
using System.Collections.Generic;
using Amazon.SimpleSystemsManagement;
using Amazon.SimpleSystemsManagement.Model;
using Microsoft.Configuration.ConfigurationBuilders;
using System.Linq;
using System.Diagnostics;
using System.Collections.Specialized;
using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using System.Configuration;
using System.Threading.Tasks;
namespace AXS.Configurations
{
public class ParameterStoreConfigBuilder : KeyValueConfigBuilder
{
public const string envTag = "Environment";
public const string appNameTag = "AppName";
private IAmazonSimpleSystemsManagement client;
/// <summary>
/// Gets or sets an environment (dev|qa|staging|production)
/// </summary>
public string Environment { get; set; }
/// <summary>
/// Gets or sets a AppName
/// </summary>
public string AppName { get; set; }
public ParameterStoreConfigBuilder(IAmazonSimpleSystemsManagement client,
string appName,
string environment)
{
this.client = client;
Environment = environment.ToLower();
AppName = appName;
}
public ParameterStoreConfigBuilder()
{
client = new AmazonSimpleSystemsManagementClient();
}
public override string Description => "Parameter Store";
public override string Name => "SSM";
protected override void LazyInitialize(string name, NameValueCollection config)
{
Optional = false;
base.LazyInitialize(name, config);
string env = UpdateConfigSettingWithAppSettings(envTag);
if (string.IsNullOrWhiteSpace(env))
throw new ArgumentException($"environment must be specified with the '{envTag}' attribute.");
Environment = env;
string appName = UpdateConfigSettingWithAppSettings(appNameTag);
if (string.IsNullOrWhiteSpace(appName))
throw new ArgumentException($"appName must be specified with the '{appNameTag}' attribute.");
AppName = appName;
client = new AmazonSimpleSystemsManagementClient("","", Amazon.RegionEndpoint.USWest2);
}
public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
{
Trace.TraceInformation($"return values prefix {prefix}");
if (client == null)
return null;
var parameters = new List<Parameter>();
string nextToken = null;
do
{
var response = client.GetParametersByPath(new GetParametersByPathRequest { Path = prefix, Recursive = true, WithDecryption = true, NextToken = nextToken });
nextToken = response.NextToken;
parameters.AddRange(response.Parameters);
} while (!string.IsNullOrEmpty(nextToken));
return parameters.Select(p => new
{
Key = p.Name,
p.Value
}).ToDictionary(parameter => parameter.Key, parameter => parameter.Value, StringComparer.OrdinalIgnoreCase);
}
public override string GetValue(string key)
{
return Task.Run(async () => { return await GetValueAsync(key); }).Result;
}
private async Task<string> GetValueAsync(string key)
{
var name = $"/{Environment}/{AppName}/{key.Replace(':', '/')}";
Trace.WriteLine($"get value async:{name}");
if (client == null)
return null;
try
{
Trace.TraceInformation($"fetch key {name}");
var request = new GetParameterRequest
{
Name = name,
WithDecryption = true
};
var response = await client.GetParameterAsync(request);
var parameter = response.Parameter;
var value = parameter.Type == ParameterType.SecureString ? "*****" : parameter.Value;
Trace.TraceInformation($"fetched name={name} value={value}");
return value;
}
catch (Exception e) when (Optional && ((e.InnerException is System.Net.Http.HttpRequestException) || (e.InnerException is UnauthorizedAccessException))) { }
return null;
}
}
}
The problem seems to be that AWS SSM client never gets created.
If I change the code and try to instantiate in the constructor I get a stack overflow exception due to recursion.
Any ideas on how to force to get AmazonSimpleSystemsManagementClient created?
The code uses guidance from https://github.com/aspnet/MicrosoftConfigurationBuilders
The App.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection,
System.Configuration, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
restartOnExternalChanges="false"
requirePermission="true" />
</configSections>
<configBuilders>
<builders>
<add name="ParameterStore" Environment="development" AppName="myAppNameforParmStore" type="AXS.Configurations.ParameterStoreConfigBuilder, AXS.Configurations" />
<add name="Env" prefix="appsettings_" stripPrefix="true" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=2.0.0.0, Culture=neutral" />
</builders>
</configBuilders>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
</startup>
<appSettings configBuilders="Env,ParameterStore">
<add key="Url" value="URL Value for from paramter Store" />
<add key="Secret" value="Some Secret value decrypted" />
</appSettings>
</configuration>
Thanks
UPDATE
I posted an updated version of the AwsSsmConfigurationBuilder, and a sample ASP.NET Web Forms project that uses it, on my GitHub:
https://github.com/Kirkaiya/AwsSsmConfigBuilderPoC/
Disclaimer: This is a proof-of-concept (POC) for a custom ConfigurationBuilder for ASP.NET 4.7.1 or higher (running on .NET Framework obviously). It's a POC, so it doesn't do anything besides allow you store Configuration AppSettings in AWS Parameter Store (a feature of Simple Systems Manager). So, clearly, don't use this in production without productizing and testing it!
Prerequisites:
Your project must target .NET Framework 4.7.1 or higher
Include NuGet package Microsoft.Configuration.ConfigurationBuilders.Base
Have parameters in AWS SSM Parameter Store that have the same name (not counting the prefix) as parameters in your web.config file, and vice-versa.
Notes
In order to avoid recursively calling a concrete constructor or Initialize, I used a static constructor to instantiate the AmazonSimpleSystemsManagementClient, which is held in a static member.
Web.Config additions
Note: change the assembly/class-name of your builder to match yours, etc.
<configSections>
<section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false" />
</configSections>
<configBuilders>
<builders>
<add name="ParameterStore" ssmPrefix="/padnugapp/ApiKeys" type="Microsoft.Configuration.ConfigurationBuilders.AwsSsmConfigBuilder, AspNetWebFormsSample" />
</builders>
</configBuilders>
<appSettings configBuilders="ParameterStore">
<add key="TestKey" value="TestKey Value from web.config" />
<add key="TwitterKey" value="TwitterKey value from web.config" />
</appSettings>
And the AwsSsmConfigBuilder.cs file:
namespace Microsoft.Configuration.ConfigurationBuilders
{
public class AwsSsmConfigBuilder : KeyValueConfigBuilder
{
private string BaseParameterPath = "/padnugapp/ApiKeys";
private static IAmazonSimpleSystemsManagement _client;
static AwsSsmConfigBuilder()
{
_client = new AmazonSimpleSystemsManagementClient();
}
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
if (config["ssmPrefix"] == null)
return;
BaseParameterPath = config["ssmPrefix"];
}
public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
{
if (_client == null)
return null;
var request = new GetParametersByPathRequest
{
Path = $"{BaseParameterPath}/{prefix}",
WithDecryption = true,
};
var response = _client.GetParametersByPathAsync(request).Result;
var result = response.Parameters.ToDictionary(param => param.Name, param => param.Value, StringComparer.OrdinalIgnoreCase);
return result;
}
public override string GetValue(string key)
{
if (_client == null)
return null;
var request = new GetParameterRequest
{
Name = $"{BaseParameterPath}/{key}",
WithDecryption = true,
};
var response = _client.GetParameterAsync(request).Result;
return response.Parameter.Value;
}
}
}
The code I put into a web-forms (.aspx) page that renders the two appSettings items in HTML:
TestKey =
<%=(System.Configuration.ConfigurationManager.AppSettings["TestKey"]) %>
<br />
TwitterKey =
<%=(System.Configuration.ConfigurationManager.AppSettings["TwitterKey"]) %>
I can't stress enough that this is just for a demo I'm doing, and not tested in any way, shape or form except on my laptop ;-)

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary: DYNAMICS 365 Plugin

When I run a Unit Test for my plugin I get the following Exception being Thrown:
Message: Test method Plugins.Tests.UnitTest1.TestUnitPlugin threw exception:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
The following link will give you the stack trace:
Stacktrace
Even when I deploy & register my plugin to the online instance, I would get the same message!!
My Plugin code looks like this:
using System;
using System.Collections.Generic;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// This plugin takes the data provided in the contract lines and makes Unit Orders.. Inside the unit orders, an Alter Unit Orders table is present.
/// The Alter Unit Orders table describes the daily order for each day in the contract's duration.
/// </summary>
namespace DCWIMS.Plugins
{
[CrmPluginRegistration(MessageNameEnum.Update,
"contract",
StageEnum.PreOperation,
ExecutionModeEnum.Synchronous,
"title",
"Post-Update Contract",
1000,
IsolationModeEnum.Sandbox,
Image1Name = "PreImage",
Image1Type = ImageTypeEnum.PreImage,
Image1Attributes = "title")]
public class UnitPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// Wil be registering this plugin, thus will need to add tracing service related code.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//obtain execution context from service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters colletion contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
//obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//Obtain Target Entity Id
var targetId = entity.Id.ToString();
//verify that the target entity represents the the contract entity and is active
if (entity.LogicalName != "contract" && entity.GetAttributeValue<OptionSetValue>("statecode").Value != 0)
return;
//obtain the organization service for web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
//Get Contract StartDate
DateTime startDate = (DateTime)entity["activeon"];
//Get Contract EndDate
DateTime endDate = (DateTime)entity["expireson"];
//Get all weekdays in the contract duration
Eachday range = new Eachday();
var weekdays = range.WeekDay(startDate, endDate); //weekdays list
//Get Contract Number
string contractNumber = (string)entity["contractnumber"];
//Query and aggregate each Weekday's order for the 3 different meal times...
//AM SNACK
string unitsum_am = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_amsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_amsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_amsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_amsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_amsum' aggregate='sum' />
<filter type='and' >
<condition value='100000001' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_am_result =
service.RetrieveMultiple(new FetchExpression(unitsum_am));
var am_list = new List<int>();
foreach(var unit in unitsum_am_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_amsum"]).Value);
am_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_amsum"]).Value);
am_list.Add(tuesdaysum);
var wednesdaysum= ((int)((AliasedValue)unit["new_unitswednesday_amsum"]).Value);
am_list.Add(wednesdaysum);
var thursdaysum= ((int)((AliasedValue)unit["new_unitsthursday_amsum"]).Value);
am_list.Add(thursdaysum);
var fridaysum= ((int)((AliasedValue)unit["new_unitsfriday_amsum"]).Value);
am_list.Add(fridaysum);
}
//LUNCH
string unitsum_lunch = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_lunchsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_lunchsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_lunchsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_lunchsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_lunchsum' aggregate='sum' />
<filter type='and' >
<condition value='100000002' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_lunch_result =
service.RetrieveMultiple(new FetchExpression(unitsum_lunch));
var lunch_list = new List<int>();
foreach (var unit in unitsum_lunch_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_lunchsum"]).Value);
lunch_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_lunchsum"]).Value);
lunch_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_lunchsum"]).Value);
lunch_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_lunchsum"]).Value);
lunch_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_lunchsum"]).Value);
lunch_list.Add(fridaysum);
}
//PM SNACK
string unitsum_pm = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_pmsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_pmsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_pmsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_pmsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_pmsum' aggregate='sum' />
<filter type='and' >
<condition value='100000003' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_pm_result =
service.RetrieveMultiple(new FetchExpression(unitsum_pm));
var pm_list = new List<int>();
foreach (var unit in unitsum_pm_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_pmsum"]).Value);
pm_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_pmsum"]).Value);
pm_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_pmsum"]).Value);
pm_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_pmsum"]).Value);
pm_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_pmsum"]).Value);
pm_list.Add(fridaysum);
}
foreach(var day in weekdays)
{
var alterunit = new Entity("new_alterunitorder");
alterunit.Attributes.Add("new_orderdate", DateTime.Parse(day));
switch (day.Split(',')[0])
{
case "Monday":
alterunit.Attributes.Add("new_amsnack", am_list[0]);
alterunit.Attributes.Add("new_lunch", lunch_list[0]);
alterunit.Attributes.Add("new_pmsnack", pm_list[0]);
break;
case "Tuesday":
alterunit.Attributes.Add("new_amsnack", am_list[1]);
alterunit.Attributes.Add("new_lunch", lunch_list[1]);
alterunit.Attributes.Add("new_pmsnack", pm_list[1]);
break;
case "Wednesday":
alterunit.Attributes.Add("new_amsnack", am_list[2]);
alterunit.Attributes.Add("new_lunch", lunch_list[2]);
alterunit.Attributes.Add("new_pmsnack", pm_list[2]);
break;
case "Thursday":
alterunit.Attributes.Add("new_amsnack", am_list[3]);
alterunit.Attributes.Add("new_lunch", lunch_list[3]);
alterunit.Attributes.Add("new_pmsnack", pm_list[3]);
break;
case "Friday":
alterunit.Attributes.Add("new_amsnack", am_list[4]);
alterunit.Attributes.Add("new_lunch", lunch_list[4]);
alterunit.Attributes.Add("new_pmsnack", pm_list[4]);
break;
default:
Console.WriteLine($"An unexpected value ({day.Split(',')})");
break;
}
alterunit.Attributes.Add("new_name", contractNumber); //set the record name
//set the unit order record relation
alterunit.Attributes["new_orderlineid"] =
new EntityReference("new_alterunitorder", unitOrderId);
service.Create(alterunit);
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occured.. Phil is responsible.", ex);
}
catch (Exception ex)
{
tracing.Trace("An Error Occured: {0}", ex.ToString());
throw;
}
}
}
}
}
Here is the code for Eachday:
using System;
using System.Collections.Generic;
namespace DCWIMS.Plugins
{
public class Eachday
{
public List<string> WeekDay(DateTime from, DateTime thru)
{
List<string> days_list = new List<string>();
for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
{
days_list.Add(day.ToLongDateString());
if (day.DayOfWeek == DayOfWeek.Sunday || day.DayOfWeek == DayOfWeek.Saturday)
days_list.Remove(day.ToLongDateString());
}
return days_list;
}
}
}
My unit test looks like this:
using DCWIMS.Plugins;
using Microsoft.Crm.Sdk.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
namespace Plugins.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
[TestCategory("Unit Test")]
public void TestUnitPlugin()
{
using (var pipline = new PluginPipeline(FakeMessageNames.Update, FakeStages.PreOperation, new Entity("contract")))
{
var plugin = new UnitPlugin();
pipline.Execute(plugin);
}
}
}
}
Even when I registered the plugin on the actual CRM instance I got this error message:
Here is the log file retrieved from CRM Online!
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ActivityId>4025e0f8-eed5-4b7f-a3b1-52a9f2a6f2cc</ActivityId>
<ErrorCode>-2147220956</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.</Message>
<Timestamp>2018-07-20T17:47:36.4061434Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText>
[Plugins: DCWIMS.Plugins.UnitPlugin]
[dbb33fa1-448c-e811-815c-480fcff4b5b1: Pre-Create Contract]
An Error Occured: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Microsoft.Xrm.Sdk.AttributeCollection.get_Item(String attributeName)
at DCWIMS.Plugins.UnitPlugin.Execute(IServiceProvider serviceProvider)
</TraceText>
</OrganizationServiceFault
>
Unit testing ain't my forte. But I will try to guide you.
First, my opinion on unit testing. I don't value it much in Dynamics because it's an environment that is hard to control. You can test that your business logic is well implemented in the plugin but a javascript, a business rule, a workflow or an action can derail your logic. Your plugin can say blue but an async workflow change it to red right after.
Instead, I prefer UI testing. It will test the process from A to Z and force the execution of all the components mentioned above. Here's a nice framework I recommend: https://github.com/Microsoft/EasyRepro
That being said. To solve your problem you also need to mock the Organization Service and return fake data. By that, I mean this line:
EntityCollection unitsum_am_result = service.RetrieveMultiple(new FetchExpression(unitsum_am));
Sadly, even if you mock, you can't really parse the fetchXml and validate it.
Then, after returning the fake data you should assert that this line, create an entity with the right sums:
service.Create(alterunit);

Google Glass Live Card not inserting

Glass GDK here. Trying to insert a livecard using remote views from service. I'm launching service via voice invocation. The voice command works, however it appears my service is not starting(no entries in log). Service is in android manifest. Below is code:
public class PatientLiveCardService extends Service {
private static final String LIVE_CARD_ID = "timer";
#Override
public void onCreate() {
Log.warn("oncreate");
super.onCreate();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
publishCard(this);
return START_STICKY;
}
#Override
public void onDestroy() {
unpublishCard(this);
super.onDestroy();
}
private void publishCard(Context context) {
Log.info("inserting live card");
if (mLiveCard == null) {
String cardId = "my_card";
TimelineManager tm = TimelineManager.from(context);
mLiveCard = tm.getLiveCard(cardId);
mLiveCard.setViews(new RemoteViews(context.getPackageName(),
R.layout.activity_vitals));
Intent intent = new Intent(context, MyActivity.class);
mLiveCard.setAction(PendingIntent
.getActivity(context, 0, intent, 0));
mLiveCard.publish();
} else {
// Card is already published.
return;
}
}
private void unpublishCard(Context context) {
if (mLiveCard != null) {
mLiveCard.unpublish();
mLiveCard = null;
}
}
}
Here is AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.RECORD_AUDIO" >
</uses-permission>
<application
android:name="com.myApp"
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.myApp.MyActivity"
android:label="#string/app_name"
android:screenOrientation="landscape" >
</activity>
<service android:name="com.myApp.services.MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
</intent-filter>
<meta-data
android:name="com.google.android.glass.VoiceTrigger"
android:resource="#xml/voice_trigger_get_patient" />
</service>
</application>
This is a bug with XE11: the service is not started after the speech recognizer is complete.
As a workaround, you can have your voice trigger start an Activity which:
Processes the recognized speech in onResume.
Once the speech is processed, starts your Service with startService.
Calls finish to jump to the published LiveCard.

Soap Error connecting to hosted CRM online 2011

I have been trying to troubleshoot this error for a long time.
"An error occurred when verifying security for the message"
I did some research, people said this is because the time difference between the server and the client.
This anyone else have the same problem?
Below is the detail of my error
System.ServiceModel.FaultException was caught
Message=An error occurred when verifying security for the message.
Source=mscorlib
StackTrace:
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at WindowsFormsApplication1.CrmSdk.Discovery.IDiscoveryService.Execute(DiscoveryRequest request)
at WindowsFormsApplication1.CrmSdk.Discovery.DiscoveryServiceClient.Execute(DiscoveryRequest request) in WindowsFormsApplication1\Service References\CrmSdk.Discovery\Reference.cs:line 723
at WindowsFormsApplication1.Form1.DiscoverOrganizationUrl(String organizationName, String discoveryServiceUrl) in Form1.cs:line 110
InnerException:
Here is the code i used to access the hosted CRM online webservice
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Web;
//using System.ServiceModel;
//using System.ServiceModel.Description;
namespace WindowsFormsApplication1
{
using CrmSdk;
using CrmSdk.Discovery;
using System.Net;
using System.Globalization;
using LocalServices;
using System.ServiceModel;
using System.Web.Services.Protocols;
public partial class Form1 : Form
{
///hosted CRM online
private const string DiscoveryServiceUrl = "https://disco.crm.dynamics.com/XRMServices/2011/Discovery.svc";
private IOrganizationService _service;
private string fetchXML =
#"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='account'>
<attribute name='name' />
<attribute name='address1_city' />
<attribute name='primarycontactid' />
<attribute name='telephone1' />
<attribute name='accountid' />
<order attribute='name' descending='false' />
<filter type='and'>
<condition attribute='ownerid' operator='eq-userid' />
<condition attribute='statecode' operator='eq' value='0' />
</filter>
<link-entity name='contact' from='contactid' to='primarycontactid' visible='false' link-type='outer' alias='accountprimarycontactidcontactcontactid'>
<attribute name='emailaddress1' />
</link-entity>
</entity>
</fetch>
";
public Form1()
{
InitializeComponent();
//GetDataFromCRM();
GetDataFromCRM2();
}
private void GetDataFromCRM2()
{
private string DiscoverOrganizationUrl(string organizationName, string discoveryServiceUrl)
{
using (CrmSdk.Discovery.DiscoveryServiceClient client = new CrmSdk.Discovery.DiscoveryServiceClient("CustomBinding_IDiscoveryService", discoveryServiceUrl))
{
//ApplyCredentials(client, credentials);
client.ClientCredentials.Windows.ClientCredential.UserName = UserName;
client.ClientCredentials.Windows.ClientCredential.Password = Password
client.ClientCredentials.Windows.ClientCredential.Domain = Domain
CrmSdk.Discovery.RetrieveOrganizationRequest request = new CrmSdk.Discovery.RetrieveOrganizationRequest()
{
UniqueName = organizationName
};
try
{
CrmSdk.Discovery.RetrieveOrganizationResponse response = (CrmSdk.Discovery.RetrieveOrganizationResponse)client.Execute(request);
foreach (KeyValuePair<CrmSdk.Discovery.EndpointType, string> endpoint in response.Detail.Endpoints)
{
if (CrmSdk.Discovery.EndpointType.OrganizationService == endpoint.Key)
{
Console.WriteLine("Organization Service URL: {0}", endpoint.Value);
return endpoint.Value;
}
}
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
"Organization {0} does not have an OrganizationService endpoint defined.", organizationName));
}
catch (FaultException e)
{
MessageBox.Show(e.Message);
throw;
}
catch (SoapHeaderException e)
{
MessageBox.Show(e.Message);
throw;
}
catch (SoapException e)
{
MessageBox.Show(e.Message);
throw;
}
return null;
}
}
//private static void ApplyCredentials<TChannel>(ClientBase<TChannel> client, ICredentials credentials)
// where TChannel : class
//{
// client.ClientCredentials.Windows.ClientCredential = credentials.Windows.ClientCredential;
//}
private void ExecuteFetch(string serviceUrl)
{
using (OrganizationServiceClient client = new OrganizationServiceClient("CustomBinding_IOrganizationService", new EndpointAddress(serviceUrl)))
{
client.ClientCredentials.Windows.ClientCredential.UserName = UserName;
client.ClientCredentials.Windows.ClientCredential.Password = Password;
client.ClientCredentials.Windows.ClientCredential.Domain = Domain;
_service = (IOrganizationService)client;
FetchExpression expression = new FetchExpression();
expression.Query =
#"
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='account'>
<attribute name='name' />
<attribute name='address1_city' />
<attribute name='primarycontactid' />
<attribute name='telephone1' />
<attribute name='accountid' />
<order attribute='name' descending='false' />
<filter type='and'>
<condition attribute='ownerid' operator='eq-userid' />
<condition attribute='statecode' operator='eq' value='0' />
</filter>
<link-entity name='contact' from='contactid' to='primarycontactid' visible='false' link-type='outer' alias='accountprimarycontactidcontactcontactid'>
<attribute name='emailaddress1' />
</link-entity>
</entity>
</fetch>
";
EntityCollection result = _service.RetrieveMultiple(expression);
DataTable temp = ConvertEntityToTable(result);
}
}
/// Convert Entity To datatable
private DataTable ConvertEntityToTable(EntityCollection result)
{
DataTable dt = new DataTable();
int rowCount = result.Entities.Count();
try
{
for (int i = 0; i < rowCount; i++)
{
DataRow dr = dt.NewRow();
Entity currentEntity = (Entity)result.Entities[i];
var keys = currentEntity.Attributes.Count();
for (int j = 0; j < keys; j++)
{
string columName = currentEntity.Attributes[j].Key;
string value = currentEntity.Attributes[j].Value.ToString();
if (dt.Columns.IndexOf(columName) == -1)
dt.Columns.Add(columName, Type.GetType("Sysem.String"));
dr[columName] = value;
}
dt.Rows.Add(dr);
}
return dt;
}
catch (Exception exp)
{
throw;
}
}
}
}
app.config
<bindings>
<customBinding>
<binding name="CustomBinding_IDiscoveryService">
<textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
messageVersion="Default" writeEncoding="utf-8">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</textMessageEncoding>
<httpsTransport manualAddressing="false" maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" allowCookies="false" authenticationScheme="Anonymous"
bypassProxyOnLocal="false" decompressionEnabled="true" hostNameComparisonMode="StrongWildcard"
keepAliveEnabled="true" maxBufferSize="65536" proxyAuthenticationScheme="Anonymous"
realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
useDefaultWebProxy="true" requireClientCertificate="false"
/>
</binding>
It throw the error at FaultException
Thanks for all the help
The general consensus appears to be that besides the actual time, the timezone and daylight savings time settings appear to be the cause of this error. The client and server need to be in sync with each other.