How do I resolve: Akka.Remote.EndpointDisassociatedException? - f#

I have some code that involves remote deploying actors onto a separate process.
I am getting: Akka.Remote.EndpointDisassociatedException
[WARNING][3/24/2017 1:54:32 PM][Thread
0008][[akka://system1/system/endpointMana
ger/reliableEndpointWriter-akka.tcp%3A%2F%2Fsystem2%40localhost%3A8080-1#1408457
663]] Association with remote system akka.tcp://system2#localhost:8080
has faile d; address is now gated for 5000 ms. Reason is:
[Akka.Remote.EndpointDisassociat edException: Disassociated at
Akka.Remote.EndpointWriter.PublishAndThrow(Exception reason, LogLevel
leve l, Boolean needToThrow) at
Akka.Actor.ReceiveActor.ExecutePartialMessageHandler(Object message,
Parti alAction1 partialAction) at
Akka.Actor.ActorCell.<>c__DisplayClass114_0.<Akka.Actor.IUntypedActorConte
xt.Become>b__0(Object m) at
Akka.Actor.ActorBase.AroundReceive(Receive receive, Object message)
at Akka.Actor.ActorCell.ReceiveMessage(Object message) at
Akka.Actor.ActorCell.AutoReceiveMessage(Envelope envelope) at
Akka.Actor.ActorCell.Invoke(Envelope envelope)] [ERROR][3/24/2017
1:54:32 PM][Thread 0008][akka://system1/system/endpointManager
/reliableEndpointWriter-akka.tcp%3A%2F%2Fsystem2%40localhost%3A8080-1/endpointWr
iter] Disassociated Cause: Akka.Remote.EndpointDisassociatedException:
Disassociated at
Akka.Remote.EndpointWriter.PublishAndThrow(Exception reason, LogLevel
leve l, Boolean needToThrow) at
Akka.Actor.ReceiveActor.ExecutePartialMessageHandler(Object message,
Parti alAction1 partialAction) at
Akka.Actor.ActorCell.<>c__DisplayClass114_0.b__0(Object m) at
Akka.Actor.ActorBase.AroundReceive(Receive receive, Object message)
at Akka.Actor.ActorCell.ReceiveMessage(Object message) at
Akka.Actor.ActorCell.AutoReceiveMessage(Envelope envelope) at
Akka.Actor.ActorCell.Invoke(Envelope envelope)
Here's the code that I execute in a separate process that triggers that error:
use system = ActorSystem.Create("system1", config)
let reply = system.ActorOf<ReplyActor>("reply")
let props1 = Props.Create(typeof<SomeActor>, [||])
let props2 = Props.Create(typeof<SomeActor>, [||])
let props3 = Props.Create(typeof<SomeActor>, [||])
let remote1 = system.ActorOf(props1.WithRouter(FromConfig.Instance), "remoteactor1")
let remote2 = system.ActorOf(props2.WithRouter(FromConfig.Instance), "remoteactor2")
let remote3 = system.ActorOf(props3.WithRouter(FromConfig.Instance), "remoteactor3")
let hashGroup = system.ActorOf(Props.Empty.WithRouter(ConsistentHashingGroup(config)))
Task.Delay(500).Wait();
let routee1 = Routee.FromActorRef(remote1);
hashGroup.Tell(new AddRoutee(routee1));
let routee2 = Routee.FromActorRef(remote2);
hashGroup.Tell(new AddRoutee(routee2));
let routee3 = Routee.FromActorRef(remote3);
hashGroup.Tell(new AddRoutee(routee3));
Task.Delay(500).Wait();
for i = 0 to 5 do
for j = 0 to 7 do
let message = new HashMessage(j, sprintf "remote message: %i" j);
hashGroup.Tell(message, reply);
Console.ReadLine() |> ignore
Here's the configuration that my remote deploy code relies on:
open Akka.Configuration
let config = ConfigurationFactory.ParseString(#"
akka {
log-config-on-start = on
stdout-loglevel = DEBUG
loglevel = DEBUG
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
debug {
receive = on
autoreceive = on
lifecycle = on
event-stream = on
unhandled = on
}
deployment {
/localactor {
router = consistent-hashing-pool
nr-of-instances = 5
virtual-nodes-factor = 10
}
/remoteactor1 {
router = consistent-hashing-pool
nr-of-instances = 5
remote = ""akka.tcp://system2#localhost:8080""
}
/remoteactor2 {
router = consistent-hashing-pool
nr-of-instances = 5
remote = ""akka.tcp://system2#localhost:8080""
}
/remoteactor3 {
router = consistent-hashing-pool
nr-of-instances = 5
remote = ""akka.tcp://system2#localhost:8080""
}
}
}
remote {
helios.tcp {
port = 8090
hostname = localhost
}
}
}
")
Here's the C# code that actually works that my F# implementation is based off:
var config = ConfigurationFactory.ParseString(#"
akka {
log-config-on-start = on
stdout-loglevel = DEBUG
loglevel = DEBUG
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
debug {
receive = on
autoreceive = on
lifecycle = on
event-stream = on
unhandled = on
}
deployment {
/localactor {
router = consistent-hashing-pool
nr-of-instances = 5
virtual-nodes-factor = 10
}
/remoteactor1 {
router = consistent-hashing-pool
nr-of-instances = 5
remote = ""akka.tcp://system2#localhost:8080""
}
/remoteactor2 {
router = consistent-hashing-pool
nr-of-instances = 5
remote = ""akka.tcp://system2#localhost:8080""
}
/remoteactor3 {
router = consistent-hashing-pool
nr-of-instances = 5
remote = ""akka.tcp://system2#localhost:8080""
}
}
}
remote {
dot-netty.tcp {
port = 8090
hostname = localhost
}
}
}
");
using (var system = ActorSystem.Create("system1", config))
{
var reply = system.ActorOf<ReplyActor>("reply");
//create a remote deployed actor
var remote1 = system.ActorOf(Props.Create(() => new SomeActor(null, 123)).WithRouter(FromConfig.Instance), "remoteactor1");
var remote2 = system.ActorOf(Props.Create(() => new SomeActor(null, 456)).WithRouter(FromConfig.Instance), "remoteactor2");
var remote3 = system.ActorOf(Props.Create(() => new SomeActor(null, 789)).WithRouter(FromConfig.Instance), "remoteactor3");
var hashGroup = system.ActorOf(Props.Empty.WithRouter(new ConsistentHashingGroup(config)));
Task.Delay(500).Wait();
var routee1 = Routee.FromActorRef(remote1);
hashGroup.Tell(new AddRoutee(routee1));
var routee2 = Routee.FromActorRef(remote2);
hashGroup.Tell(new AddRoutee(routee2));
var routee3 = Routee.FromActorRef(remote3);
hashGroup.Tell(new AddRoutee(routee3));
Task.Delay(500).Wait();
for (var i = 0; i < 5; i++)
{
for (var j = 0; j < 7; j++)
{
var message = new SomeMessage(j, $"remote message: {j}");
hashGroup.Tell(message, reply);
}
}
Console.ReadLine();
}
}
}
}
Can anyone provide guidance on why I'm getting this exception and how I can resolve it?
Hence, the F# implementation closely mirrors the working C# implementation.
The F# code can be found on GitHub.

