Hi I'm new on IBM MQ environment so I need your help....
I need to develope a script that check an IBMMQ (outside my factory) queue to verify if there are messages waiting to be get from my application-server (I mean if my apllication-server lost the connection to IBMMQ for any reason, I want to be informed, in this mode I can take the correction).
If is possible I need the script in PowereShell or C#.
Thanks a lot.
There is sample code that can get you started - https://github.com/ibm-messaging/mq-dev-patterns/blob/master/dotnet/dotNetGet.cs
replace the CreateConsumer method invocation with CreateBrowser
ie. the modified snippet becomes
private void ReceiveMessagesFromEndpoint(IConnectionFactory cf)
{
IConnection connectionWMQ;
ISession sessionWMQ;
IDestination destination;
IMessageBrowser browser;
ITextMessage textMessage;
// Create connection.
connectionWMQ = cf.CreateConnection();
Console.WriteLine("Connection created");
// Create session
sessionWMQ = connectionWMQ.CreateSession(false, AcknowledgeMode.AutoAcknowledge);
Console.WriteLine("Session created");
// Create destination
destination = sessionWMQ.CreateQueue(env.Conn.queue_name);
Console.WriteLine("Destination created");
// Create browser
browser = sessionWMQ.CreateBrowser(destination);
Console.WriteLine("Browser created");
...
Instructions on setup are available in the associated readme - https://github.com/ibm-messaging/mq-dev-patterns/blob/master/dotnet/README.md
Do not browse the queue to get the current queue depth as it will cause an excessive amount of network traffic and is totally unnecessary.
Here is a C#/.NET/MQ sample program (fully functional) that will inquire on the queue for its current depth.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using IBM.WMQ;
/// <summary> Program Name
/// MQTest75
///
/// Description
/// This C# class will connect to a remote queue manager
/// and inquire on the current depth of the queue using a managed .NET environment.
///
/// </summary>
/// <author> Roger Lacroix
/// </author>
namespace MQTest75
{
class MQTest75
{
private Hashtable qMgrProp = null;
private System.String qManager;
private System.String inputQName;
/*
* The constructor
*/
public MQTest75()
: base()
{
}
/// <summary> Make sure the required parameters are present.</summary>
/// <returns> true/false
/// </returns>
private bool allParamsPresent()
{
bool b = false;
if ( (ConfigurationManager.AppSettings["ConnectionName"] != null) &&
(ConfigurationManager.AppSettings["Port"] != null) &&
(ConfigurationManager.AppSettings["ChannelName"] != null) &&
(ConfigurationManager.AppSettings["QMgrName"] != null) &&
(ConfigurationManager.AppSettings["QueueName"] != null) )
{
try
{
System.Int32.Parse(ConfigurationManager.AppSettings["Port"]);
b = true;
}
catch (System.FormatException e)
{
b = false;
}
}
return b;
}
/// <summary> Extract the configuration applicaiton settings and initialize the MQ variables.</summary>
/// <param name="args">
/// </param>
/// <throws> IllegalArgumentException </throws>
private void init(System.String[] args)
{
if (allParamsPresent())
{
qManager = ConfigurationManager.AppSettings["QMgrName"];
inputQName = ConfigurationManager.AppSettings["QueueName"];
qMgrProp = new Hashtable();
qMgrProp.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
qMgrProp.Add(MQC.HOST_NAME_PROPERTY, ConfigurationManager.AppSettings["ConnectionName"]);
qMgrProp.Add(MQC.CHANNEL_PROPERTY, ConfigurationManager.AppSettings["ChannelName"]);
try
{
qMgrProp.Add(MQC.PORT_PROPERTY, System.Int32.Parse(ConfigurationManager.AppSettings["Port"]));
}
catch (System.FormatException e)
{
qMgrProp.Add(MQC.PORT_PROPERTY, 1414);
}
if (ConfigurationManager.AppSettings["UserId"] != null)
qMgrProp.Add(MQC.USER_ID_PROPERTY, ConfigurationManager.AppSettings["UserId"]);
if (ConfigurationManager.AppSettings["Password"] != null)
qMgrProp.Add(MQC.PASSWORD_PROPERTY, ConfigurationManager.AppSettings["Password"]);
logger("Parameters:");
logger(" QMgrName ='" + qManager + "'");
logger(" Queue Name ='" + inputQName + "'");
logger("Connection values:");
foreach (DictionaryEntry de in qMgrProp)
{
logger(" " + de.Key + " = '" + de.Value + "'");
}
}
else
{
throw new System.ArgumentException();
}
}
/// <summary> Connect, open queue, output the current queue depth, close queue and disconnect.</summary>
/// <throws> MQException </throws>
private void handleIt()
{
MQQueueManager qMgr = null;
MQQueue inQ = null;
int openOptions = MQC.MQOO_INQUIRE + MQC.MQOO_FAIL_IF_QUIESCING;
try
{
qMgr = new MQQueueManager(qManager, qMgrProp);
logger("Successfully connected to " + qManager);
inQ = qMgr.AccessQueue(inputQName, openOptions);
logger("Successfully opened " + inputQName);
logger("Current queue depth is " + inQ.CurrentDepth);
}
catch (MQException mqex)
{
logger("CC=" + mqex.CompletionCode + " : RC=" + mqex.ReasonCode);
}
catch (System.IO.IOException ioex)
{
logger("Error: ioex=" + ioex);
}
finally
{
try
{
if (inQ != null)
{
inQ.Close();
logger("Closed: " + inputQName);
}
}
catch (MQException mqex)
{
logger("CC=" + mqex.CompletionCode + " : RC=" + mqex.ReasonCode);
}
try
{
if (qMgr != null)
{
qMgr.Disconnect();
logger("Disconnected from " + qManager);
}
}
catch (MQException mqex)
{
logger("CC=" + mqex.CompletionCode + " : RC=" + mqex.ReasonCode);
}
}
}
/// <summary> Output the log message to stdio.</summary>
/// <param name="data">
/// </param>
private void logger(String data)
{
DateTime myDateTime = DateTime.Now;
System.Console.Out.WriteLine(myDateTime.ToString("yyyy/MM/dd HH:mm:ss.fff") + " " + this.GetType().Name + ": " + data);
}
/// <summary> main line</summary>
/// <param name="args">
/// </param>
// [STAThread]
public static void Main(System.String[] args)
{
MQTest75 mqt = new MQTest75();
try
{
mqt.init(args);
mqt.handleIt();
}
catch (System.ArgumentException e)
{
System.Console.Out.WriteLine("Check your configuration file for an incorrect parameter.");
System.Environment.Exit(1);
}
catch (MQException e)
{
System.Console.Out.WriteLine(e);
System.Environment.Exit(1);
}
System.Environment.Exit(0);
}
}
}
Just create a configuration called MQTest75.exe.config and put the following in it:
<configuration>
<appSettings>
<add key="QMgrName" value="MQA1"/>
<add key="ConnectionName" value="10.10.10.10"/>
<add key="Port" value="1414"/>
<add key="ChannelName" value="TEST.CHL"/>
<add key="QueueName" value="TEST.Q1"/>
<add key="UserId" value="tester"/>
<add key="Password" value="mypwd"/>
</appSettings>
</configuration>
Related
This is not about Delayed message delivery. Lately I've noticed my endpoint has a delay in handling a message when looking at when the message is taken from the RabbitMQ queue.
I was wondering if there is some configuration option I’m overlooking that has an impact on the delay between the receipt of the message from RabbitMQ by NServiceBus and the actual handling of the message (command) in the IHandleMessages<> handler.
I’m seeing a consistent delay when comparing the NServiceBus logfile and the logfile of the handler. It does vary a bit between client and server but in a given session it’s always constant. Now I’m running through all the options that I can find but there’s no real pointer for me out in the wild.
The thing is I’m running a workflow solution that uses NServiceBus to do all the messaging between the workflow server (based on MS WF) and 50/60 clients. The server runs on NServiceBus 7.6.0 but all client still use NServiceBus 5.2.26. I decided to write a small test client that also runs on NServiceBus 7.6.0 but that also shows the delay albeit a smaller one. But the delay between individual commands is constant at 6.5 seconds in the test client. So the message is retrieved by NSB from the queue and 6.5 seconds the IHandleMessages<> handler is invoked.
I’ve tried a couple of things (direct routing vs conventional routing, making sure license files where distributed properly for example). The only thing I’m able to come up right now is flat out making a new test server of the workflow server I’m using that is only able to accept 1 command, generating a reply and nothing more and see if that makes a difference.
Configuration wise the solution runs on a Windows Server 2016 having 4 cores, RabbitMQ 3.7.21 and NServiceBus 7.6.0. Clients run on Windows 10 machines using NServiceBus 5.2.26 but the test client uses NServiceBus 7.6.0.
Now for the logs. This is the NServicebus log when it receives the GetVersionCommand:
2022-01-20 16:09:58.104 DEBUG Processing message type: HyFlo.Service.Messages.Commands.Info.GetVersionCommand
Message headers:
CorrelationId : 619d678d-25bf-46c0-b5c0-57c624e3d557
NServiceBus.MessageId : 548813d5-2465-4ff5-904d-ae2300f9ee23
NServiceBus.MessageIntent : Send
NServiceBus.ConversationId : 367575b0-7bee-40d0-90ca-ae2300f9ee24
NServiceBus.CorrelationId : 548813d5-2465-4ff5-904d-ae2300f9ee23
NServiceBus.TimeToBeReceived : 00:10:00
NServiceBus.ReplyToAddress : Hyflo.Client.Test
NServiceBus.OriginatingMachine : DYP297
NServiceBus.OriginatingEndpoint : Hyflo.Client.Test
$.diagnostics.originating.hostid : 3a3bbd6661214b655aa6dbcd95d112a2
NServiceBus.ContentType : text/xml
NServiceBus.EnclosedMessageTypes : HyFlo.Service.Messages.Commands.Info.GetVersionCommand, HyFlo.Service.Messages, Version=4.10.3.26734, Culture=neutral, PublicKeyToken=null;HyFlo.Types.Interfaces.IInfoCommand, HyFlo.Types, Version=4.10.8.20782, Culture=neutral, PublicKeyToken=null
NServiceBus.Version : 7.6.0
NServiceBus.TimeSent : 2022-01-20 15:09:58:107461 Z
NServiceBus.NonDurableMessage : False
Handlers to invoke:
HyFlo.Service.CommandHandlers.Info.GetVersionHandler
Then the custom log of the GetVersionHandler:
2022-01-20 16:10:04.6516 TRACE = New Message =====================================================================================================================================
2022-01-20 16:10:04.6516 TRACE CommandEventId:9125b036-c315-484e-9948-889ed1e56587: New message handled by handler of type 'HyFlo.Service.CommandHandlers.Info.GetVersionHandler' with CorrelationId '619d678d-25bf-46c0-b5c0-57c624e3d557' ..
2022-01-20 16:10:04.6516 TRACE ===================================================================================================================================================
2022-01-20 16:10:04.6516 INFO Cache contains a systemstatus ..
2022-01-20 16:10:04.6516 INFO Retrieved systemstatus from cache is: 0
2022-01-20 16:10:04.6516 INFO Running the injected code ..
2022-01-20 16:10:04.6672 TRACE ===================================================================================================================================================
2022-01-20 16:10:04.6672 TRACE CommandEventId:9125b036-c315-484e-9948-889ed1e56587: Message processed by handler of type 'HyFlo.Service.CommandHandlers.Info.GetVersionHandler' with CorrelationId '619d678d-25bf-46c0-b5c0-57c624e3d557' ..
2022-01-20 16:10:04.6672 TRACE Elapsed time: 00:00:00.0156159 ..
2022-01-20 16:10:04.6672 TRACE = End of Message ==================================================================================================================================
The command is received at 2022-01-20 16:09:58.104 but is delivered at the Handler at 2022-01-20 16:10:04.6516 and this delay is quite consistent between this and other commands. I just can't get my head around why this delay is there.
The endpoint configuration is the following:
internal class ProgramService : ServiceBase
{
private IEndpointInstance _endpointInstance = null;
/// <summary>
/// Determines if application is ran as service or just as console application
/// </summary>
/// <param name="name"></param>
/// <returns>true if it's a service</returns>
private static bool IsService(string name)
{
if (!Environment.UserInteractive) return true;
var sc = new ServiceController(name);
try
{
return sc.Status == ServiceControllerStatus.StartPending;
}
catch (InvalidOperationException)
{
return false;
}
}
/// <summary>
/// Main entry
/// </summary>
private static void Main()
{
using (var service = new ProgramService())
{
// so we can run interactive from Visual Studio or as a windows service
if (!IsService("HyFlo.Service"))
{
Console.CancelKeyPress += (sender, e) => { service.OnStop(); };
service.OnStart(null);
Console.WriteLine("\r\nPress enter key to stop program\r\n");
Console.Read();
service.OnStop();
return;
}
Run(service);
}
}
/// <summary>
/// On critical errors bail out
/// </summary>
/// <param name="errorMessage"></param>
/// <param name="exception"></param>
async Task OnCriticalError(ICriticalErrorContext context)
{
var fatalMessage = $"The following critical error was encountered:\n{context.Error}\nProcess is shutting down.";
try
{
Console.WriteLine("fatalMessage: " + fatalMessage);
TraceWriter.Error(fatalMessage);
if (context.Exception.InnerException != null)
{
TraceWriter.Error("innerException message: " + context.Exception.InnerException.Message + #"\n" +
context.Exception.InnerException.StackTrace);
}
await context.Stop().ConfigureAwait(false);
}
finally
{
Environment.FailFast(fatalMessage, context.Exception);
}
}
/// <summary>
/// Starting the service
/// </summary>
/// <param name="args"></param>
private async Task AsyncOnStart()
{
TraceWriter.Trace("AsyncOnStart() running .. ");
try
{
TraceWriter.Info("Running configuration management ..");
var config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var section =
config.GetSection("connectionStrings");
if (!section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection("DPAPIProtection");
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Modified);
}
var endpointConfig = new EndpointConfiguration("hyflo.service");
endpointConfig.EnableInstallers();
endpointConfig.SendFailedMessagesTo("hyflo.service.errors");
endpointConfig.AuditProcessedMessagesTo("hyflo.service.audit");
endpointConfig.UseSerialization<XmlSerializer>();
endpointConfig.DefineCriticalErrorAction(OnCriticalError);
endpointConfig.LimitMessageProcessingConcurrencyTo(8);
var persistenceMethod = ConfigurationManager.AppSettings["NServicebusPersistence"];
if (persistenceMethod.ToUpper() == "RAVENDB")
{
string connectionString = ConfigurationManager.ConnectionStrings["NServiceBus/Persistence"].ConnectionString;
TraceWriter.Info($"Setting persistence to RavenDBPersistence based on connectionString '{connectionString}' .. ");
var documentStore = new Raven.Client.Document.DocumentStore
{
ConnectionStringName = "NServiceBus/Persistence",
DefaultDatabase = "HyFlo.Service"
};
documentStore.Initialize();
endpointConfig.UsePersistence<RavenDBPersistence>().SetDefaultDocumentStore(documentStore);
}
else
{
TraceWriter.Info("Setting persistence to InMemoryPersistence .. ");
endpointConfig.UsePersistence<InMemoryPersistence>();
}
var transportConnection = ConfigurationManager.ConnectionStrings[#"NServiceBus/Transport"];
string transportConnectionString = transportConnection.ConnectionString;
if (String.IsNullOrEmpty(transportConnectionString))
{
transportConnectionString = "host=localhost";
}
TraceWriter.Info($"Configuring RabbitMQTransport for connection '{transportConnectionString}' .. ");
var transport = endpointConfig.UseTransport<RabbitMQTransport>();
transport.ConnectionString(transportConnectionString);
transport.UseConventionalRoutingTopology();
string databusBasePath = ConfigurationManager.AppSettings["DataBusBasePath"] ?? "";
TraceWriter.Info($"Setting Databus's basepath to '{databusBasePath}' ..");
endpointConfig.UseDataBus<FileShareDataBus>().BasePath(databusBasePath);
TraceWriter.Info("Scannning for Hyflo assemblies .. ");
List<string> hyfloAssemblies =
Directory.GetFiles(Directory.GetCurrentDirectory(), "HyFlo.*dll", SearchOption.TopDirectoryOnly).ToList();
TraceWriter.Info("Initializing Autofac with assemblies .. ");
foreach (string assemblyName in hyfloAssemblies)
{
TraceWriter.Info($"Scanning '{assemblyName}' for an Autofac module .. ");
}
try
{
var containerSettings = endpointConfig.UseContainer(new AutofacServiceProviderFactory());
containerSettings.ConfigureContainer(containerBuilder =>
{
var loadedAssemblies = hyfloAssemblies.Select(Assembly.LoadFile).ToList();
containerBuilder.RegisterAssemblyModules(loadedAssemblies.ToArray());
});
}
catch (Exception ex)
{
TraceWriter.Error($"{ex.Message}\n{ex.StackTrace}\n{ex.InnerException?.Source}");
await OnCriticalError(new CriticalErrorContext(null, $"Exception occurred during initialization. Exception is: {ex.Message}\n{ex.StackTrace}\n{ex.InnerException?.Source}", ex));
}
TraceWriter.Info("Setting up default message conventions ..");
var conventions = endpointConfig.Conventions();
conventions.DefiningTimeToBeReceivedAs(type => TimeSpan.FromMinutes(10));
var logFactory = LogManager.Use<DefaultFactory>();
logFactory.Level(LogLevel.Debug);
endpointConfig.EnableFeature<HyfloInitializationFeature>();
TraceWriter.Info("Initialized! Now starting Hyflo ..");
_endpointInstance = await Endpoint.Start(endpointConfig);
TraceWriter.Info("Hyflo started ..");
}
catch (Exception exception)
{
TraceWriter.Error($"{exception.Message}\n{exception.StackTrace}\n{exception.InnerException?.Source}");
await OnCriticalError(new CriticalErrorContext(null, "Failed to start the bus.", exception));
}
}
/// <summary>
/// Stopping the service
/// </summary>
/// <returns></returns>
private Task AsyncOnStop()
{
TraceWriter.Info("Shutting down Hyflo ..");
if (_endpointInstance != null)
{
return _endpointInstance.Stop();
}
return Task.CompletedTask;
}
// <summary>
/// Starting the service
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
TraceWriter.Trace("ProgramService.OnStart() ..");
AsyncOnStart().GetAwaiter().GetResult();
}
/// <summary>
/// Stopping the service
/// </summary>
protected override void OnStop()
{
TraceWriter.Trace("ProgramService.OnStop() ..");
AsyncOnStop().GetAwaiter().GetResult();
}
}
The HyfloInitializationFeature is a startup task that sends a list of all current workflows to the connected clients.
using HyFlo.Tasks.Interfaces;
using NServiceBus.Features;
namespace HyFlo.Service
{
public class HyfloInitializationFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
context.RegisterStartupTask(c => new SystemStartupTask(c.Build<IGetInfoOnAllWorkflowsTask>(), c.Build<ISystemTestTask>()));
}
}
}
This is the SystemStartupTask:
namespace HyFlo.Service
{
public class SystemStartupTask : FeatureStartupTask, IDisposable
{
private readonly IGetInfoOnAllWorkflowsTask _getInfoOnAllWorkflowsTask;
private readonly ISystemTestTask _systemTestTask;
public SystemStartupTask(IGetInfoOnAllWorkflowsTask getInfoOnAllWorkflowsTask, ISystemTestTask systemTestTask)
{
_getInfoOnAllWorkflowsTask = getInfoOnAllWorkflowsTask;
_systemTestTask = systemTestTask;
}
public void Dispose()
{
}
/// <summary>
/// OnStart
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected override async Task OnStart(IMessageSession session)
{
TraceWriter.Trace($"==> Running SystemStartupTask.OnStart() ..");
DeleteHeartbeatIndicator();
ObjectCache cache = MemoryCache.Default;
var policy = new CacheItemPolicy { SlidingExpiration = (TimeSpan.FromSeconds(15)) };
bool systemOk = false;
if (_systemTestTask != null)
{
_systemTestTask.ContextWrapper = new MessageContextWrapper { Session = session };
_systemTestTask.QuickTest = false;
_systemTestTask.TestComponentsMask = 31;
_systemTestTask.SystemStartup = true;
if (_systemTestTask.Run())
{
var systemResultTexts = new List<string>();
if (int.TryParse(_systemTestTask.Results.ToString(), out int systemsResult))
{
if ((systemsResult & 1) == 1)
{
systemResultTexts.Add("HSB offline");
}
if ((systemsResult & 2) == 2)
{
systemResultTexts.Add("HDM offline");
}
if ((systemsResult & 4) == 4)
{
systemResultTexts.Add("SqlServer offline");
}
if ((systemsResult & 8) == 8)
{
systemResultTexts.Add("Workflow Queueing offline");
}
}
TraceWriter.Trace(
$"SystemStartupTask: Results returned by systemtest: '{(!systemResultTexts.Any() ? "All online" : String.Join(",", systemResultTexts))}' ..");
if (!_systemTestTask.Failure && !systemResultTexts.Any())
{
TraceWriter.Info("HyFlo dependencies all up and running ..");
systemOk = true;
// for caching purposes
if (_getInfoOnAllWorkflowsTask != null)
{
_getInfoOnAllWorkflowsTask.UserId = "";
_getInfoOnAllWorkflowsTask.Run();
}
}
else
{
TraceWriter.Warn("HyFlo can't be started. One or more of its dependencies returned a failure!");
}
}
}
else
{
TraceWriter.Warn("A general failure occurred during the Hyflo systemcheck!");
}
var allworkflowStates = new WorkflowState[0];
if (_getInfoOnAllWorkflowsTask != null)
{
if (!_getInfoOnAllWorkflowsTask.Failure && _getInfoOnAllWorkflowsTask.Results is List<WorkflowState>)
{
allworkflowStates = (_getInfoOnAllWorkflowsTask.Results as List<WorkflowState>).ToArray();
}
TraceWriter.Info(
$"Workflowstate retrieval completed. Failure: '{_getInfoOnAllWorkflowsTask.Failure}', Results: '{allworkflowStates.Count()}' workflow{(allworkflowStates.Count() == 1 ? "" : "s")} found ..");
var timeStamp = DateTime.Now;
TraceWriter.Trace("Sending IStartedHyflo # GUI with the following parameters:\n" +
$"\tErrorCode: '{Convert.ToInt32(_systemTestTask?.Results)}'\n" +
$"\tFailure: '{!systemOk}'\n" + $"\tTimeStamp: '{timeStamp}'\n" +
$"\tResulting workflowcount: '{allworkflowStates.Length}'\n");
await session.Publish<IStartedHyflo>(evt =>
{
evt.ErrorCode = Convert.ToInt32(_systemTestTask?.Results);
evt.Failure = !systemOk;
evt.TimeStamp = timeStamp;
evt.Result = new DataBusProperty<WorkflowState[]>(allworkflowStates);
}).ConfigureAwait(false);
cache.Set("SystemTest", systemOk, policy);
}
}
/// <summary>
/// OnStop
/// </summary>
/// <param name="session"></param>
/// <returns></returns>
protected override async Task OnStop(IMessageSession session)
{
TraceWriter.Trace($"==> Running SystemStartupTask.OnStop() ..");
TraceWriter.Info("Deleting heartbeat indicator .. ");
DeleteHeartbeatIndicator();
var timeStamp = DateTime.Now;
TraceWriter.Trace("Sending IShutdownHyflo # GUI with the following parameters:\n" + "\tErrorCode: '0'\n" +
"\tFailure: 'false'\n" + $"\tTimeStamp: '{timeStamp}'");
await session.Publish<IShutdownHyflo>(evt =>
{
evt.ErrorCode = 0;
evt.Failure = false;
evt.TimeStamp = timeStamp;
}).ConfigureAwait(false);
TraceWriter.Info("HyFlo has shutted down .. ");
}
/// <summary>
/// Function deletes the heartbeat.txt file that indicates wether a heartbeat flow is running or not
/// </summary>
private void DeleteHeartbeatIndicator()
{
string stateFolder = ConfigurationManager.AppSettings["StateFolder"];
string fullStateFolder = stateFolder.IndexOf(":", StringComparison.Ordinal) == -1
? $"{AppDomain.CurrentDomain.BaseDirectory}{stateFolder}"
: stateFolder;
string fileName = $#"{fullStateFolder}\heartbeat.txt";
if (File.Exists(fileName))
{
File.Delete(fileName);
}
}
}
}
The GetVersionHandler is the following:
namespace HyFlo.Service.CommandHandlers.Info
{
public class GetVersionHandler : HandlerBase, IHandleMessages<GetVersionCommand>
{
public GetVersionHandler(ISystemTestTask systemTestTask, ITaskBuilderController taskBuilderController)
{
_systemTestTask = systemTestTask;
_taskBuilderController = taskBuilderController;
}
public virtual async Task Handle(GetVersionCommand message, IMessageHandlerContext context)
{
if (message == null)
return;
await RunDirect(async () =>
{
var assemblyVersion = Assembly.GetAssembly(typeof(GetVersionHandler)).GetName().Version;
var reply = new GetVersionReply
{
Failure = false,
Version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}",
TimeStamp = DateTime.Now,
TaskId = CommandEventId,
ErrorCode = 0
};
await Retry.ExecuteAsync(async () =>
{
var replyOptions = new ReplyOptions();
replyOptions.SetHeader("CorrelationId", CorrelationId);
await context.Reply(reply, replyOptions);
});
}, new MessageContextWrapper { HandlerContext = context });
}
}
}
The RunDirect method is located in the HandlerBase class and one of the first things it does is log that a new message is being handled.
public async Task RunDirect(Action codeToRun, IMessageContextWrapper contextWrapper)
{
_taskBuilderController.HandlerBase = this;
CommandEventId = Guid.NewGuid();
ContextWrapper = contextWrapper;
CorrelationId = GetHeaderValue("CorrelationId");
string fullMethodName = GetType().ToString();
TraceWriter.Trace("= New Message =====================================================================================================================================");
TraceWriter.Trace($"CommandEventId:{CommandEventId}: New message handled by handler of type '{fullMethodName}' with CorrelationId '{CorrelationId}' .. ");
TraceWriter.Trace("===================================================================================================================================================");
...
The last thing is the test client I've used to send this GetVersionCommand:
namespace HPS_EndpointTest
{
internal class Program
{
static void Main(string[] args)
{
var endpointConfiguration = new EndpointConfiguration("Hyflo.Client.Test");
endpointConfiguration.EnableInstallers();
endpointConfiguration.UsePersistence<InMemoryPersistence>();
endpointConfiguration.PurgeOnStartup(true);
var transport = endpointConfiguration.UseTransport<RabbitMQTransport>();
endpointConfiguration.UseTransport<RabbitMQTransport>().ConnectionString("host=hswv0601;username=hyflo;password=hyflo");
endpointConfiguration.UseTransport<RabbitMQTransport>().UseDirectRoutingTopology();
IEndpointInstance endpointInstance = null;
AsyncPump.Run(async delegate
{
endpointInstance = await Endpoint.Start(endpointConfiguration).ConfigureAwait(false);
});
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": Client started ..");
var correlationId = Guid.NewGuid();
string destination = "hyflo.service";
var sendOptions = new SendOptions();
sendOptions.SetHeader("CorrelationId", correlationId.ToString());
sendOptions.SetDestination(destination);
sendOptions.RequireImmediateDispatch();
var versionMessage = new HyFlo.Service.Messages.Commands.Info.GetVersionCommand();
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": Sending message ..");
AsyncPump.Run(async delegate
{
if (endpointInstance != null)
{
await endpointInstance.Send(versionMessage, sendOptions).ConfigureAwait(false);
}
});
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": Message sent!");
while (true)
{
var key = Console.ReadKey();
if (key.Key == ConsoleKey.Enter)
{
break;
}
}
AsyncPump.Run(async delegate
{
await endpointInstance.Stop().ConfigureAwait(false);
});
}
}
public class RetrieveAllWorkflowsHandler : IHandleMessages<HyFlo.Service.Messages.Events.IRetrievedAllWorkflowsEvent>
{
public Task Handle(HyFlo.Service.Messages.Events.IRetrievedAllWorkflowsEvent message, IMessageHandlerContext context)
{
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": IRetrievedAllWorkflowEvents reply received ..");
return Task.CompletedTask;
}
}
public class RetrieveVersionHandler : IHandleMessages<GetVersionReply>
{
public Task Handle(GetVersionReply message, IMessageHandlerContext context)
{
Console.WriteLine(DateTime.Now.ToString("dd-MM-yyyyTHH:mm:ss.ffff") + ": GetVersionReply reply received ..");
return Task.CompletedTask;
}
}
}
I just don't understand why this delay is there.
What I'm doing right now is create a completely new endpoint with limited functionality (ie only supporting the GetVersionCommand) and see what happens with that. If the delay isn't there I'll expand support with other commands and see what happens then. Hope any of you guys sees this and slaps him/herself to the head and replying "but you forgot so and so".
Thanks in advance!!
With help of Daniel Marbach I managed to pin point the problem. Because of a complex object graph in one of the classes of the solution (the class that is the primary access point to the solution in fact) the delay occurred. Now when creating either specialised classes or injecting the actual needed dependency there was virtually no delay.
So the final solution was to alter each and every IHandleMessages-handler to have it injected a targeted dependency with which the handler could work.
We're trying to send a message on an IBM MQ Message Queue.
We can contact the Queue Manager, but as soon as we call the MQMessage constructor, the process hangs. We suspect that this is actually because whatever AccessQueue is doing is failing.
using (MQQueueManager qMgr = new MQQueueManager(queueManagerName, connectionProperties))
{
// Set up the options on the queue we want to open
int openOptions = MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING;
// Now specify the queue that we want to open,and the open options
MQQueue system_default_local_queue =
qMgr.AccessQueue(sendQueueName, openOptions); // we suspect this is actually where things are wrong
// Define a WebSphere MQ message, writing some text in UTF format
MQMessage mqMessage = new MQMessage(); // ** THIS HANGS
mqMessage.WriteUTF(text);
// Specify the message options
MQPutMessageOptions pmo = new MQPutMessageOptions(); // accept the defaults,
// same as MQPMO_DEFAULT
// Put the message on the queue
system_default_local_queue.Put(mqMessage, pmo);
system_default_local_queue.Close();
// Disconnect from the queue manager
qMgr.Disconnect();
}
Is there an option we're not including or something we're missing?
We're using the the NuGet package WebSphereMqClient v8.0.0.7.
Update:
I'll include our connection properties for completeness based on feedback.
Hashtable connectionProperties = new Hashtable();
connectionProperties.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
connectionProperties.Add(MQC.USE_MQCSP_AUTHENTICATION_PROPERTY, true);
// Set up the rest of the connection properties, based on the
// connection type requested
// this is all for MQC.TRANSPORT_MQSERIES_MANAGED
connectionProperties.Add(MQC.HOST_NAME_PROPERTY, "myserver.com");
connectionProperties.Add(MQC.CHANNEL_PROPERTY, "MY.CHANNEL");
connectionProperties.Add(MQC.PORT_PROPERTY, 3223);
connectionProperties.Add(MQC.CONNECT_OPTIONS_PROPERTY, MQC.MQCNO_RECONNECT_Q_MGR);
In answer to a question from Roger, this is a Managed Queue.
You don't show us your connection properties, so I don't know if you set everything correctly. Did you make sure you set 'managed mode'? Also, why aren't you doing any print-outs of what the code has completed?
Here is a complete and working C# .NET/MQ program that uses 'managed mode' connection.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using IBM.WMQ;
/// <summary> Program Name
/// MQTest51
///
/// Description
/// This C# class will connect to a remote queue manager
/// and put a message to a queue under a managed .NET environment.
///
/// Sample Command Line Parameters
/// -h 127.0.0.1 -p 1414 -c TEST.CHL -m MQWT1 -q TEST.Q1
///
/// </summary>
namespace MQTest51
{
class MQTest51
{
private Hashtable inParms = null;
private Hashtable qMgrProp = null;
private System.String qManager;
private System.String outputQName;
/*
* The constructor
*/
public MQTest51()
: base()
{
}
/// <summary> Make sure the required parameters are present.</summary>
/// <returns> true/false
/// </returns>
private bool allParamsPresent()
{
bool b = inParms.ContainsKey("-h") && inParms.ContainsKey("-p") &&
inParms.ContainsKey("-c") && inParms.ContainsKey("-m") &&
inParms.ContainsKey("-q");
if (b)
{
try
{
System.Int32.Parse((System.String)inParms["-p"]);
}
catch (System.FormatException e)
{
b = false;
}
}
return b;
}
/// <summary> Extract the command-line parameters and initialize the MQ variables.</summary>
/// <param name="args">
/// </param>
/// <throws> IllegalArgumentException </throws>
private void init(System.String[] args)
{
inParms = Hashtable.Synchronized(new Hashtable());
if (args.Length > 0 && (args.Length % 2) == 0)
{
for (int i = 0; i < args.Length; i += 2)
{
inParms[args[i]] = args[i + 1];
}
}
else
{
throw new System.ArgumentException();
}
if (allParamsPresent())
{
qManager = ((System.String)inParms["-m"]);
outputQName = ((System.String)inParms["-q"]);
qMgrProp = new Hashtable();
qMgrProp.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_MANAGED);
qMgrProp.Add(MQC.HOST_NAME_PROPERTY, ((System.String)inParms["-h"]));
qMgrProp.Add(MQC.CHANNEL_PROPERTY, ((System.String)inParms["-c"]));
try
{
qMgrProp.Add(MQC.PORT_PROPERTY, System.Int32.Parse((System.String)inParms["-p"]));
}
catch (System.FormatException e)
{
qMgrProp.Add(MQC.PORT_PROPERTY, 1414);
}
if (inParms.ContainsKey("-u"))
qMgrProp.Add(MQC.USER_ID_PROPERTY, ((System.String)inParms["-u"]));
if (inParms.ContainsKey("-x"))
qMgrProp.Add(MQC.PASSWORD_PROPERTY, ((System.String)inParms["-x"]));
if ( (inParms.ContainsKey("-u")) && (inParms.ContainsKey("-x")) )
qMgrProp.Add(MQC.USE_MQCSP_AUTHENTICATION_PROPERTY, true);
}
else
{
throw new System.ArgumentException();
}
}
/// <summary> Connect, open queue, write a message, close queue and disconnect.
///
/// </summary>
/// <throws> MQException </throws>
private void testSend()
{
MQQueueManager qMgr = null;
MQQueue outQ = null;
System.String line = "This is a test message embedded in the MQTest51 program.";
int openOptions = MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING;
MQPutMessageOptions pmo = new MQPutMessageOptions();
try
{
qMgr = new MQQueueManager(qManager, qMgrProp);
System.Console.Out.WriteLine("MQTest51 successfully connected to " + qManager);
outQ = qMgr.AccessQueue(outputQName, openOptions);
System.Console.Out.WriteLine("MQTest51 successfully opened " + outputQName);
// Define a simple MQ message, and write some text in UTF format..
MQMessage sendmsg = new MQMessage();
sendmsg.Format = MQC.MQFMT_STRING;
sendmsg.MessageType = MQC.MQMT_DATAGRAM;
sendmsg.MessageId = MQC.MQMI_NONE;
sendmsg.CorrelationId = MQC.MQCI_NONE;
sendmsg.WriteString(line);
// put the message on the outQ
outQ.Put(sendmsg, pmo);
System.Console.Out.WriteLine("Message Data>>>" + line);
}
catch (MQException mqex)
{
System.Console.Out.WriteLine("MQTest51 CC=" + mqex.CompletionCode + " : RC=" + mqex.ReasonCode);
}
catch (System.IO.IOException ioex)
{
System.Console.Out.WriteLine("MQTest51 ioex=" + ioex);
}
finally
{
try
{
if (outQ != null)
{
outQ.Close();
System.Console.Out.WriteLine("MQTest51 closed: " + outputQName);
}
}
catch (MQException mqex)
{
System.Console.Out.WriteLine("MQTest51 CC=" + mqex.CompletionCode + " : RC=" + mqex.ReasonCode);
}
try
{
if (qMgr != null)
{
qMgr.Disconnect();
System.Console.Out.WriteLine("MQTest51 disconnected from " + qManager);
}
}
catch (MQException mqex)
{
System.Console.Out.WriteLine("MQTest51 CC=" + mqex.CompletionCode + " : RC=" + mqex.ReasonCode);
}
}
}
/// <summary> main line</summary>
/// <param name="args">
/// </param>
// [STAThread]
public static void Main(System.String[] args)
{
MQTest51 write = new MQTest51();
try
{
write.init(args);
write.testSend();
}
catch (System.ArgumentException e)
{
System.Console.Out.WriteLine("Usage: MQTest51 -h host -p port -c channel -m QueueManagerName -q QueueName [-u userID] [-x passwd]");
System.Environment.Exit(1);
}
catch (MQException e)
{
System.Console.Out.WriteLine(e);
System.Environment.Exit(1);
}
System.Environment.Exit(0);
}
}
}
I have a data source, filter, and a filtered repeater that I have created custom filters for.
For some reason, both my filters work without the other on the screen. One works fine with the other filter on the screen.
The last filter refuses to work when I have the other filter on the screen but works fine without anything else.
I am pretty sure it has something to do with my code behind files which I will put below.
FILTER#1
using CMS.DocumentEngine;
using CMS.Helpers;
using System;
using System.Web;
using System.Web.UI.WebControls;
using CMS.DocumentEngine.Web.UI;
public partial class CMSGlobalFiles_SectorFilterControl : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Sets up the inner child controls.
/// </summary>
private void SetupControl()
{
// Hides the filter if StopProcessing is enabled
if (this.StopProcessing)
{
this.Visible = false;
}
// Initializes only if the current request is NOT a postback
else if (!RequestHelper.IsPostBack())
{
// Loads product departments as filtering options
InitializeClientSectors();
}
}
/// <summary>
/// Loads all existing product departments as filtering options into the department drop-down list.
/// </summary>
private void InitializeClientSectors()
{
// Adds the default '(all)' option
this.drpSector.Items.Insert(0, new ListItem("(all)", "##ALL##"));
var clientSectors = DocumentHelper.GetDocuments("BBUS.Sector")
.Path("/Sector/", PathTypeEnum.Children)
.OnSite("Balfour-dev.allata.com");
if (!DataHelper.DataSourceIsEmpty(clientSectors))
{
int count = 1;
foreach (var clientSector in clientSectors)
{
var ClientSectorID = clientSector.GetValue("SectorID").ToString();
this.drpSector.Items.Insert(count++, new ListItem(clientSector.DocumentName, ClientSectorID));
}
}
}
/// <summary>
/// Generates a WHERE condition and ORDER BY clause based on the current filtering selection.
/// </summary>
private void SetFilter()
{
string where = null;
// Generates a WHERE condition based on the selected product department
if (this.drpSector.SelectedIndex > 0 && this.drpSector.SelectedValue != null)
{
//where = string.Format("clientSector = {0}", this.drpClientSector.SelectedValue);
where = string.Format(
"sector LIKE '%|{0}|%' " +
"OR sector LIKE '{0}|%' " +
"OR sector LIKE '%|{0}' " +
"OR sector = '{0}'", this.drpSector.SelectedValue);
}
if (where != null)
{
// Sets the Where condition
this.WhereCondition = where;
}
// Raises the filter changed event
this.RaiseOnFilterChanged();
}
/// <summary>
/// Init event handler.
/// </summary>
protected override void OnInit(EventArgs e)
{
// Creates the child controls
SetupControl();
base.OnInit(e);
}
/// <summary>
/// PreRender event handler
/// </summary>
protected override void OnPreRender(EventArgs e)
{
var ClientSectorID = HttpContext.Current.Request.QueryString.Get("SectorID");
// Checks if the current request is a postback
if (RequestHelper.IsPostBack())
{
// Applies the filter to the displayed data
SetFilter();
}
else if (!string.IsNullOrEmpty(ClientSectorID))
{
this.drpSector.SelectedIndex = this.drpSector.Items.IndexOf(this.drpSector.Items.FindByValue(ClientSectorID));
SetFilter();
}
base.OnPreRender(e);
}
protected void btnFilter_Click(object sender, EventArgs e)
{
// Remove Query Strings
string url = Request.RawUrl.Split(new[] { '?' })[0];
// Add ClientSectorID Query String
string updatedQueryString = "?" + "SectorID=" + this.drpSector.SelectedValue;
Response.Redirect(url + updatedQueryString);
}
}
FILTER#2
using CMS.DocumentEngine;
using CMS.Helpers;
using System;
using System.Web;
using System.Web.UI.WebControls;
using CMS.DocumentEngine.Web.UI;
public partial class CMSGlobalFiles_SectorFilterControl : CMSAbstractDataFilterControl
{
protected void Page_Load(object sender, EventArgs e)
{
}
/// <summary>
/// Sets up the inner child controls.
/// </summary>
private void SetupControl()
{
// Hides the filter if StopProcessing is enabled
if (this.StopProcessing)
{
this.Visible = false;
}
// Initializes only if the current request is NOT a postback
else if (!RequestHelper.IsPostBack())
{
// Loads product departments as filtering options
InitializeClientSectors();
}
}
/// <summary>
/// Loads all existing product departments as filtering options into the department drop-down list.
/// </summary>
private void InitializeClientSectors()
{
// Adds the default '(all)' option
this.drpSector.Items.Insert(0, new ListItem("(all)", "##ALL##"));
var clientSectors = DocumentHelper.GetDocuments("BBUS.Sector")
.Path("/Sector/", PathTypeEnum.Children)
.OnSite("Balfour-dev.allata.com");
if (!DataHelper.DataSourceIsEmpty(clientSectors))
{
int count = 1;
foreach (var clientSector in clientSectors)
{
var ClientSectorID = clientSector.GetValue("SectorID").ToString();
this.drpSector.Items.Insert(count++, new ListItem(clientSector.DocumentName, ClientSectorID));
}
}
}
/// <summary>
/// Generates a WHERE condition and ORDER BY clause based on the current filtering selection.
/// </summary>
private void SetFilter()
{
string where = null;
// Generates a WHERE condition based on the selected product department
if (this.drpSector.SelectedIndex > 0 && this.drpSector.SelectedValue != null)
{
//where = string.Format("clientSector = {0}", this.drpClientSector.SelectedValue);
where = string.Format(
"sector LIKE '%|{0}|%' " +
"OR sector LIKE '{0}|%' " +
"OR sector LIKE '%|{0}' " +
"OR sector = '{0}'", this.drpSector.SelectedValue);
}
if (where != null)
{
// Sets the Where condition
this.WhereCondition = where;
}
// Raises the filter changed event
this.RaiseOnFilterChanged();
}
/// <summary>
/// Init event handler.
/// </summary>
protected override void OnInit(EventArgs e)
{
// Creates the child controls
SetupControl();
base.OnInit(e);
}
/// <summary>
/// PreRender event handler
/// </summary>
protected override void OnPreRender(EventArgs e)
{
var ClientSectorID = HttpContext.Current.Request.QueryString.Get("SectorID");
// Checks if the current request is a postback
if (RequestHelper.IsPostBack())
{
// Applies the filter to the displayed data
SetFilter();
}
else if (!string.IsNullOrEmpty(ClientSectorID))
{
this.drpSector.SelectedIndex = this.drpSector.Items.IndexOf(this.drpSector.Items.FindByValue(ClientSectorID));
SetFilter();
}
base.OnPreRender(e);
}
protected void btnFilter_Click(object sender, EventArgs e)
{
// Remove Query Strings
string url = Request.RawUrl.Split(new[] { '?' })[0];
// Add ClientSectorID Query String
string updatedQueryString = "?" + "SectorID=" + this.drpSector.SelectedValue;
Response.Redirect(url + updatedQueryString);
}
}
Sadly this is a little bit of a limitation, you can only have 1 filter per Repeater/Data Source (except Smart Search it seems, which can handle multiple).
You will most likely need to combine both of your filters into 1 filter, and combine the logics into one where condition.
https://docs.kentico.com/k10/developing-websites/loading-and-displaying-data-on-websites/filtering-and-paging-data
Would be great to allow multiple filters though!
I encapsulated the odata container because of some extensions:
public class Connector
{
public string apiKey{get;set;}
public Connector(string apiKey)
{
this.apiKey = apiKey;
}
public Default.Container Get()
{
Default.Container container = new Default.Container(new Uri("http://documents.omnisoftonline.be/odata"));
container.BuildingRequest += container_BuildingRequest;
return container;
}
/// <summary>
/// When fetching a single Document, you can enable this method to including fetch the binary data
/// </summary>
/// <param name="container"></param>
/// <returns></returns>
internal bool doIO = false;
internal void container_BuildingRequest(object sender, BuildingRequestEventArgs e)
{
e.Headers.Add("X-ApiKey", apiKey);
if (doIO && e.RequestUri.ToString().Contains("/Documents"))
{
e.RequestUri = new Uri(e.RequestUri.ToString() + (e.RequestUri.Query.Contains("?") ? "&" : "?") + "doIO=true");
}
this.container_BuildingRequest(sender, e);
}
}
When i use my .dll ( using the Connector class), i have an empty result ( statuscode = -1, no headers, ...)
This is how i call the DLL
documentsConnector.Get().AddToDocuments(docToCreate);
var serviceResponse = documentsConnector.Get().SaveChanges(Microsoft.OData.Client.SaveChangesOptions.None); //tried several options
foreach(var operationResponse in serviceResponse)
{
Console.WriteLine("Response: {0}", operationResponse.StatusCode); //no operationResponses, so this isn't executed
}
It could be because my object isn't valid. But it's weird that i don't see any validation happening...
Any thoughts on how to propagate the SaveChanges() or pre-validate ( before submit) the Entity? The post isn't happening ( checked with Fiddler)
My wrapper class created a new container every time, so the entities got deleted from the Default.Container
The latest Android Wear update comes with support for ChannelApi that can be used for sending files to/from wearable or handheld. The problem is I cannot find a single sample of how to use this functionality. The Android samples doesn't include this feature. So if anyone knows how to use the sendFile/receiveFile and can give a quick example here it would be appreciated.
Take a look on this answer to know how to use the Channel API to create the channel between the devices.
After you create the googleClient and retrive the nodeId of the device you want to send the file to, basically you can use the following code on the wearable side:
//opening channel
ChannelApi.OpenChannelResult result = Wearable.ChannelApi.openChannel(googleClient, nodeId, "/mypath").await();
channel = result.getChannel();
//sending file
channel.sendFile(googleClient, Uri.fromFile(file));
Then, on the handheld device:
//receiving the file
#Override
public void onChannelOpened(Channel channel) {
if (channel.getPath().equals("/mypath")) {
file = new File("/sdcard/file.txt");
try {
file.createNewFile();
} catch (IOException e) {
//handle error
}
channel.receiveFile(mGoogleApiClient, Uri.fromFile(file), false);
}
}
//when file is ready
#Override
public void onInputClosed(Channel channel, int i, int i1) {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "File received!", Toast.LENGTH_SHORT).show();
}
});
}
If you need more information about this, please visit the reference site from Google
This is just an add to the answer: also check your WearableListenerService in androidmanifest. It's intent filter should contain the com.google.android.gms.wearable.CHANNEL_EVENT action.
I have used some code like this with success. Transfers can be fairly slow.
Both the handheld and wearable applications MUST HAVE the same applicationId in their gradle files.
The wearable needs a manifest entry something like this
<service
android:name="com.me.myWearableListenerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<!-- listeners receive events that match the action and data filters -->
<action android:name="com.google.android.gms.wearable.CHANNEL_EVENT"/>
<data android:scheme="wear" android:host="*" android:pathPrefix="/MyAppPath" />
</intent-filter>
</service>
to launch its WearableListenerService when the Handheld sends a file.
private static final String WEARABLE_FILE_COPY = "MyAppPath/FileCopy";
private void copyFileToWearable (final File file, final String nodeId, Context ctx) {
new Thread(new Runnable() {
#Override
public void run() {
final ChannelClient cc = Wearable.getChannelClient(ctx);
ChannelClient.ChannelCallback ccb = new ChannelClient.ChannelCallback() {
#Override
public void onChannelClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onChannelClosed(channel, i, i1);
Log.d(TAG, "copyFileToWearable " + channel.getNodeId() + " onChannelClosed ");
cc.unregisterChannelCallback(this);
}
#Override
public void onOutputClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onOutputClosed(channel, i, i1);
Log.d(TAG, "copyFileToWearable " + channel.getNodeId() + " onOutputClosed ");
cc.unregisterChannelCallback(this);
// this is transfer success callback ...
}
};
ChannelClient.Channel c;
Log.d(TAG, "copyFileToWearable transfer file " + file.getName() +
" size:" + file.length()/1000000 + "Mb");
try {
// send the filename to the wearable with the channel open
c = Tasks.await(cc.openChannel(nodeId, WEARABLE_FILE_COPY + "/" + file.getName()));
Log.d(TAG, "copyFileToWearable channel opened to " + nodeId);
Log.d(TAG, "copyFileToWearable register callback");
Tasks.await(cc.registerChannelCallback(c, ccb));
Log.d(TAG, "copyFileToWearable sending file " + file.getName());
Tasks.await(cc.sendFile(c, Uri.fromFile(file)));
// completion is indicated by onOutputClosed
} catch (Exception e) {
Log.w(TAG, "copyFileToWearable exception " + e.getMessage());
cc.unregisterChannelCallback(ccb);
// failure
}
}
}).start();
}
call this from onChannelOpened in a WearableListenerService when c.getPath() starts with WEARABLE_FILE_COPY
private void receiveFileFromHandheld(final ChannelClient.Channel c, File myStorageLocation, Context ctx) {
// filename sent by the handheld is at the end of the path
String[] bits = c.getPath().split("\\/");
// store in a suitable spot
final String receivedFileName = myStorageLocation.getAbsolutePath() + "/" + bits[bits.length-1];
new Thread(new Runnable() {
#Override
public void run() {
final ChannelClient cc = Wearable.getChannelClient(ctx);
ChannelClient.ChannelCallback ccb = new ChannelClient.ChannelCallback() {
boolean mClosed = false;
#Override
public void onChannelClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onChannelClosed(channel, i, i1);
Log.d(TAG, "receiveFileFromHandheld " + channel.getNodeId() + " onChannelClosed ");
if (!mClosed){
// failure ...
}
}
#Override
public void onInputClosed(#NonNull ChannelClient.Channel channel, int i, int i1) {
super.onInputClosed(channel, i, i1);
Log.d(TAG, "receiveFileFromHandheld " + channel.getNodeId() + " onInputClosed ");
long fs = new File(receivedFileName).length();
Log.d(TAG, "receiveFileFromHandheld got " + receivedFileName +
" size:" + fs / 1000000 + "Mb");
cc.unregisterChannelCallback(this);
mClosed = true;
// success !
}
};
try {
Log.d(TAG, "receiveFileFromHandheld register callback");
Tasks.await(cc.registerChannelCallback(c, ccb));
Log.d(TAG, "receiveFileFromHandheld receiving file " + receivedFileName);
Tasks.await(cc.receiveFile(c, Uri.fromFile(new File(receivedFileName)), false));
// completion is indicated by onInputClosed
} catch (Exception e) {
Log.w(TAG, "receiveFileFromHandheld exception " + e.getMessage());
cc.unregisterChannelCallback(ccb);
// failure ...
}
}
}
).start();
}