initialize
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
using com.itac.mes.commonsmt;
|
||||
using com.itac.mes.commonsmt.data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Runtime.Remoting.Proxies;
|
||||
using System.Text;
|
||||
|
||||
namespace com.itac.mes.commonsmt
|
||||
{
|
||||
class FailoverInvocationHandler : FailoverHostList
|
||||
{
|
||||
|
||||
// Zugriff auf die folgenden Maps muss synchronisiert erfolgen
|
||||
static readonly object _locker = new object();
|
||||
// In dieser Map sind alle offenen Verbindungen zum DataInterface enthalten
|
||||
private Dictionary<String, IhapEventChannel> openClientChannels = new Dictionary<String, IhapEventChannel>();
|
||||
// pro Verbindung wird gezaehlt wie viele Connections es schon gab...
|
||||
private Dictionary<String, Int32> channelNameCounter = new Dictionary<String, Int32>();
|
||||
|
||||
public FailoverInvocationHandler(MyEventHandler OnLog)
|
||||
: base(OnLog)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a new communication channel with specified name
|
||||
*
|
||||
* @param channelName
|
||||
* the name for the channel
|
||||
* @param connectionListener
|
||||
* when a connection listener is set (!= null) a thread is established to cyclically check wether the channel is still
|
||||
* active or not. If a connection is shutdown by a server (the remote function host) an event at the listener is fired
|
||||
* @return the communication channel where mesFunctions are exceuted (remote calls via IHap)
|
||||
* @throws IOException
|
||||
* when no connection could be established
|
||||
*/
|
||||
|
||||
private IMesServicesChannel getConnection(string channelName, Object s)
|
||||
{
|
||||
if (getActiveHost() == null)
|
||||
{
|
||||
log(TraceEventType.Error, "cannot get new connection " + channelName + " because active host is unset");
|
||||
return null;
|
||||
}
|
||||
// for every channel a new connection Every connection has it's own counter
|
||||
string channelNameWithNumber = getChannelName(channelName);
|
||||
|
||||
// Neue Verbindung zum DataInterface aufmachen
|
||||
var tcpClient = new TcpClient(getActiveHost().getHostname(), getActiveHost().getPort());
|
||||
// eine neue TCp-Client-Verbindung
|
||||
var iHapEventChannel = new IhapEventChannel(tcpClient, channelNameWithNumber);
|
||||
if (iHapEventChannel == null)
|
||||
{
|
||||
log(TraceEventType.Error, "iHapEventChannel not created");
|
||||
throw new Exception("iHapEventChannel not created");
|
||||
}
|
||||
|
||||
log(TraceEventType.Information, "before adding " + channelNameWithNumber);
|
||||
lock (_locker)
|
||||
{
|
||||
if (openClientChannels.ContainsKey(channelNameWithNumber))
|
||||
{
|
||||
openClientChannels.Remove(channelNameWithNumber);
|
||||
}
|
||||
openClientChannels.Add(channelNameWithNumber, iHapEventChannel);
|
||||
}
|
||||
log(TraceEventType.Information, "" + channelNameWithNumber + " added");
|
||||
|
||||
iHapEventChannel.channelName = channelNameWithNumber;
|
||||
// Listener installieren, um zu reagieren, wenn das Data-Interface die Verbindung abgebrochen hat
|
||||
// iHapEventChannel.connectionStateChangeListener += new PropertyChangeListener(iHapEventChannel_connectionStateChangeListener);
|
||||
|
||||
var remoteMesService = (IMesServicesChannel)iHapEventChannel.GetTransparentProxy();
|
||||
if (remoteMesService == null)
|
||||
{
|
||||
log(TraceEventType.Error, "iHapEventChannel.transparentProxy not created");
|
||||
throw new Exception("iHapEventChannel.transparentProxy not created");
|
||||
}
|
||||
remoteMesService.setChannelName(channelNameWithNumber);
|
||||
remoteMesService.startup();
|
||||
return remoteMesService;
|
||||
}
|
||||
|
||||
private string getChannelName(string channelName)
|
||||
{
|
||||
// gab es fuer diesen channelName bereits eine Connection
|
||||
string channelNameWithNumber = channelName;
|
||||
var counter = 0;
|
||||
lock (_locker)
|
||||
{
|
||||
if (channelNameCounter.ContainsKey(channelName))
|
||||
{
|
||||
counter = channelNameCounter[channelName];
|
||||
counter++;
|
||||
if (counter > 100000) { counter = 1; }
|
||||
channelNameCounter.Remove(channelName);
|
||||
channelNameCounter.Add(channelName, counter);
|
||||
}
|
||||
else
|
||||
{
|
||||
channelNameCounter.Add(channelName, counter);
|
||||
}
|
||||
channelNameWithNumber = channelName + "#" + counter;
|
||||
}
|
||||
return channelNameWithNumber;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void handleThrowable(Object response, Exception throwable)
|
||||
{
|
||||
if (!(response is MesResponse))
|
||||
{
|
||||
return;
|
||||
}
|
||||
MesResponse mesResponse = (MesResponse)response;
|
||||
mesResponse.setTotalResult(MesServices.MES_RESULT_NOT_OK);
|
||||
if (throwable is PluginException)
|
||||
{
|
||||
setResponseValues(mesResponse, ((PluginException)throwable).getResponseDetail());
|
||||
}
|
||||
else if (throwable is IOException)
|
||||
{
|
||||
addError(mesResponse.getErrorDetails(),
|
||||
getErrorDetail(throwable.Message, ResponseDetail.COMMUNICATION_FAILURE));
|
||||
}
|
||||
else
|
||||
{
|
||||
addError(mesResponse.getErrorDetails(),
|
||||
getErrorDetail(throwable.Message, ResponseDetail.PROCESSED_WITH_EXCEPTION));
|
||||
}
|
||||
}
|
||||
|
||||
private ErrorDetail[] addError(ErrorDetail[] errorDetails, ErrorDetail errorDetail)
|
||||
{
|
||||
List<ErrorDetail> list = new List<ErrorDetail>();
|
||||
foreach (ErrorDetail item in errorDetails)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
list.Add(errorDetail);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
// convenient method
|
||||
private ErrorDetail getErrorDetail(String detail, int code)
|
||||
{
|
||||
ErrorDetail errorDetail = new ErrorDetail();
|
||||
errorDetail.setCode(code);
|
||||
errorDetail.setDetail(detail);
|
||||
return errorDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* finalizing means closing communication channel and loggin response Object
|
||||
*
|
||||
* @param connection
|
||||
* the used connection
|
||||
*/
|
||||
private void finalizeRequest(IMesServicesChannel connection)
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
string connectionName = connection.getChannelName();
|
||||
if (connectionName != null)
|
||||
{
|
||||
openClientChannels.Remove(connectionName);
|
||||
}
|
||||
connection.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private IMesServicesChannel getConnection(string channelName)
|
||||
{
|
||||
return getConnection(channelName, null);
|
||||
}
|
||||
|
||||
protected void setResponseValues(MesResponse response, ResponseDetail responseDetail)
|
||||
{
|
||||
if (responseDetail.getCode() == ResponseDetail.OK)
|
||||
{
|
||||
response.setTotalResult(MesServices.MES_RESULT_OK);
|
||||
return;
|
||||
}
|
||||
response.setTotalResult(MesServices.MES_RESULT_NOT_OK);
|
||||
addError(response.getErrorDetails(), getErrorDetail(responseDetail.getText(), responseDetail.getCode()));
|
||||
}
|
||||
|
||||
public IMessage Invoke(IMessage msg)
|
||||
{
|
||||
IMethodCallMessage methodMsg = (IMethodCallMessage)msg;
|
||||
|
||||
MethodInfo methodInfo = typeof(IMesServices).GetMethod(methodMsg.MethodName, (Type[])methodMsg.MethodSignature);
|
||||
if (methodInfo == null)
|
||||
{
|
||||
// TODO: check if this is longer required
|
||||
// object ret = failoverInvocationHandler.invoke(this, methodInfo, methodMsg.InArgs);
|
||||
}
|
||||
|
||||
// special handling for a couple of function (mesStart, mesStop, getFailoverHosts..FailoverInvocationHandler.)
|
||||
Object response = Activator.CreateInstance(methodInfo.ReturnType);
|
||||
|
||||
// if no failoverhost available return error
|
||||
if (getActiveHost() == null && size() == 0)
|
||||
{
|
||||
// no host set, no failover available --> fail
|
||||
return null;
|
||||
}
|
||||
|
||||
// do a failover for all methods startswith "mes" (operational methods)
|
||||
if (methodInfo.Name.StartsWith("mes"))
|
||||
{
|
||||
IMesServices connection = null;
|
||||
try
|
||||
{
|
||||
|
||||
bool callFailed = false;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
callFailed = false;
|
||||
log(TraceEventType.Information, "call " + methodInfo.Name + "@" + getActiveHost().getHostname());
|
||||
connection = getConnection(methodInfo.Name);
|
||||
if (connection == null)
|
||||
{
|
||||
callFailed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
response = methodInfo.Invoke(connection, methodMsg.InArgs);
|
||||
if (response == null)
|
||||
{
|
||||
callFailed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if ((response is MesResponse))
|
||||
{
|
||||
MesResponse responseObject = (MesResponse)response;
|
||||
if (responseObject.getTotalResult() == MesServices.MES_RESULT_NOT_OK)
|
||||
{
|
||||
// set DetailResult to COMMUNICATION_FAILURE
|
||||
// repeat this call to another host
|
||||
callFailed = hasCommunicationFailure(responseObject.getErrorDetails());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
callFailed = true;
|
||||
}
|
||||
|
||||
if (callFailed)
|
||||
{
|
||||
if (connection != null)
|
||||
{
|
||||
// explicit finalize this connection
|
||||
connection = null;
|
||||
}
|
||||
remove(getActiveHost());
|
||||
setActiveHost(getNextFailoverHost());
|
||||
}
|
||||
} while (callFailed && getActiveHost() != null);
|
||||
|
||||
// if failover did not find any available host return communication error
|
||||
if (getActiveHost() == null)
|
||||
{
|
||||
|
||||
if ((response is MesResponse))
|
||||
{
|
||||
MesResponse responseObject = (MesResponse)response;
|
||||
|
||||
addError(responseObject.getErrorDetails(),
|
||||
getErrorDetail("no failover host active", ResponseDetail.COMMUNICATION_FAILURE));
|
||||
|
||||
responseObject.setTotalResult(MesServices.MES_RESULT_NOT_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception throwable)
|
||||
{
|
||||
handleThrowable(response, throwable);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (response is MesFailoverResponse)
|
||||
{
|
||||
// internally update the list
|
||||
setFailover(((MesFailoverResponse)response).getFailoverHosts());
|
||||
}
|
||||
finalizeRequest((IMesServicesChannel)connection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply call the method, no failover for them
|
||||
response = methodInfo.Invoke(this, methodMsg.InArgs);
|
||||
}
|
||||
|
||||
return new ReturnMessage(response, methodMsg.Args, 0, methodMsg.LogicalCallContext, methodMsg);
|
||||
}
|
||||
|
||||
private Boolean hasCommunicationFailure(ErrorDetail[] errorDetails)
|
||||
{
|
||||
if (errorDetails == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach (ErrorDetail errDetail in errorDetails)
|
||||
{
|
||||
if (errDetail.getCode() == ResponseDetail.COMMUNICATION_FAILURE)
|
||||
{
|
||||
log(TraceEventType.Error, "communication failure on this connection");
|
||||
throw new SocketException();
|
||||
}
|
||||
if (errDetail.getCode() == -10005)
|
||||
{
|
||||
log(TraceEventType.Error, "communication failure on this connection");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user