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 openClientChannels = new Dictionary(); // pro Verbindung wird gezaehlt wie viele Connections es schon gab... private Dictionary channelNameCounter = new Dictionary(); 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 list = new List(); 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; } } }