From 1dfd6324829e9e828aeb543b4218b01daa6efa3f Mon Sep 17 00:00:00 2001 From: Sinan Date: Tue, 6 Nov 2018 14:41:00 +0100 Subject: [PATCH 3/3] Add a config loader and a transport registry This changes also the Factory to use the config file. --- rsb-cil-test/Program.cs | 19 +- rsb-cil/Rsb/Config/ParticipantConfig.cs | 108 +++++++++ rsb-cil/Rsb/Config/ParticipantConfigCreator.cs | 48 ++++ rsb-cil/Rsb/Config/TransportConfig.cs | 150 +++++++++++++ .../Rsb/Converter/DefaultConverterRepository.cs | 20 +- rsb-cil/Rsb/Converter/IConverterRepository.cs | 12 + rsb-cil/Rsb/Factory.cs | 50 +++-- rsb-cil/Rsb/Informer.cs | 7 +- rsb-cil/Rsb/Transport/ConnectorInfo.cs | 24 ++ rsb-cil/Rsb/Transport/IConnector.cs | 9 - rsb-cil/Rsb/Transport/IInPushConnector.cs | 6 +- rsb-cil/Rsb/Transport/IOutConnector.cs | 8 + rsb-cil/Rsb/Transport/ITransportFactory.cs | 15 ++ .../Rsb/Transport/Socket/BusClientConnection.cs | 12 +- rsb-cil/Rsb/Transport/Socket/ServerMode.cs | 9 + .../Rsb/Transport/Socket/SocketConfiguration.cs | 14 -- rsb-cil/Rsb/Transport/Socket/SocketFactory.cs | 96 ++++++++ ...InPushConnector.cs => SocketInPushConnector.cs} | 16 +- rsb-cil/Rsb/Transport/Socket/SocketOption.cs | 18 ++ .../{OutConnector.cs => SocketOutConnector.cs} | 12 +- rsb-cil/Rsb/Transport/TransportRegistry.cs | 73 ++++++ rsb-cil/Rsb/Util/ConfigLoader.cs | 245 +++++++++++++++++++++ rsb-cil/Rsb/Util/Properties.cs | 162 ++++++++++++++ rsb-cil/Rsb/Util/Property.cs | 93 ++++++++ rsb-cil/rsb-cil.csproj | 22 +- 25 files changed, 1147 insertions(+), 101 deletions(-) create mode 100644 rsb-cil/Rsb/Config/ParticipantConfig.cs create mode 100644 rsb-cil/Rsb/Config/ParticipantConfigCreator.cs create mode 100644 rsb-cil/Rsb/Config/TransportConfig.cs create mode 100644 rsb-cil/Rsb/Converter/IConverterRepository.cs create mode 100644 rsb-cil/Rsb/Transport/ConnectorInfo.cs delete mode 100644 rsb-cil/Rsb/Transport/IConnector.cs create mode 100644 rsb-cil/Rsb/Transport/IOutConnector.cs create mode 100644 rsb-cil/Rsb/Transport/ITransportFactory.cs create mode 100644 rsb-cil/Rsb/Transport/Socket/ServerMode.cs delete mode 100644 rsb-cil/Rsb/Transport/Socket/SocketConfiguration.cs create mode 100644 rsb-cil/Rsb/Transport/Socket/SocketFactory.cs rename rsb-cil/Rsb/Transport/Socket/{InPushConnector.cs => SocketInPushConnector.cs} (87%) create mode 100644 rsb-cil/Rsb/Transport/Socket/SocketOption.cs rename rsb-cil/Rsb/Transport/Socket/{OutConnector.cs => SocketOutConnector.cs} (87%) create mode 100644 rsb-cil/Rsb/Transport/TransportRegistry.cs create mode 100644 rsb-cil/Rsb/Util/ConfigLoader.cs create mode 100644 rsb-cil/Rsb/Util/Properties.cs create mode 100644 rsb-cil/Rsb/Util/Property.cs diff --git a/rsb-cil-test/Program.cs b/rsb-cil-test/Program.cs index ee536b6..b9bd679 100644 --- a/rsb-cil-test/Program.cs +++ b/rsb-cil-test/Program.cs @@ -13,8 +13,7 @@ namespace rsbciltest { class MainClass { - static readonly string host = "localhost"; - static readonly int port = 30010; + static readonly SocketOptions options = new SocketOptions("localhost", 30010, true); public static void PrintEvent(Event theEvent) { @@ -36,7 +35,6 @@ namespace rsbciltest static void Main(String[] args) { - BasicConfigurator.Configure(); var inConverters = buildConverterSelection(new List> { @@ -49,18 +47,17 @@ namespace rsbciltest new Tuple(typeof(JointAngles), new ProtocolBufferConverter(JointAngles.Descriptor)) }); - var listener = new Listener(new InPushConnector(new BusClientConnection(host, port), new Scope("/"), inConverters)); + var listener = new Listener(new SocketInPushConnector(new BusClientConnection(options), inConverters)); listener.EventReceived += new NewEventHandler(PrintEvent); listener.Activate(); - var informer = new Informer("/test/narf", new OutConnector(new BusClientConnection(host, port), + var informer = new Informer("/test/narf", new SocketOutConnector(new BusClientConnection(options), outConverters)); Console.WriteLine(informer.Scope); informer.Activate(); for (int i = 0; i < 5; i++) { - var angles = new JointAngles(); angles.Angles.Add(1); informer.Send(angles); @@ -77,10 +74,7 @@ namespace rsbciltest Console.WriteLine("\nFactory Test:"); - SocketConfiguration.Hostname = host; - SocketConfiguration.Port = port; - - DefaultConverterRepository.Instance.addConverter(new ProtocolBufferConverter(JointAngles.Descriptor)); + DefaultConverterRepository.Instance.AddConverter(new ProtocolBufferConverter(JointAngles.Descriptor)); Informer repoInformer = Factory.Instance.CreateInformer("/test/repo"); Listener repoListener = Factory.Instance.CreateListener("/"); @@ -159,7 +153,7 @@ namespace rsbciltest remote.Call("voidvoid"); // do some asynchronous calls - var futureEcho = remote.CallAsync("echo", "Async"); + var futureEcho = remote.CallAsync("echo", "Async"); remote.CallAsync("voidvoid"); var futureType = remote.CallAsync("typevoid"); @@ -198,8 +192,7 @@ namespace rsbciltest server.Deactivate(); remote.Deactivate(); } - - } + } public class ExceptionCall : DataCallback { diff --git a/rsb-cil/Rsb/Config/ParticipantConfig.cs b/rsb-cil/Rsb/Config/ParticipantConfig.cs new file mode 100644 index 0000000..b03da78 --- /dev/null +++ b/rsb-cil/Rsb/Config/ParticipantConfig.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rsb.Config +{ + /** + * Like the Java version but... + * - no Introspection + * - no receivingStrategy! + */ + + public class ParticipantConfig + { + public Dictionary TransportsByName + { + get; + } = new Dictionary(); + + public HashSet EnabledTransports + { + get + { + HashSet enabledTransports = + new HashSet(); + foreach (TransportConfig config in this.TransportsByName.Values) + { + if (config.Enabled) + { + enabledTransports.Add(config); + } + } + return enabledTransports; + } + } + + public bool HasTransport(String name) + { + return this.TransportsByName.ContainsKey(name); + } + + public TransportConfig GetOrCreateTransport(String transportName) + { + if (this.TransportsByName.ContainsKey(transportName)) + { + return this.TransportsByName[transportName]; + } + else + { + TransportConfig newTransport = + new TransportConfig(transportName); + this.TransportsByName[transportName] = newTransport; + return newTransport; + } + } + + public ParticipantConfig Copy() + { + ParticipantConfig copy = new ParticipantConfig(); + + foreach (var transportName in this.TransportsByName.Keys) + { + copy.TransportsByName[transportName] = TransportsByName[transportName].Copy(); + } + + return copy; + } + + public override String ToString() + { + StringBuilder builder = new StringBuilder(57); + builder.Append(GetType().Name); + builder.Append("[transports={"); + bool first = true; + foreach (var transport in TransportsByName.Values) + { + if (first) + { + first = false; + } + else + { + builder.Append(","); + } + builder.Append(transport.ToString()); + } + builder.Append("}]"); + + return builder.ToString(); + } + + public override int GetHashCode() + { + int result = 17 * this.TransportsByName.GetHashCode(); + return result; + } + + public override bool Equals(object obj) + { + if (!(obj.GetType().Equals(typeof(ParticipantConfig)))) + { + return false; + } + ParticipantConfig other = (ParticipantConfig)obj; + return this.TransportsByName.Equals(other.TransportsByName); + } + } +} diff --git a/rsb-cil/Rsb/Config/ParticipantConfigCreator.cs b/rsb-cil/Rsb/Config/ParticipantConfigCreator.cs new file mode 100644 index 0000000..85d583b --- /dev/null +++ b/rsb-cil/Rsb/Config/ParticipantConfigCreator.cs @@ -0,0 +1,48 @@ +using Rsb.Transport; +using Rsb.Util; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Rsb.Config +{ + /** + * like java implementation but... + * - without introspection + */ + + public class ParticipantConfigCreator + { + public ParticipantConfig Create(Properties properties) + { + ParticipantConfig config = new ParticipantConfig(); + Reconfigure(config, properties); + return config; + } + + public void Reconfigure(ParticipantConfig config, Properties properties) + { + foreach (String transportName in TransportRegistry.Instance.AvailableTransportNames) + { + String transportPropPrefix = "transport." + transportName; + Properties transportProps = properties + .Filter(transportPropPrefix); + + HashSet handledKeys = new HashSet(); + + TransportConfig transportConfig = config + .GetOrCreateTransport(transportName); + + String enabledKey = transportPropPrefix + ".enabled"; + transportConfig.Enabled = transportProps.GetProperty( + enabledKey, transportConfig.Enabled).AsBoolean(); + handledKeys.Add(enabledKey); + + transportProps.Remove(handledKeys); + transportConfig.Options.Merge(transportProps); + } + } + } +} diff --git a/rsb-cil/Rsb/Config/TransportConfig.cs b/rsb-cil/Rsb/Config/TransportConfig.cs new file mode 100644 index 0000000..e00392b --- /dev/null +++ b/rsb-cil/Rsb/Config/TransportConfig.cs @@ -0,0 +1,150 @@ +using Rsb.Converter; +using Rsb.Util; +using System; +using System.Text; + +namespace Rsb.Config +{ + public class TransportConfig + { + public String Name { get; private set; } + public bool Enabled { get; set; } + public Properties Options { get; set; } + private IConverterRepository _converter; + public IConverterRepository Converters + { + get + { + if (_converter == null) + { + return DefaultConverterRepository.Instance; + } + else + { + return _converter; + } + } + set + { + _converter = value; + } + } + + public TransportConfig(String name, bool enabled, Properties options, IConverterRepository converters) + { + this.Name = name; + this.Enabled = enabled; + this.Options = options; + this.Converters = converters; + } + + public TransportConfig(String name) : this(name, false, new Properties(), null) + { + } + + public override String ToString() + { + StringBuilder builder = new StringBuilder(42); + builder.Append(GetType().Name); + builder.Append("[name='"); + builder.Append(this.Name); + builder.Append("', enabled="); + builder.Append(this.Enabled); + builder.Append(", options="); + builder.Append(this.Options); + builder.Append(", converters="); + builder.Append(this.Converters); + builder.Append(']'); + + return builder.ToString(); + } + + public TransportConfig Copy() + { + TransportConfig copy = new TransportConfig(Name); + copy.Enabled = this.Enabled; + copy.Converters = this.Converters; + foreach (var key in Options.GetAvailableKeys()) + { + copy.Options.SetProperty(key, Options.GetProperty(key).AsString()); + } + return copy; + } + + public override int GetHashCode() + { + int prime = 31; + int result = 1; + result = + prime * result + + ((this.Converters == null) ? 0 : this.Converters.GetHashCode()); + result = prime * result + (this.Enabled ? 1231 : 1237); + result = + prime * result + + ((this.Name == null) ? 0 : this.Name.GetHashCode()); + result = + prime + * result + + ((this.Options == null) ? 0 : this.Options.GetHashCode()); + return result; + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj.GetType().Equals(typeof(TransportConfig)))) + { + return false; + } + TransportConfig other = (TransportConfig)obj; + if (this.Converters == null) + { + if (other.Converters != null) + { + return false; + } + } + else + { + if (!this.Converters.Equals(other.Converters)) + { + return false; + } + } + if (this.Enabled != other.Enabled) + { + return false; + } + if (this.Name == null) + { + if (other.Name != null) + { + return false; + } + } + else if (!this.Name.Equals(other.Name)) + { + return false; + } + if (this.Options == null) + { + if (other.Options != null) + { + return false; + } + } + else if (!this.Options.Equals(other.Options)) + { + return false; + } + return true; + } + } +} diff --git a/rsb-cil/Rsb/Converter/DefaultConverterRepository.cs b/rsb-cil/Rsb/Converter/DefaultConverterRepository.cs index 272f03e..6ab0ae3 100644 --- a/rsb-cil/Rsb/Converter/DefaultConverterRepository.cs +++ b/rsb-cil/Rsb/Converter/DefaultConverterRepository.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Rsb.Converter { - public class DefaultConverterRepository + public class DefaultConverterRepository : IConverterRepository { private static readonly DefaultConverterRepository instance = new DefaultConverterRepository(); @@ -36,27 +36,13 @@ namespace Rsb.Converter private DefaultConverterRepository() { - foreach (var converter in defaultConverter) { - ConverterSignature signature = converter.GetSignature(); - - deserializationConverterList.Add( - new Tuple.Predicate, IConverter>( - new IsExactMatch(signature.Schema), - converter - )); - - serializationConverterList.Add( - new Tuple.Predicate, IConverter>( - new IsExactMatch(signature.DataType), - converter - )); + AddConverter(converter); } DeserializationConverterSelector = new PredicateConverterSelection(deserializationConverterList); SerializationConverterSelector = new PredicateConverterSelection(serializationConverterList); - } public static DefaultConverterRepository Instance @@ -67,7 +53,7 @@ namespace Rsb.Converter } } - public void addConverter(IConverter converter) + public void AddConverter(IConverter converter) { deserializationConverterList.Add( new Tuple.Predicate, IConverter>( diff --git a/rsb-cil/Rsb/Converter/IConverterRepository.cs b/rsb-cil/Rsb/Converter/IConverterRepository.cs new file mode 100644 index 0000000..33dac88 --- /dev/null +++ b/rsb-cil/Rsb/Converter/IConverterRepository.cs @@ -0,0 +1,12 @@ +using System; + +namespace Rsb.Converter +{ + public interface IConverterRepository + { + PredicateConverterSelection DeserializationConverterSelector { get; } + PredicateConverterSelection SerializationConverterSelector { get; } + + void AddConverter(IConverter converter); + } +} diff --git a/rsb-cil/Rsb/Factory.cs b/rsb-cil/Rsb/Factory.cs index 76b6125..594e866 100644 --- a/rsb-cil/Rsb/Factory.cs +++ b/rsb-cil/Rsb/Factory.cs @@ -1,6 +1,11 @@ using Rsb.Transport.Socket; using Rsb.Converter; using Rsb.Patterns; +using Rsb.Util; +using System; +using Rsb.Config; +using Rsb.Transport; +using System.Collections.Generic; namespace Rsb { @@ -8,6 +13,9 @@ namespace Rsb { private static readonly Factory instance = new Factory(); + private Properties properties = new Properties(); + private ParticipantConfig defaultConfig = new ParticipantConfig(); + // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Factory() @@ -16,6 +24,10 @@ namespace Rsb private Factory() { + TransportRegistry.Instance.RegisterTransport("socket", new SocketFactory()); + + new ConfigLoader().Load(this.properties); + new ParticipantConfigCreator().Reconfigure(this.defaultConfig, this.properties); } public static Factory Instance @@ -26,30 +38,37 @@ namespace Rsb } } - public Informer CreateInformer(Scope scope) { - - return new Informer(scope, - new OutConnector( - new BusClientConnection(SocketConfiguration.Hostname, SocketConfiguration.Port), - DefaultConverterRepository.Instance.SerializationConverterSelector)); + public Informer CreateInformer(Scope scope) + { + List connectors = new List(); + foreach (TransportConfig config in defaultConfig.EnabledTransports) + { + connectors.Add(TransportRegistry.Instance.GetFactory(config.Name).CreateOutConnector(config.Options, config.Converters.SerializationConverterSelector)); + } + // only one transport can be used right now. Actually, only one transport exists right now... + return new Informer(scope, connectors[0]); } - public Informer CreateInformer(string scope) { + public Informer CreateInformer(string scope) + { return CreateInformer(new Scope(scope)); } public Listener CreateListener(Scope scope) { - - return new Listener( - new InPushConnector( - new BusClientConnection(SocketConfiguration.Hostname, SocketConfiguration.Port), - scope, - DefaultConverterRepository.Instance.DeserializationConverterSelector - )); + List connectors = new List(); + foreach (TransportConfig config in defaultConfig.EnabledTransports) + { + IInPushConnector connector = TransportRegistry.Instance.GetFactory(config.Name).CreateInPushConnector(config.Options, config.Converters.DeserializationConverterSelector); + connector.SetScope(scope); + connectors.Add(connector); + } + // only one transport can be used right now. Actually, only one transport exists right now... + return new Listener(connectors[0]); } - public Listener CreateListener(string scope) { + public Listener CreateListener(string scope) + { return CreateListener(new Scope(scope)); } @@ -77,6 +96,5 @@ namespace Rsb { return CreateLocalServer(new Scope(scope)); } - } } diff --git a/rsb-cil/Rsb/Informer.cs b/rsb-cil/Rsb/Informer.cs index 49189a6..5dcc02f 100644 --- a/rsb-cil/Rsb/Informer.cs +++ b/rsb-cil/Rsb/Informer.cs @@ -6,8 +6,7 @@ namespace Rsb { public class Informer : IActivatable { - - private IConnector connector; + private IOutConnector connector; private Guid id = Guid.NewGuid(); private long sequenceNumber = 1; @@ -17,12 +16,12 @@ namespace Rsb private set; } - public Informer(string scope, IConnector connector) + public Informer(string scope, IOutConnector connector) : this(new Scope(scope), connector) { } - public Informer(Scope scope, IConnector connector) + public Informer(Scope scope, IOutConnector connector) { this.Scope = scope; this.connector = connector; diff --git a/rsb-cil/Rsb/Transport/ConnectorInfo.cs b/rsb-cil/Rsb/Transport/ConnectorInfo.cs new file mode 100644 index 0000000..fced792 --- /dev/null +++ b/rsb-cil/Rsb/Transport/ConnectorInfo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace Rsb.Transport +{ + public class ConnectorInfo + { + public String Name { get; private set; } + public HashSet Schemas { get; private set; } + public HashSet Options { get; private set; } + public bool IsRemote { get; private set; } + + public ConnectorInfo(String name, + HashSet schemas, + HashSet options, + bool remote) + { + this.Name = name; + this.Schemas = schemas; + this.Options = options; + this.IsRemote = remote; + } + } +} diff --git a/rsb-cil/Rsb/Transport/IConnector.cs b/rsb-cil/Rsb/Transport/IConnector.cs deleted file mode 100644 index 9a522e1..0000000 --- a/rsb-cil/Rsb/Transport/IConnector.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -using Rsb.Util; -namespace Rsb.Transport -{ - public interface IConnector : IActivatable - { - void Push(Event theEvent); - } -} diff --git a/rsb-cil/Rsb/Transport/IInPushConnector.cs b/rsb-cil/Rsb/Transport/IInPushConnector.cs index ed4e50c..61fe1c0 100644 --- a/rsb-cil/Rsb/Transport/IInPushConnector.cs +++ b/rsb-cil/Rsb/Transport/IInPushConnector.cs @@ -1,14 +1,10 @@ -using System; -using Rsb.Util; +using Rsb.Util; namespace Rsb.Transport { - public interface IInPushConnector : IActivatable { - event NewEventHandler EventReceived; void SetScope(Scope scope); - } } diff --git a/rsb-cil/Rsb/Transport/IOutConnector.cs b/rsb-cil/Rsb/Transport/IOutConnector.cs new file mode 100644 index 0000000..5ecc073 --- /dev/null +++ b/rsb-cil/Rsb/Transport/IOutConnector.cs @@ -0,0 +1,8 @@ +using Rsb.Util; +namespace Rsb.Transport +{ + public interface IOutConnector : IActivatable + { + void Push(Event theEvent); + } +} diff --git a/rsb-cil/Rsb/Transport/ITransportFactory.cs b/rsb-cil/Rsb/Transport/ITransportFactory.cs new file mode 100644 index 0000000..c058d64 --- /dev/null +++ b/rsb-cil/Rsb/Transport/ITransportFactory.cs @@ -0,0 +1,15 @@ +using Rsb.Converter; +using Rsb.Util; +using System; + +namespace Rsb.Transport +{ + public interface ITransportFactory + { + ConnectorInfo GetInfo(); + + IOutConnector CreateOutConnector(Properties properties, IConverterSelectionStrategy converters); + + IInPushConnector CreateInPushConnector(Properties properties, IConverterSelectionStrategy converters); + } +} diff --git a/rsb-cil/Rsb/Transport/Socket/BusClientConnection.cs b/rsb-cil/Rsb/Transport/Socket/BusClientConnection.cs index 058dda3..25f535a 100644 --- a/rsb-cil/Rsb/Transport/Socket/BusClientConnection.cs +++ b/rsb-cil/Rsb/Transport/Socket/BusClientConnection.cs @@ -11,7 +11,6 @@ namespace Rsb.Transport.Socket { public class BusClientConnection { - private static readonly log4net.ILog LOGGER = log4net.LogManager.GetLogger(typeof(BusClientConnection)); private static readonly int MESSAGE_LENGTH_SIZE = 4; @@ -19,13 +18,15 @@ namespace Rsb.Transport.Socket private String host; private int port; + private bool noDelay; private TcpClient client = null; private NetworkStream stream = null; - public BusClientConnection(String host, int port) + public BusClientConnection(SocketOptions options) { - this.host = host; - this.port = port; + this.host = options.Host; + this.port = options.Port; + this.noDelay = options.TcpNoDelay; } public void Activate() @@ -36,7 +37,7 @@ namespace Rsb.Transport.Socket // https://stackoverflow.com/questions/18390715/why-is-the-tcpclient-slower-to-connect-with-parameters-in-the-constructor this.client = new TcpClient(); this.client.Connect(host, port); - this.client.NoDelay = true; + this.client.NoDelay = noDelay; this.stream = client.GetStream(); handshake(); @@ -109,6 +110,5 @@ namespace Rsb.Transport.Socket totalReceived += currentReceived; } } - } } diff --git a/rsb-cil/Rsb/Transport/Socket/ServerMode.cs b/rsb-cil/Rsb/Transport/Socket/ServerMode.cs new file mode 100644 index 0000000..b882e36 --- /dev/null +++ b/rsb-cil/Rsb/Transport/Socket/ServerMode.cs @@ -0,0 +1,9 @@ +namespace Rsb.Transport.Socket +{ + public enum ServerMode + { + YES, + NO, + AUTO + } +} diff --git a/rsb-cil/Rsb/Transport/Socket/SocketConfiguration.cs b/rsb-cil/Rsb/Transport/Socket/SocketConfiguration.cs deleted file mode 100644 index bd75267..0000000 --- a/rsb-cil/Rsb/Transport/Socket/SocketConfiguration.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Rsb.Transport.Socket -{ - public static class SocketConfiguration - { - public static int Port = 55555; - public static string Hostname = "localhost"; - } -} diff --git a/rsb-cil/Rsb/Transport/Socket/SocketFactory.cs b/rsb-cil/Rsb/Transport/Socket/SocketFactory.cs new file mode 100644 index 0000000..379f29b --- /dev/null +++ b/rsb-cil/Rsb/Transport/Socket/SocketFactory.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using Rsb.Converter; +using Rsb.Util; + +namespace Rsb.Transport.Socket +{ + public class SocketFactory : ITransportFactory + { + private static readonly String SCHEMA = "socket"; + + private static readonly String SERVER_MODE_NO = "0"; + private static readonly String SERVER_MODE_YES = "1"; + private static readonly String SERVER_MODE_AUTO = "auto"; + + private static readonly String PORT_KEY = "transport.socket.port"; + private static readonly int DEFAULT_PORT = 55555; + private static readonly String HOST_KEY = "transport.socket.host"; + private static readonly String DEFAULT_HOST = "localhost"; + private static readonly String NODELAY_KEY = "transport.socket.tcpnodelay"; + private static readonly bool DEFAULT_NODELAY = true; + private static readonly String SERVER_MODE_KEY = "transport.socket.server"; + private static readonly String DEFAULT_SERVER_MODE = SERVER_MODE_NO; + + public IInPushConnector CreateInPushConnector(Properties properties, IConverterSelectionStrategy converters) + { + ServerMode mode = ParseServerMode(properties); + return new SocketInPushConnector( + new BusClientConnection(ParseSocketOptions(properties)), (IConverterSelectionStrategy)converters); + } + + public IOutConnector CreateOutConnector(Properties properties, IConverterSelectionStrategy converters) + { + return new SocketOutConnector(new BusClientConnection(ParseSocketOptions(properties)), (IConverterSelectionStrategy)converters); + } + + public ConnectorInfo GetInfo() + { + HashSet schemas = new HashSet + { + SCHEMA + }; + HashSet options = new HashSet + { + "host", + "port", + "server", + "tcpnodelay" + }; + return new ConnectorInfo(SCHEMA, schemas, options, true); + } + + private SocketOptions ParseSocketOptions(Properties properties) + { + int port = properties.GetProperty(PORT_KEY, DEFAULT_PORT).AsInteger(); + if (port < 0) + { + throw new ArgumentException("Port must be a number >= 0"); + } + String address = properties.GetProperty(HOST_KEY, DEFAULT_HOST).AsString(); + bool tcpNoDelay = properties.GetProperty(NODELAY_KEY, DEFAULT_NODELAY).AsBoolean(); + + return new SocketOptions(address, port, tcpNoDelay); + } + + private ServerMode ParseServerMode(Properties properties) + { + String serverModeString = + properties.GetProperty(SERVER_MODE_KEY, DEFAULT_SERVER_MODE) + .AsString(); + ServerMode serverMode; + + if (SERVER_MODE_NO.Equals(serverModeString)) + { + serverMode = ServerMode.NO; + } + /* NOT IMPLEMENTED YET + else if (SERVER_MODE_AUTO.Equals(serverModeString)) + { + serverMode = ServerMode.AUTO; + } + else if (SERVER_MODE_YES.Equals(serverModeString)) + { + serverMode = ServerMode.YES; + } + */ + else + { + throw new NotSupportedException(String.Format( + "Unsupported server mode '{0}'", serverModeString)); + } + + return serverMode; + } + } +} diff --git a/rsb-cil/Rsb/Transport/Socket/InPushConnector.cs b/rsb-cil/Rsb/Transport/Socket/SocketInPushConnector.cs similarity index 87% rename from rsb-cil/Rsb/Transport/Socket/InPushConnector.cs rename to rsb-cil/Rsb/Transport/Socket/SocketInPushConnector.cs index 562447a..c7bba6e 100644 --- a/rsb-cil/Rsb/Transport/Socket/InPushConnector.cs +++ b/rsb-cil/Rsb/Transport/Socket/SocketInPushConnector.cs @@ -6,22 +6,25 @@ using Rsb.Converter; using Rsb.Protocol; namespace Rsb.Transport.Socket { - public class InPushConnector : IInPushConnector + /** + * ServerMode will not be evaluated. + * Server mode does actually not exist! + */ + public class SocketInPushConnector : IInPushConnector { - private static readonly log4net.ILog LOGGER = log4net.LogManager.GetLogger(typeof(InPushConnector)); + private static readonly log4net.ILog LOGGER = log4net.LogManager.GetLogger(typeof(SocketInPushConnector)); private BusClientConnection connection; - private Scope scope; + private Scope scope = new Scope("/"); // default private Thread thread; private volatile bool interrupted = false; private IConverterSelectionStrategy converters; - - public InPushConnector(BusClientConnection connection, Scope scope, IConverterSelectionStrategy inConverters) + public SocketInPushConnector(BusClientConnection connection, IConverterSelectionStrategy inConverters) { this.connection = connection; - this.scope = scope; this.converters = inConverters; + //serverMode ignored } public event NewEventHandler EventReceived; @@ -90,6 +93,5 @@ namespace Rsb.Transport.Socket } } } - } } diff --git a/rsb-cil/Rsb/Transport/Socket/SocketOption.cs b/rsb-cil/Rsb/Transport/Socket/SocketOption.cs new file mode 100644 index 0000000..6554078 --- /dev/null +++ b/rsb-cil/Rsb/Transport/Socket/SocketOption.cs @@ -0,0 +1,18 @@ +using System; + +namespace Rsb.Transport.Socket +{ + public class SocketOptions + { + public String Host { get; set; } + public int Port { get; set; } + public bool TcpNoDelay { get; set; } + + public SocketOptions(String host, int port, bool tcpNoDelay) + { + this.Host = host; + this.Port = port; + this.TcpNoDelay = tcpNoDelay; + } + } +} diff --git a/rsb-cil/Rsb/Transport/Socket/OutConnector.cs b/rsb-cil/Rsb/Transport/Socket/SocketOutConnector.cs similarity index 87% rename from rsb-cil/Rsb/Transport/Socket/OutConnector.cs rename to rsb-cil/Rsb/Transport/Socket/SocketOutConnector.cs index a1c8b70..c53b00a 100644 --- a/rsb-cil/Rsb/Transport/Socket/OutConnector.cs +++ b/rsb-cil/Rsb/Transport/Socket/SocketOutConnector.cs @@ -6,10 +6,13 @@ using Google.Protobuf; namespace Rsb.Transport.Socket { - public class OutConnector : IConnector + /** + * ServerMode will not be evaluated. + * Server mode does actually not exist! + */ + public class SocketOutConnector : IOutConnector { - - private static readonly log4net.ILog LOGGER = log4net.LogManager.GetLogger(typeof(OutConnector)); + private static readonly log4net.ILog LOGGER = log4net.LogManager.GetLogger(typeof(SocketOutConnector)); private Thread thread; private volatile bool deactivating = false; @@ -17,7 +20,7 @@ namespace Rsb.Transport.Socket private BusClientConnection connection; private IConverterSelectionStrategy outConverters; - public OutConnector(BusClientConnection connection, IConverterSelectionStrategy outConverters) + public SocketOutConnector(BusClientConnection connection, IConverterSelectionStrategy outConverters) { this.connection = connection; this.outConverters = outConverters; @@ -74,6 +77,5 @@ namespace Rsb.Transport.Socket } LOGGER.Debug("Dummy receiver terminated"); } - } } diff --git a/rsb-cil/Rsb/Transport/TransportRegistry.cs b/rsb-cil/Rsb/Transport/TransportRegistry.cs new file mode 100644 index 0000000..fe47f2a --- /dev/null +++ b/rsb-cil/Rsb/Transport/TransportRegistry.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace Rsb.Transport +{ + public class TransportRegistry + { + private static readonly TransportRegistry instance = new TransportRegistry(); + + private Dictionary factories = new Dictionary(); + + public List AvailableTransportNames + { + get + { + return factories.Keys.ToList(); + } + } + + // Explicit static constructor to tell C# compiler + // not to mark type as beforefieldinit + static TransportRegistry() + { + } + + private TransportRegistry() + { + } + + public static TransportRegistry Instance + { + get + { + return instance; + } + } + + public bool HasTransport(String name) + { + return factories.ContainsKey(name); + } + + public ITransportFactory GetFactory(String name) + { + if (!factories.ContainsKey(name)) + { + throw new ArgumentException("No transport with name '" + name + "' available."); + } + return factories[name]; + } + + public void RegisterTransport(String name, ITransportFactory factory) + { + lock (factories) + { + if (String.IsNullOrEmpty(name)) + { + throw new ArgumentException("Transport name must not be null or empty."); + } + if (factories.ContainsKey(name)) + { + throw new ArgumentNullException("There is already a transport with name '" + name + "'."); + } + if (factory == null) + { + throw new ArgumentNullException("Factory instance must not be null."); + } + + factories.Add(name, factory); + } + } + } +} diff --git a/rsb-cil/Rsb/Util/ConfigLoader.cs b/rsb-cil/Rsb/Util/ConfigLoader.cs new file mode 100644 index 0000000..4b72101 --- /dev/null +++ b/rsb-cil/Rsb/Util/ConfigLoader.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Rsb.Util +{ + public class ConfigLoader + { + private static readonly String CONFIG_DEBUG_VARIABLE = "RSB_CONFIG_DEBUG"; + + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfigLoader)); + + private static void WriteDebug1(String output, params object[] refs) + { + Console.WriteLine(" " + output, refs); + } + + private static void WriteDebug2(String output, params object[] refs) + { + Console.WriteLine(" " + output, refs); + } + + private static readonly String CONFIG_FILES_VARIABLE = "RSB_CONFIG_FILES"; + + // const - to be able to be used in switch causes + // const can't be static + private const String CONFIG_FILE_KEY_SYSTEM = "%system"; + private const String CONFIG_FILE_KEY_PREFIX = "%prefix"; + private const String CONFIG_FILE_KEY_USER = "%user"; + private const String CONFIG_FILE_KEY_PWD = "%pwd"; + + private static readonly String ENV_SEPARATOR = "_"; + private static readonly String KEY_SEPARATOR = "."; + + private static readonly String ALL_USER = @"\Users\Public"; + private static readonly String CONFIG_DIRECTORY = ".config"; // C# is more a win thing, thus instead of */etc/*, *\.config\* is considered + private static readonly String CONFIG_FILE_NAME = "rsb.conf"; + + private List configFilePaths = new List(new String[] { + CONFIG_FILE_KEY_SYSTEM, + CONFIG_FILE_KEY_USER, + CONFIG_FILE_KEY_PWD + }.AsEnumerable()); + + private List readFiles = new List(); + + private string prefix; + private string root; + private string environmentPrefix; + private bool debug; + + public ConfigLoader(String systemPrefix, String environmentPrefix) + { + String customPaths = Environment.GetEnvironmentVariable(CONFIG_FILES_VARIABLE); + if (customPaths != null) + { + configFilePaths = (List)customPaths.Split(':').AsEnumerable(); + } + + this.root = Environment.GetEnvironmentVariable("HOMEDRIVE"); + this.prefix = systemPrefix; + this.environmentPrefix = environmentPrefix; + this.debug = (Environment.GetEnvironmentVariable(CONFIG_DEBUG_VARIABLE) != null); + } + + public ConfigLoader() : this("\\", "RSB_") + { + } + + public Properties Load(Properties properties) + { + LOG.DebugFormat("Loading properties into {0}", properties); + + this.LoadFiles(properties); + this.LoadEnv(properties); + return properties; + } + + private void LoadFiles(Properties properties) + { + LOG.DebugFormat("Loading properties for config files into {0}", properties); + + if (this.debug) + { + WriteDebug1("1. Configuration files"); + } + int index = 1; + readFiles.Clear(); + foreach (String path in configFilePaths) + { + ResolveAndLoadFile(index++, path, properties); + } + } + + private void ResolveAndLoadFile(int index, String path, Properties properties) + { + switch (path) + { + case CONFIG_FILE_KEY_SYSTEM: + LoadFile(index, + Path.GetFullPath(Path.Combine(root, ALL_USER, CONFIG_DIRECTORY, CONFIG_FILE_NAME)), + "System wide config file", + properties); + break; + case CONFIG_FILE_KEY_PREFIX: + LoadFile(index, + Path.GetFullPath(Path.Combine(root, prefix, CONFIG_DIRECTORY, CONFIG_FILE_NAME)), + "Prefix wide config file", + properties); + break; + case CONFIG_FILE_KEY_USER: + LoadFile(index, + Path.GetFullPath(Path.Combine(root, Environment.GetEnvironmentVariable("HOMEPATH"), CONFIG_DIRECTORY, CONFIG_FILE_NAME)), + "User config file", + properties); + break; + case CONFIG_FILE_KEY_PWD: + LoadFile(index, + Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), CONFIG_FILE_NAME)), + "Current directory file", + properties); + break; + default: + LoadFile(index, + path, + "User specified config file", + properties); + break; + } + } + + private Properties LoadFile(int index, String path, String description, Properties properties) + { + LOG.DebugFormat("Loading properties from {0} into {1} if available", path, properties); + + try + { + bool exists = File.Exists(path); + bool read = readFiles.Contains(path); + + if (this.debug) + { + WriteDebug2("{0}. {1} \"{2}\" {3}", index, description, path, exists ? ("exists" + (read ? " and is already read" : "")) : "does not exist"); + } + + if (exists) + { + LOG.DebugFormat("{0} does exist, loading.", path); + if (!read) + { + this.LoadFile(path, properties); + } + } + } + catch (IOException ex) + { + LOG.Error(String.Format("Caught IOException trying to read {0}.", path), ex); + } + return properties; + } + + private Properties LoadFile(string path, Properties properties) + { + LOG.DebugFormat("Loading properties from file {0} into {1}", path, properties); + + StreamReader reader = new StreamReader(path); + + try + { + String section = ""; + while (!reader.EndOfStream) + { + String line = reader.ReadLine(); + int index = line.IndexOf('#'); + if (index != -1) + { + line = line.Substring(0, index); + } + line = line.Trim(); + if ("".Equals(line)) + { + continue; + } + if (line.StartsWith("[") && line.EndsWith("]")) + { + section = line.Substring(1, line.Length - 2); + continue; + } + index = line.IndexOf('='); + if (index == -1) + { + LOG.WarnFormat("Illegal line in configuration file: `{0}`", line); + continue; + } + String key = + section + KEY_SEPARATOR + + line.Substring(0, index).Trim(); + String value = line.Substring(index + 1).Trim(); + LOG.DebugFormat("Parsed key {0} to be {1}. Setting in result object.", key, value); + properties.SetProperty(key, value); + } + } + finally + { + reader.Close(); + readFiles.Add(path); + } + + return properties; + } + + private Properties LoadEnv(Properties properties) + { + LOG.DebugFormat("Loading properties from environment into {0}", properties); + if (this.debug) + { + WriteDebug1("2. Environment variables with prefix RSB_"); + } + + foreach (var keyRaw in Environment.GetEnvironmentVariables().Keys) + { + String key = (String)keyRaw; + + if (key.StartsWith(this.environmentPrefix)) + { + String value = Environment.GetEnvironmentVariable(key); + String propsKey = + key.Substring(this.environmentPrefix.Length) + .Replace(ENV_SEPARATOR, KEY_SEPARATOR).ToLower(new System.Globalization.CultureInfo("us-US")); + if (this.debug) + { + WriteDebug2("{0} (mapped to {1}) -> {2}", key, propsKey, value); + } + LOG.DebugFormat("Parsed from map: {0} = {1}", propsKey, value); + properties.SetProperty(propsKey, value); + } + } + + return properties; + } + } +} diff --git a/rsb-cil/Rsb/Util/Properties.cs b/rsb-cil/Rsb/Util/Properties.cs new file mode 100644 index 0000000..4bce349 --- /dev/null +++ b/rsb-cil/Rsb/Util/Properties.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rsb.Util +{ + public class Properties + { + private Dictionary propertiesByName = + new Dictionary(); + + public Properties() + { + } + + public void Reset() + { + propertiesByName.Clear(); + } + + public Property GetProperty(String key) + { + if (this.propertiesByName.ContainsKey(key)) + { + return this.propertiesByName[key]; + } + else + { + throw new ArgumentException( + "Trying to get unknown property '" + key + + "' in Properties.getProperty()"); + } + } + + public Property GetProperty(String key, TargetType defaultValue) + { + try + { + return GetProperty(key); + } + catch (ArgumentException e) + { + return new Property(defaultValue.ToString()); + } + } + + public void SetProperty(String key, String value) + { + this.propertiesByName[key.Trim()] = new Property(value); + } + + public HashSet GetAvailableKeys() + { + return new HashSet(this.propertiesByName.Keys); + } + + public bool HasProperty(String key) + { + return this.propertiesByName.ContainsKey(key); + } + + public Properties Filter(String desiredPrefix) + { + Properties filtered = new Properties(); + foreach (String key in this.propertiesByName.Keys) + { + if (key.Equals(desiredPrefix) + || key.StartsWith(desiredPrefix + ".")) + { + filtered.SetProperty(key, propertiesByName[key].AsString()); + } + } + return filtered; + } + + public void Remove(IEnumerable keys) + { + foreach (String key in keys) + { + this.propertiesByName.Remove(key); + } + } + + public void Remove(String key) + { + this.propertiesByName.Remove(key); + } + + public void Merge(Properties properties) + { + foreach (String key in properties.propertiesByName.Keys) + { + this.propertiesByName[key] = new Property(properties.propertiesByName[key].AsString()); + } + } + + /* + public Iterator> iterator() + { + return this.propertiesByName.entrySet().iterator(); + }*/ + + public override String ToString() + { + StringBuilder builder = new StringBuilder(); + builder.Append(this.GetType().Name); + builder.Append("["); + foreach (var k in propertiesByName.Keys) + { + builder.Append(k); + builder.Append("="); + builder.Append(propertiesByName[k]); + builder.Append(";"); + } + builder.Append(']'); + + return builder.ToString(); + } + + public override int GetHashCode() + { + int prime = 31; + int result = 1; + result = + prime + * result + + ((this.propertiesByName == null) ? 0 + : this.propertiesByName.GetHashCode()); + return result; + } + + public override bool Equals(object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj.GetType().Equals(typeof(Properties)))) + { + return false; + } + + Properties other = (Properties)obj; + if (this.propertiesByName == null) + { + if (other.propertiesByName != null) + { + return false; + } + } + else if (!this.propertiesByName.Equals(other.propertiesByName)) + { + return false; + } + return true; + } + } +} diff --git a/rsb-cil/Rsb/Util/Property.cs b/rsb-cil/Rsb/Util/Property.cs new file mode 100644 index 0000000..41c601d --- /dev/null +++ b/rsb-cil/Rsb/Util/Property.cs @@ -0,0 +1,93 @@ +using System; +using System.Text; + +namespace Rsb.Util +{ + public class Property + { + private String value; + + public Property(String value) + { + this.value = value.Trim(); + } + + public String AsString() + { + return this.value; + } + + public bool AsBoolean() + { + return this.value.ToLower().Equals("true") + || this.value.ToLower().Equals("yes") + || this.value.ToLower().Equals("on") + || this.value.Equals("1"); + } + + public int AsInteger() + { + return Int32.Parse(this.value); + } + + public long AsLong() + { + return Int64.Parse(this.value); + } + + public float AsFloat() + { + return Single.Parse(this.value); + } + + public double AsDouble() + { + return Double.Parse(this.value); + } + + public override String ToString() + { + return this.GetType().Name + "[" + this.value + "]"; + } + + public override int GetHashCode() + { + int prime = 31; + int result = 1; + result = + prime + * result + + ((this.value == null) ? 0 : this.value.GetHashCode()); + return result; + } + + public override bool Equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj.GetType().Equals(typeof(Property)))) + { + return false; + } + Property other = (Property)obj; + if (this.value == null) + { + if (other.value != null) + { + return false; + } + } + else if (!this.value.Equals(other.value)) + { + return false; + } + return true; + } + } +} diff --git a/rsb-cil/rsb-cil.csproj b/rsb-cil/rsb-cil.csproj index fa8e7d7..68a960c 100644 --- a/rsb-cil/rsb-cil.csproj +++ b/rsb-cil/rsb-cil.csproj @@ -40,7 +40,20 @@ - + + + + + + + + + + + + + + @@ -69,21 +82,20 @@ - - + - + - + -- 2.14.2.windows.1