When you're starting your application, you may read an exact exception that causes node disassociation: Could not load file or assembly 'System1....
What you've defined in your routers configuration is remote deployment. This means, that from one system you're trying to create actors on another node and communicate with them as if they were available locally. While this is possible, there is one requirement: a destination actor system must know how to build an actor. Since your actors are defined in System1 and created in System2, which doesn't know anything about SomeActor it fails and causes actor system to disassociate.
You need to pass SomeActor class to shared assembly, available for both systems, in order for your scenario to work.

Related

F# Akkling Unable to send message through sharding proxy

When I try to send a message to the akka.net region proxy with the following code,
open Akkling.Cluster.Sharding
open Akka.Actor
open Akka.Cluster
open Akka.Cluster.Sharding
open System
open Akkling
let configWithPort (port:int) =
let config = Configuration.parse ("""
akka {
actor {
provider = cluster
}
remote {
dot-netty.tcp {
public-hostname = "localhost"
hostname = "localhost"
port = """ + port.ToString() + """
}
}
cluster {
roles = ["Worker"]
sharding {
journal-plugin-id = "akka.persistence.journal.inmem"
snapshot-plugin-id = "akka.persistence.snapshot-store.inmem"
}
seed-nodes = [ "akka.tcp://cluster-system#localhost:5000" ]
}
}
""")
config
.WithFallback(Akka.Cluster.Tools.Singleton.ClusterSingletonManager.DefaultConfig())
.WithFallback(ClusterSharding.DefaultConfig())
let system1 = ActorSystem.Create("cluster-system", configWithPort 5000)
let system2 = ActorSystem.Create("cluster-system", configWithPort 5001)
/// Domain
type FileCommand = {
ProgramId : string
Duration : TimeSpan
FilePath : string
}
/// Actors
let aggregateRootActor (mailbox:Actor<_>) (msg:FileCommand) =
let nodeAddress = Cluster.Get(mailbox.System).SelfUniqueAddress
logInfof mailbox "Program: [%s] with path [%s] on [%A]" msg.ProgramId msg.FilePath nodeAddress
ignored ()
let extractorFunction (message:FileCommand) =
let entityId = message.ProgramId
let hash = entityId.GetHashCode()
let numberOfShards = 5
let shardId = sprintf "shard_%d" ((abs hash) % numberOfShards)
shardId, entityId, message
let region1 = spawnSharded extractorFunction system1 "fileRouter" (props (actorOf2 aggregateRootActor))
let region2 = spawnSharded extractorFunction system2 "fileRouter" (props (actorOf2 aggregateRootActor))
let shardRegionProxy =
spawnShardedProxy extractorFunction system1 "fileRouterProxy" None
And sending message to the proxy always failed.
shardRegionProxy <! { ProgramId = "a"; Duration = TimeSpan.FromMinutes 10.; FilePath = "\\a_1.mp4" } //this failed
The error message is
> [INFO][8/26/2020 5:13:15 PM][Thread 0027][akka://cluster-system/system/sharding/fileRouterProxyCoordinator/singleton/coordinator] Message [RegisterProxy] from akka://cluster-system/system/sharding/fileRouterProxyProxy to akka://cluster-system/system/sharding/fileRouterProxyCoordinator/singleton/coordinator was not delivered. [6] dead letters encountered. If this is not an expected behavior then akka://cluster-system/system/sharding/fileRouterProxyCoordinator/singleton/coordinator may have terminated unexpectedly. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
However these sends are successful.
region1 <! { ProgramId = "d"; Duration = TimeSpan.FromMinutes 8.; FilePath = "\\a_2.mp4" }
region2 <! { ProgramId = "a"; Duration = TimeSpan.FromMinutes 10.; FilePath = "\\a_1.mp4" }
Excuse me,
How do I correctly create the shardingcoordinator?
Or if it was incorrect, what's the problem using shardingcoordinator like this?
The name was wrong, change the code like this, and everything is FINE!
let shardRegionProxy = spawnShardedProxy extractorFunction system1 "fileRouter" (Some "Worker")

Prosody muc chat history is not complete

I try to make conference chat using prosody, and for the client I use strophe.js. Everything works great except one thing that the chat history shown to user when they just join in to the room is not complete. For example:
One client has already sent messages to the room like this:
1
2
3
4
5
6
7
8
9
10
But when new client join in to the room, they only get messages like this:
1
3
5
7
9
I try to set max_history_messages = 10 in prosody config, and set maxstanzas = 10 from client. But still the same.
Here is my config file
admins = { "agent#localhost" }
modules_enabled = {
"message_logging";
"roster";
"saslauth";
"tls";
"dialback";
"disco";
"private";
"vcard";
"version";
"uptime";
"time";
"ping";
"pep";
"register";
"admin_adhoc";
"admin_telnet";
"bosh";
"posix";
};
bosh_ports = { 5280 }
bosh_max_inactivity = 60
consider_bosh_secure = true
cross_domain_bosh = true
http_paths = {
bosh = "/http-bind"; -- Serve BOSH at /http-bind
files = "/"; -- Serve files from the base URL
}
allow_registration = true;
daemonize = true;
pidfile = "/var/run/prosody/prosody.pid";
ssl = {
key = "/etc/prosody/certs/localhost.key";
certificate = "/etc/prosody/certs/localhost.crt";
}
c2s_require_encryption = false
s2s_secure_auth = false
authentication = "internal_plain"
log = {
info = "/var/log/prosody/prosody.log";
error = "/var/log/prosody/prosody.err";
{ levels = { "error" }; to = "syslog"; };
}
VirtualHost "localhost"
enabled = true -- Remove this line to enable this host
ssl = {
key = "/etc/prosody/certs/localhost.key";
certificate = "/etc/prosody/certs/localhost.crt";
}
Component "conference.localhost" "muc"
restrict_room_creation = true
max_history_messages = 10
Include "conf.d/*.cfg.lua"
Is there something need to set in config?
Here's how I handle message in Strophe.js:
function onLoginComplete(status) {
console.log(status);
if (status == Strophe.Status.CONNECTING) {
console.log('Strophe is connecting.');
} else if (status == Strophe.Status.CONNFAIL) {
console.log('Strophe failed to connect.');
} else if (status == Strophe.Status.DISCONNECTING) {
console.log('Strophe is disconnecting.');
} else if (status == Strophe.Status.DISCONNECTED) {
console.log('Strophe is disconnected.');
} else if (status == Strophe.Status.CONNECTED) {
console.log('Strophe is connected.');
connection.addHandler(onMessage, null, 'message', null, null, null);
if (!chat_room) {
// join to chatroom
}
}
/**
* on new message handler
**/
function onMessage(message) {
console.log(message);
var type = $(message).attr('type');
var body = $(message).find('body').text();
switch (type) {
case 'groupchat':
console.log(body);
// todo append message to the list
appendMessage(message);
break;
}
return true;
}
Here's one message of the history when user just join the room:
<message xmlns="jabber:client" type="groupchat" to="subkhan#localhost/edff55f2-2980-4d01-bf65-0d2c0b011845" from="test#conference.localhost/subkhan"><body>8</body><delay xmlns="urn:xmpp:delay" stamp="2017-04-12T02:54:48Z"></delay><x xmlns="jabber:x:delay" stamp="20170412T02:54:48"></x></message>
Is there something to do with the delay?
Thank you in advance.
Turns out, all this time client already gets all the complete messages history, except it goes to rawInput(data), not to onMessage() handler. So I just remove the handler and handle the incoming message through rawInput(data).

I receive errors with my F# implementation of SimpleClusterListener

I have observed the following error on my F# implementation of SimpleClusterListener:
[ERROR][3/20/2017 11:32:53 AM][Thread
0008][[akka://ClusterSystem/system/endpoin
tManager/reliableEndpointWriter-akka.tcp%3A%2F%2FClusterSystem%400.0.0.0%3A2552-
5/endpointWriter#1522364225]] Dropping message
[Akka.Actor.ActorSelectionMessage ] for non-local recipient
[[akka.tcp://ClusterSystem#localhost:2552/]] arriving at
[akka.tcp://ClusterSystem#localhost:2552] inbound addresses
[akka.tcp://Clust erSystem#0.0.0.0:2552]
I ran the C# implementation (referenced in the Appendix below) with no issues. In addition, I am using the same ports that the C# implementation is using.
NOTE:
I'm new to Akka.Net and as a result, am struggling to troubleshoot where I went wrong with the example I attempted to port.
My implementation is as follows:
Main.fs
module Program
open System
open System.Configuration
open Akka.Configuration.Hocon
open Akka.Configuration
open Akka.Actor
open Samples.Cluster.Simple
[<Literal>]
let ExitWithSuccess = 0
let createActor port =
let section = ConfigurationManager.GetSection "akka" :?> AkkaConfigurationSection
let config = ConfigurationFactory.ParseString("akka.remote.dot-netty.tcp.port=" + port)
.WithFallback(section.AkkaConfig)
let system = ActorSystem.Create ("ClusterSystem", config)
let actorRef = Props.Create(typeof<SimpleClusterListener>)
system.ActorOf(actorRef, "clusterListener") |> ignore
let startUp (ports:string list) = ports |> List.iter createActor
[<EntryPoint>]
let main args =
startUp ["2551"; "2552"; "0"]
Console.WriteLine("Press any key to exit")
Console.ReadLine() |> ignore
ExitWithSuccess
SimpleClusterListener.fs
namespace Samples.Cluster.Simple
open Akka.Actor
open Akka.Cluster
open Akka.Event
type SimpleClusterListener() =
inherit UntypedActor()
override this.PreStart() =
let cluster = Cluster.Get(UntypedActor.Context.System)
let (events:System.Type array) = [| typeof<ClusterEvent.IMemberEvent>
typeof<ClusterEvent.UnreachableMember> |]
cluster.Subscribe(base.Self, ClusterEvent.InitialStateAsEvents, events)
override this.OnReceive(message:obj) =
let log = UntypedActor.Context.GetLogger()
match message with
| :? ClusterEvent.MemberUp as e -> log.Info("Member is up: {0}", e.Member)
| :? ClusterEvent.UnreachableMember as e -> log.Info("Member detected as unreachable: {0}", e.Member)
| :? ClusterEvent.MemberRemoved as e -> log.Info("Member is removed: {0}", e.Member)
| _ -> ()
override this.PostStop() =
let cluster = Akka.Cluster.Cluster.Get(UntypedActor.Context.System)
cluster.Unsubscribe base.Self
The OnReceive method above never gets invoked. However, the PreStart method does.
Appendix:
As stated earlier, I ported the C# implementation below. I successfully ran this code. Thus, I am confused as to where I went wrong when I attempted to port it.
//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Akka.NET Project">
// Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2016 Akka.NET project <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------
using Akka.Actor;
using Akka.Configuration;
using Akka.Configuration.Hocon;
using System;
using System.Configuration;
namespace Samples.Cluster.Simple
{
class Program
{
static void Main(string[] args)
{
StartUp(args.Length == 0 ? new String[] { "2551", "2552", "0" } : args);
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
public static void StartUp(string[] ports)
{
var section = (AkkaConfigurationSection)ConfigurationManager.GetSection("akka");
foreach (var port in ports)
{
//Override the configuration of the port
var config =
ConfigurationFactory.ParseString("akka.remote.dot-netty.tcp.port=" + port)
.WithFallback(section.AkkaConfig);
//create an Akka system
var system = ActorSystem.Create("ClusterSystem", config);
//create an actor that handles cluster domain events
system.ActorOf(Props.Create(typeof(SimpleClusterListener)), "clusterListener");
}
}
}
}
//-----------------------------------------------------------------------
// <copyright file="SimpleClusterListener.cs" company="Akka.NET Project">
// Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2016 Akka.NET project <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------
using Akka.Actor;
using Akka.Cluster;
using Akka.Event;
namespace Samples.Cluster.Simple
{
public class SimpleClusterListener : UntypedActor
{
protected ILoggingAdapter Log = Context.GetLogger();
protected Akka.Cluster.Cluster Cluster = Akka.Cluster.Cluster.Get(Context.System);
/// <summary>
/// Need to subscribe to cluster changes
/// </summary>
protected override void PreStart() =>
Cluster.Subscribe(Self, ClusterEvent.InitialStateAsEvents, new[] { typeof(ClusterEvent.IMemberEvent), typeof(ClusterEvent.UnreachableMember) });
/// <summary>
/// Re-subscribe on restart
/// </summary>
protected override void PostStop() => Cluster.Unsubscribe(Self);
protected override void OnReceive(object message)
{
var up = message as ClusterEvent.MemberUp;
if (up != null)
{
var mem = up;
Log.Info("Member is Up: {0}", mem.Member);
}
else if (message is ClusterEvent.UnreachableMember)
{
var unreachable = (ClusterEvent.UnreachableMember)message;
Log.Info("Member detected as unreachable: {0}", unreachable.Member);
}
else if (message is ClusterEvent.MemberRemoved)
{
var removed = (ClusterEvent.MemberRemoved)message;
Log.Info("Member is Removed: {0}", removed.Member);
}
else if (message is ClusterEvent.IMemberEvent)
{
//IGNORE
}
else if (message is ClusterEvent.CurrentClusterState)
{
}
else
{
Unhandled(message);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="akka" type="Akka.Configuration.Hocon.AkkaConfigurationSection, Akka"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
<akka>
<hocon>
<![CDATA[
akka {
actor {
provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"
}
remote {
log-remote-lifecycle-events = DEBUG
dot-netty.tcp {
hostname = "localhost"
port = 0
}
}
cluster {
seed-nodes = [
"akka.tcp://ClusterSystem#localhost:2551",
"akka.tcp://ClusterSystem#localhost:2552"]
#auto-down-unreachable-after = 30s
}
}
]]>
</hocon>
</akka>
</configuration>
Assuming your using the 1.1.3 packages.
You should use
ConfigurationFactory.ParseString("akka.remote.helios.tcp.port=" + port)
.WithFallback(section.AkkaConfig);`
And not the dot-netty transport. That one is not released by us yet. And is only available in the dev branch.

TestManagementHttpClient Create Test Run

I am trying below C# code to create TFS test run. But every time I am getting below error. Though I have given test plan details. I couldnt even find documentations on this.
Error
An exception of type 'Microsoft.TeamFoundation.TestManagement.WebApi.TestObjectNotFoundException' occurred in mscorlib.dll but was not handled in user code
Additional information: Test plan {0} not found.
Code
public async Task CreateTestRun()
{
TestManagementHttpClient witClient = connection.GetClient<TestManagementHttpClient>();
TestCaseResultUpdateModel TestCaseResultUpdateModel = new TestCaseResultUpdateModel();
ShallowReference testPlanReference = new ShallowReference();
testPlanReference.Id = "{TestPlanId}";
testPlanReference.Name = "{TestPlanName}";
testPlanReference.Url = "http://{TFSInstance}/{Project}/_apis/test/plans/{TestPlanID}";
RunCreateModel RunCreateModel = new RunCreateModel("SWAT-Run","",new int[] {2304187},testPlanReference,
null,0,"",false,"Error Message","","","","","comment","","", "",
null, null, null, null,null,"","","","",new TimeSpan(0,0,10,0,0),"",null);
TestRun testRun = await witClient.CreateTestRunAsync(this.Project, RunCreateModel, null);
}
I have done it succssful with below code, hope it can be helpful to you.
var tfsRun = _testPoint.Plan.CreateTestRun(false);
tfsRun.DateStarted = DateTime.Now;
tfsRun.AddTestPoint(_testPoint, _currentIdentity);
tfsRun.DateCompleted = DateTime.Now;
tfsRun.Save(); // so results object is created
var result = tfsRun.QueryResults()[0];
result.Owner = _currentIdentity;
result.RunBy = _currentIdentity;
result.State = TestResultState.Completed;
result.DateStarted = DateTime.Now;
result.Duration = new TimeSpan(0L);
result.DateCompleted = DateTime.Now.AddMinutes(0.0);
var iteration = result.CreateIteration(1);
iteration.DateStarted = DateTime.Now;
iteration.DateCompleted = DateTime.Now;
iteration.Duration = new TimeSpan(0L);
iteration.Comment = "Run from TFS Test Steps Editor by " + _currentIdentity.DisplayName;
for (int actionIndex = 0; actionIndex < _testEditInfo.TestCase.Actions.Count; actionIndex++)
{
var testAction = _testEditInfo.TestCase.Actions[actionIndex];
if (testAction is ISharedStepReference)
continue;
var userStep = _testEditInfo.SimpleSteps[actionIndex];
var stepResult = iteration.CreateStepResult(testAction.Id);
stepResult.ErrorMessage = String.Empty;
stepResult.Outcome = userStep.Outcome;
foreach (var attachmentPath in userStep.AttachmentPaths)
{
var attachment = stepResult.CreateAttachment(attachmentPath);
stepResult.Attachments.Add(attachment);
}
iteration.Actions.Add(stepResult);
}
var overallOutcome = _testEditInfo.SimpleSteps.Any(s => s.Outcome != TestOutcome.Passed)
? TestOutcome.Failed
: TestOutcome.Passed;
iteration.Outcome = overallOutcome;
result.Iterations.Add(iteration);
result.Outcome = overallOutcome;
result.Save(false);

Grails Issue Dealing With Tcp Client & Tcp Server

I created a Tcp Client & Tcp Server in Groovy awhile back and had no issues with it. I was only connecting to one machine at the time to gather data. This time I am attempting to connect to the script on multiple hosts and it is only saving one of the hosts information in my grails app.
My Grails application is simple, it has a domain class for Machines (basically the computers and the information on them that I seek) and it will use my TcpClient.groovy script to connect and gather information from the TcpServer.groovy on the other computers. For each host, it should save the information gathered, however, it seems to skip right over saving any host aside from the last one.
Tcp Client :
//TCP CLIENT
public void queryData(def hosts) {
for(int aHost = 0; aHost < hosts.size; aHost++) {
cristalClient(hosts[aHost]);
}
}
public void cristalClient(String host) {
commands = ["dateScan", "computerName", "ip", "quit"]
answers = [commands.size]
requestSocket = new Socket(host, 2000)
r = new BufferedReader(new InputStreamReader(requestSocket.getInputStream()));
w = new BufferedWriter(new OutputStreamWriter(requestSocket.getOutputStream()));
String message = "Connection was successful"
message = readAvailable(r)
println("Sever>" + message)
for(int n = 0; n < commands.size; n++) {
sendMessage(commands[n]);
answers[n] = readAvailable(r)
}
lastRead = answers[0]
machineName = answers[1]
ipAddress = answers[3]
w.flush()
w.close()
}
public String readAvailable(r) {
String out = ""
String dum = null
while((dum = r.readLine()) !=null) {
if(dum == ">>EOF<<") return out
if(out.length() > 0) out += "\r\n"
out += dum
}
return out
}
public void sendMessage(msg) {
w.write(msg+"\r\n");
w.flush();
println("Client>" + msg);
}
public void printData(abc) {
abc.eachWithIndex { it, index ->
println "Drive $index"
it.each { k, v ->
println "\t$k = $v"
}
}
}
Tcp Server :
//TCP Server
def server = new ServerSocket(2000)
println("Waiting for connection")
server.accept() { socket ->
socket.withStreams { input, output ->
w = new BufferedWriter(new OutputStreamWriter(output))
String message = "Connection was successful"
r = new BufferedReader(new InputStreamReader(input))
while(true) {
if(message != null) {
sendMessage(message)
message = null
}
String a = r.readLine()
if(a == "dateScan") {
message = new Date
} else if(a == "computerName") {
message = InetAddress.getLocalHost().hostName
} else if(a == "ip") {
message = InetAddress.getLocalHost().getHostAddress()
} else if(a == "quit") {
server.close()
return
} else {
message = "$a command unknown."
println message
}
}
}
}
def sendMessage(String msg) {
println( "sending: >" + msg + "<" )
w.writeLine(msg)
w.writeLine(">>EOF<<")
w.flush();
}
Grails Controller :
//Grails Controller
CollectMachines {
def w = new tcpClient()
def hosts = ["winXp", "Win7"]
w.queryData(hosts)
def abc = w.hardDrive
abc.each { println it }
int numberOfDrives = abc.size()
//add new machine
numberOfDrives.times {
def machineName = abc.computerName[it]
def machineInstance = Machine.findByMachineName(machineName)
if (!machineInstance) {
machineInstance = new Machine(machineName)
}
def lastScan = abc.lastScan[it]
def scanDate = new Date().parse("E MMM dd H:m:s z yyyy", lastScan)
def ipAddress = abc.ipAddress[it]
machineInstance.setIpAddress(ipAddress)
machineInstance.setDateScanned(scanDate)
machineInstance.save()
}
redirect(action: "list")
}
Do I need to put a pause in so that the server has time to send a response? My Tcp Client does send out all the commands but only gets responses for the last set of commands.
Also, sorry for the indentation issues with my code snippets, I'm not sure why they are messed up.
.
There are a few problems with your code. tcpClient never assigns to hardDrive, for example. Assuming this is an oversight, I think the real problem is that tcpClient is querying data for multiple hosts, and storing all the results in the same instance variables answers, and ultimately lastRead, machineName, and ipAddress.
You need to store the results for each host separately. One way would be to have answers be a map of lists. For example, answers[host][0] would be the first answer for a given host.
I don't think any kind of pause is necessary.

Resources