/* * Copyright (c) 2015 iTAC Software AG, Germany. All Rights Reserved. * * This software is protected by copyright. Under no circumstances may any part of this file in any form be copied, * printed, edited or otherwise distributed, be stored in a retrieval system, or be translated into another language * without the written permission of iTAC Software AG. */ using System; using System.IO; using System.Net.Sockets; using System.Reflection; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Proxies; using System.Threading; using com.itac.artes.ihap; using com.itac.util.logging; namespace com.itac.mes.commonsmt { /// /// /// public class MyChannelEventArgs : EventArgs { public MyChannelEventArgs(string channelName, string message) { this.channelName = channelName; this.message = message; } public string channelName; public string message; } /// /// /// public delegate void PropertyChangeListener(Object sender, MyChannelEventArgs e); /// /// /// public class IhapEventChannel : RealProxy { public static Object classLock = new Object(); private static int CONNECTION_CHECK_MILLIES = 500; private TcpClient _client; private IhapOutputStream _ios; private IhapInputStream _iis; private bool _isShutdown; /// /// /// public string channelName { get; set; } private long lastCall = DateTime.Now.Ticks; private Object lockObject = new Object(); private Thread aliveThread; /// /// /// public event PropertyChangeListener connectionStateChangeListener; /// /// /// public IhapEventChannel(TcpClient client, String channelName) : base(typeof(IMesServicesChannel)) { // diesen Constructor nur einmal betreten lock (classLock) { } this.channelName = channelName; this._client = client; _ios = new IhapOutputStream(_client.GetStream()); _iis = new IhapInputStream(_client.GetStream()); aliveThread = new Thread(new ThreadStart(aliveThreadRun)); aliveThread.Name = channelName; } /// /// /// public void shutdown() { LogHandler.log("LOGGER", LogLevel.INFO, "closing channel" + channelName); _isShutdown = true; try { _client.Close(); } catch (Exception) { LogHandler.log("LOGGER", LogLevel.WARN, "closing channel" + channelName + " failed"); } } /// /// /// public long getLastCall() { return lastCall; } /// /// /// public bool isConnected() { return _client.Client.Connected; } /// /// /// public bool isShutdownInProgress() { return _isShutdown; } /// /// /// public override IMessage Invoke(IMessage message) { lastCall = new DateTime().Ticks; IMethodCallMessage methodMsg = (IMethodCallMessage)message; try { MethodInfo methodInfo = typeof(IMesServices).GetMethod(methodMsg.MethodName, (Type[])methodMsg.MethodSignature); // der Reihe nach alle Interface durchgehen, die als Callback implementiert sind if (methodInfo == null) { methodInfo = typeof(IMesServicesChannel).GetMethod(methodMsg.MethodName, (Type[])methodMsg.MethodSignature); } if (methodInfo == null) { throw new Exception("IHapHandler.cs: methode '" + methodMsg.MethodName + "' nicht in den Interfacen gefunden"); } object[] args = methodMsg.Args; if (methodMsg.MethodName.Equals("Equals")) { return new ReturnMessage(args[0].Equals(this), args, 0, methodMsg.LogicalCallContext, methodMsg); } else if (methodMsg.MethodName.Equals("getChannelName")) { return new ReturnMessage(channelName, args, 0, methodMsg.LogicalCallContext, methodMsg); } else if (methodMsg.MethodName.Equals("GetHashCode")) { return new ReturnMessage(this.GetHashCode(), args, 0, methodMsg.LogicalCallContext, methodMsg); } else if (methodMsg.MethodName.Equals("shutdown")) { shutdown(); stopAliveThread(); connectionClosed(); return new ReturnMessage(null, args, 0, methodMsg.LogicalCallContext, methodMsg); } else if (methodMsg.MethodName.Equals("startup")) { aliveThread.Start(); return new ReturnMessage(null, args, 0, methodMsg.LogicalCallContext, methodMsg); } Object retValue = sendCall(methodInfo, args); return new ReturnMessage(retValue, args, 0, methodMsg.LogicalCallContext, methodMsg); } catch (TargetInvocationException ite) { return new ReturnMessage(ite.GetBaseException(), methodMsg); } catch (Exception ite) { return new ReturnMessage(ite.GetBaseException(), methodMsg); } } private Object sendCall(MethodInfo method, Object[] args) { lock (lockObject) { LogHandler.log("LOGGER", LogLevel.DEBUG, "Send event '" + method.Name + "' to socket " + _client.Client.RemoteEndPoint + " on " + channelName); Object result = null; try { _ios.writeBoolean(false); _ios.call(method, args); _ios.Flush(); IhapReply reply = _iis.readReply(); if (reply is IhapSuccessReply) { result = ((IhapSuccessReply)reply).getReturnValue(); } else { IhapFaultReply fault = (IhapFaultReply)reply; switch (fault.getFaultCode()) { case FaultCode.NoSuchMethodException: throw new NotImplementedException(fault.getMessage()); case FaultCode.ProtocolException: throw new IhapProtocolException(fault.getMessage(), null); default: throw new IhapProtocolException(fault.getMessage(), null); //(Exception)fault.getException(); } } } catch (Exception e) { LogHandler.log("LOGGER", LogLevel.ERROR, "Error while sending event call on " + channelName + ":" + e.Message + "\n" + e.StackTrace); shutdown(); } return result; } } /// /// /// protected void connectionClosed() { // Hier wird überprüft, ob ein Eintrag in der Aufruf-Liste vorhanden ist. if (this.connectionStateChangeListener != null) { // Hier wird jeder Delegat, der sich für den Event registriert hat, aufgerufen. this.connectionStateChangeListener(this, new MyChannelEventArgs(channelName, "connectionClosed")); } } private void stopAliveThread() { if (aliveThread.IsAlive && (aliveThread.ThreadState != System.Threading.ThreadState.Running)) { aliveThread.Interrupt(); } } /// /// /// public void aliveThreadRun() { while (!_isShutdown && aliveThread.ThreadState == System.Threading.ThreadState.Running) { // wenn der letzte Call laenger als .. her ist... try { Thread.Sleep(50); } catch (Exception) { // wenn beim schliessen der Verbindung noch was passiert ist das nicht schlimm LogHandler.log("LOGGER", LogLevel.INFO, "interrupted for " + channelName); } if (DateTime.Now.Ticks - lastCall > CONNECTION_CHECK_MILLIES) { try { // versuchen den Server zu pingen // wenn das fehlschlägt ist die Verbindung kaputt lock (lockObject) { // das erfolgreiche Schreiben eines booleschen Wertes ist aus Kommunikationssicht zu sehen wie der // erfolgreiche Aufruf einer Methode if (!_isShutdown && (aliveThread.ThreadState == System.Threading.ThreadState.Running)) { // nur wenn die Verbindung nicht bereits gestoppt wurde... _ios.writeBoolean(true); _ios.Flush(); lastCall = DateTime.Now.Ticks; } } } catch (Exception) { try { _isShutdown = true; _client.Close(); } catch (IOException e) { // wenn beim schliessen der Verbindung noch was passiert ist das nicht schlimm LogHandler.log("LOGGER", LogLevel.INFO, "shutdown connection problem for " + channelName, e); } finally { // die Schreibverbindung ist kaputt, also diese Connection töten... LogHandler.log("LOGGER", LogLevel.DEBUG, "detected broken connection for " + channelName + " because Server closed port"); connectionClosed(); } } } else { LogHandler.log("LOGGER", LogLevel.DEBUG, "Channel too old, stop channel now"); } } // wenn beim schliessen der Verbindung noch was passiert ist das nicht schlimm stopAliveThread(); LogHandler.log("LOGGER", LogLevel.INFO, "channel shut down finished for " + channelName); } } }