diff options
Diffstat (limited to 'csharp/src')
188 files changed, 65300 insertions, 0 deletions
diff --git a/csharp/src/Glacier2/.depend.mak b/csharp/src/Glacier2/.depend.mak new file mode 100644 index 00000000000..a29c2fa8ecb --- /dev/null +++ b/csharp/src/Glacier2/.depend.mak @@ -0,0 +1,29 @@ + +Metrics.cs: \ + "$(slicedir)\Glacier2\Metrics.ice" \ + "$(slicedir)/Ice/Metrics.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +PermissionsVerifier.cs: \ + "$(slicedir)\Glacier2\PermissionsVerifier.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +Router.cs: \ + "$(slicedir)\Glacier2\Router.ice" \ + "$(slicedir)/Ice/Router.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Glacier2/Session.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/Glacier2/PermissionsVerifier.ice" + +Session.cs: \ + "$(slicedir)\Glacier2\Session.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" + +SSLInfo.cs: \ + "$(slicedir)\Glacier2\SSLInfo.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" diff --git a/csharp/src/Glacier2/Application.cs b/csharp/src/Glacier2/Application.cs new file mode 100644 index 00000000000..b5d4aa604c2 --- /dev/null +++ b/csharp/src/Glacier2/Application.cs @@ -0,0 +1,477 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Threading; + +#if !SILVERLIGHT + +namespace Glacier2 +{ + +/// <summary> +/// Utility base class that makes it easy to correctly initialize and finalize +/// the Ice run time, as well as handle signals. Unless the application specifies +/// a logger, Application installs a per-process logger that logs to the standard +/// error output. +/// +/// Applications must create a derived class that implements the {@link #run} method. +/// +/// A program can contain only one instance of this class. +/// </summary> +public abstract class Application : Ice.Application +{ + /// <summary> + /// This exception is raised if the session should be restarted. + /// </summary> + public class RestartSessionException : System.Exception + { + } + + /// <summary> + /// Initializes an instance that calls Communicator.shutdown if + /// a signal is received. + /// </summary> + public + Application() + { + } + + /// <summary> + /// Initializes an instance that handles signals according to the signal + /// policy. + /// </summary> + /// <param name="signalPolicy">@param signalPolicy Determines how to + /// respond to signals.</param> + public + Application(Ice.SignalPolicy signalPolicy) : base(signalPolicy) + { + } + + /// <summary> + /// Called once the communicator has been initialized and the Glacier2 session + /// has been established. A derived class must implement <code>runWithSession</code>, + /// which is the application's starting method. + /// </summary> + /// <param name="args"> The argument vector for the application. Application + /// scans the argument vector passed to <code>main</code> for options that are + /// specific to the Ice run time and removes them; therefore, the vector passed + /// to <code>runWithSession</code> is free from Ice-related options and contains + /// only options and arguments that are application-specific.</param> + /// + /// <returns> The runWithSession method should return zero for successful + /// termination, and non-zero otherwise. Application.main returns the + /// value returned by runWithSession.</returns> + /// + public abstract int + runWithSession(string[] args); + + /// <summary> + /// Run should not be overridden for Glacier2.Application. Instead + /// runWithSession should be used. + /// </summary> + public override int + run(string[] args) + { + // + // This shouldn't be called. + // + Debug.Assert(false); + return 0; + } + + /// <summary> + /// Called to restart the application's Glacier2 session. This + /// method never returns. The exception produce an application restart + /// when called from the Application main thread. + /// </summary> + /// <returns>throws RestartSessionException This exception is + /// always thrown.</returns> + /// + public void + restart() + { + throw new RestartSessionException(); + } + + /// <summary> + /// Creates a new Glacier2 session. A call to createSession always + /// precedes a call to runWithSession. If Ice.LocalException is thrown + /// from this method, the application is terminated. + /// </summary> + /// <returns> The Glacier2 session.</returns> + public abstract Glacier2.SessionPrx + createSession(); + + /// <summary> + /// Called when the session refresh thread detects that the session has been + /// destroyed. A subclass can override this method to take action after the + /// loss of connectivity with the Glacier2 router. This method is called + /// according to the Ice invocation dipsatch rules (in other words, it + /// uses the same rules as an servant upcall or AMI callback). + /// </summary> + public virtual void + sessionDestroyed() + { + } + + /// <summary> + /// Returns the Glacier2 router proxy. + /// </summary> + /// <returns>The router proxy.</returns> + public static Glacier2.RouterPrx + router() + { + return _router; + } + + /// <summary> + /// Returns the Glacier2 session proxy. + /// </summary> + /// <returns>The session proxy.</returns> + public static Glacier2.SessionPrx + session() + { + return _session; + } + + /// <summary> + /// Returns the category to be used in the identities of all of the client's + /// callback objects. Clients must use this category for the router to + /// forward callback requests to the intended client. + /// Throws SessionNotExistException if no session exists. + /// </summary> + /// <returns>The category.</returns> + public string + categoryForClient() + { + if(_router == null) + { + throw new SessionNotExistException(); + } + return _category; + } + + /// <summary> + /// Create a new Ice identity for callback objects with the given + /// identity name field. + /// </summary> + /// <returns>The identity.</returns> + public Ice.Identity + createCallbackIdentity(string name) + { + return new Ice.Identity(name, categoryForClient()); + } + + /// <summary> + /// Adds a servant to the callback object adapter's Active Servant Map with a UUID. + /// </summary> + /// <param name="servant">The servant to add.</param> + /// <returns>The proxy for the servant.</returns> + public Ice.ObjectPrx + addWithUUID(Ice.Object servant) + { + return objectAdapter().add(servant, createCallbackIdentity(Guid.NewGuid().ToString())); + } + + /// <summary> + /// Returns an object adapter for callback objects, creating it if necessary. + /// </summary> + /// <returns>The object adapter.</returns> + public Ice.ObjectAdapter + objectAdapter() + { + if(_router == null) + { + throw new SessionNotExistException(); + } + + lock(mutex__) + { + if(_adapter == null) + { + _adapter = communicator().createObjectAdapterWithRouter("", _router); + _adapter.activate(); + } + } + return _adapter; + } + + private class ConnectionCallbackI : Ice.ConnectionCallback + { + internal ConnectionCallbackI(Application application) + { + _application = application; + } + + public void heartbeat(Ice.Connection con) + { + + } + + public void closed(Ice.Connection con) + { + _application.sessionDestroyed(); + } + + private readonly Application _application; + } + + protected override int + doMain(string[] originArgs, Ice.InitializationData initData) + { + // + // Set the default properties for all Glacier2 applications. + // + initData.properties.setProperty("Ice.RetryIntervals", "-1"); + + bool restart; + int ret = 0; + do + { + // + // A copy of the initialization data and the string array + // needs to be passed to doMainInternal, as these can be + // changed by the application. + // + Ice.InitializationData id = (Ice.InitializationData)initData.Clone(); + id.properties = id.properties.ice_clone_(); + string[] args = (string[]) originArgs.Clone(); + + restart = doMain(args, id, out ret); + } + while(restart); + return ret; + } + + private bool + doMain(string[] args, Ice.InitializationData initData, out int status) + { + // + // Reset internal state variables from Ice.Application. The + // remainder are reset at the end of this method. + // + callbackInProgress__ = false; + destroyed__ = false; + interrupted__ = false; + + bool restart = false; + status = 0; + + try + { + communicator__ = Ice.Util.initialize(ref args, initData); + + _router = Glacier2.RouterPrxHelper.uncheckedCast(communicator().getDefaultRouter()); + if(_router == null) + { + Ice.Util.getProcessLogger().error(appName__ + ": no Glacier2 router configured"); + status = 1; + } + else + { + // + // The default is to destroy when a signal is received. + // + if(signalPolicy__ == Ice.SignalPolicy.HandleSignals) + { + destroyOnInterrupt(); + } + + // + // If createSession throws, we're done. + // + try + { + _session = createSession(); + _createdSession = true; + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + status = 1; + } + + if(_createdSession) + { + int acmTimeout = 0; + try + { + acmTimeout = _router.getACMTimeout(); + } + catch(Ice.OperationNotExistException) + { + } + if(acmTimeout <= 0) + { + acmTimeout = (int)_router.getSessionTimeout(); + } + if(acmTimeout > 0) + { + Ice.Connection connection = _router.ice_getCachedConnection(); + Debug.Assert(connection != null); + connection.setACM((int)acmTimeout, Ice.Util.None, Ice.ACMHeartbeat.HeartbeatAlways); + connection.setCallback(new ConnectionCallbackI(this)); + } + _category = _router.getCategoryForClient(); + status = runWithSession(args); + } + } + } + // + // We want to restart on those exceptions that indicate a + // break down in communications, but not those exceptions that + // indicate a programming logic error (i.e., marshal, protocol + // failure, etc). + // + catch(RestartSessionException) + { + restart = true; + } + catch(Ice.ConnectionRefusedException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + restart = true; + } + catch(Ice.ConnectionLostException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + restart = true; + } + catch(Ice.UnknownLocalException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + restart = true; + } + catch(Ice.RequestFailedException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + restart = true; + } + catch(Ice.TimeoutException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + restart = true; + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + status = 1; + } + catch(System.Exception ex) + { + Ice.Util.getProcessLogger().error("unknown exception:\n" + ex.ToString()); + status = 1; + } + + // + // Don't want any new interrupt. And at this point + // (post-run), it would not make sense to release a held + // signal to run shutdown or destroy. + // + if(signalPolicy__ == Ice.SignalPolicy.HandleSignals) + { + ignoreInterrupt(); + } + + lock(mutex__) + { + while(callbackInProgress__) + { + System.Threading.Monitor.Wait(mutex__); + } + + if(destroyed__) + { + communicator__ = null; + } + else + { + destroyed__ = true; + // + // And communicator__ != null, meaning will be + // destroyed next, destroyed__ = true also ensures that + // any remaining callback won't do anything + // + } + } + + if(_createdSession && _router != null) + { + try + { + _router.destroySession(); + } + catch(Ice.ConnectionLostException) + { + // + // Expected if another thread invoked on an object from the session concurrently. + // + } + catch(Glacier2.SessionNotExistException) + { + // + // This can also occur. + // + } + catch(System.Exception ex) + { + // + // Not expected. + // + Ice.Util.getProcessLogger().error("unexpected exception when destroying the session:\n" + + ex.ToString()); + } + _router = null; + } + + if(communicator__ != null) + { + try + { + communicator__.destroy(); + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(ex.ToString()); + status = 1; + } + catch(System.Exception ex) + { + Ice.Util.getProcessLogger().error("unknown exception:\n" + ex.ToString()); + status = 1; + } + communicator__ = null; + } + + // + // Reset internal state. We cannot reset the Application state + // here, since destroyed__ must remain true until we re-run + // this method. + // + _adapter = null; + _router = null; + _session = null; + _createdSession = false; + _category = null; + + return restart; + } + + private static Ice.ObjectAdapter _adapter; + private static Glacier2.RouterPrx _router; + private static Glacier2.SessionPrx _session; + private static bool _createdSession = false; + private static string _category; +} + +} +#endif diff --git a/csharp/src/Glacier2/AssemblyInfo.cs b/csharp/src/Glacier2/AssemblyInfo.cs new file mode 100644 index 00000000000..661acc6d7e8 --- /dev/null +++ b/csharp/src/Glacier2/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("Glacier2")] +[assembly: AssemblyDescription("Glacier2 run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Glacier2 for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/Glacier2/Makefile b/csharp/src/Glacier2/Makefile new file mode 100644 index 00000000000..e54132c9f6f --- /dev/null +++ b/csharp/src/Glacier2/Makefile @@ -0,0 +1,54 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = Glacier2 +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = Application.cs \ + AssemblyInfo.cs \ + SessionCallback.cs \ + SessionFactoryHelper.cs \ + SessionHelper.cs + +SLICE_SRCS = $(SDIR)/Metrics.ice \ + $(SDIR)/PermissionsVerifier.ice \ + $(SDIR)/Router.ice \ + $(SDIR)/Session.ice \ + $(SDIR)/SSLInfo.ice + +SDIR = $(slicedir)/Glacier2 +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) -I$(slicedir) --ice + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/Glacier2/Makefile.mak b/csharp/src/Glacier2/Makefile.mak new file mode 100755 index 00000000000..950fca5125a --- /dev/null +++ b/csharp/src/Glacier2/Makefile.mak @@ -0,0 +1,61 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = Glacier2 +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = Application.cs \ + AssemblyInfo.cs \ + SessionCallback.cs \ + SessionFactoryHelper.cs \ + SessionHelper.cs + +GEN_SRCS = $(GDIR)\Metrics.cs \ + $(GDIR)\PermissionsVerifier.cs \ + $(GDIR)\Router.cs \ + $(GDIR)\Session.cs \ + $(GDIR)\SSLInfo.cs + +SDIR = $(slicedir)\Glacier2 +GDIR = generated + +!include $(top_srcdir)/config/Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +# -r:WindowsBase.dll + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) -I$(slicedir) --ice + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) /baseaddress:0x22000000 $(MCSFLAGS) -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/Glacier2/SessionCallback.cs b/csharp/src/Glacier2/SessionCallback.cs new file mode 100644 index 00000000000..55dc238be05 --- /dev/null +++ b/csharp/src/Glacier2/SessionCallback.cs @@ -0,0 +1,51 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; + +namespace Glacier2 +{ +/// <summary> +/// A callback class to get notifications of status changes in the +/// Glacier2 session. All callbacks on the SessionCallback interface +/// occur in the main swing thread. +/// </summary> +public interface SessionCallback +{ + /// <summary> + /// Notifies the application that the communicator was created. + /// </summary> + /// <param name="session">The Glacier2 session.</param> + void createdCommunicator(SessionHelper session); + + /// <summary> + /// Notifies the application that the Glacier2 session has + /// been established. + /// </summary> + /// <param name="session">The established session.</param> + void connected(SessionHelper session); + + /// <summary> + /// Notifies the application that the Glacier2 session has been + /// disconnected. + /// </summary> + /// <param name="session">The disconnected session.</param> + void disconnected(SessionHelper session); + + /// <summary> + /// Notifies the application that the Glacier2 session + /// establishment failed. + /// </summary> + /// <param name="session">The session reporting the connection + /// failure.</param> + /// <param name="ex">The exception.</param> + void connectFailed(SessionHelper session, Exception ex); +} + +} diff --git a/csharp/src/Glacier2/SessionFactoryHelper.cs b/csharp/src/Glacier2/SessionFactoryHelper.cs new file mode 100644 index 00000000000..c37aff27581 --- /dev/null +++ b/csharp/src/Glacier2/SessionFactoryHelper.cs @@ -0,0 +1,420 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Text; +using System.Collections.Generic; + +namespace Glacier2 +{ + +/// <summary> +/// A helper class for using Glacier2 with GUI applications. +/// +/// Applications should create a session factory for each Glacier2 router to which the application will +/// connect. To connect with the Glacier2 router, call SessionFactory.connect. The callback object is +/// notified of the various life cycle events. Once the session is torn down for whatever reason, the +/// application can use the session factory to create another connection. +/// </summary> +public class SessionFactoryHelper +{ + /// <summary> + /// Creates a SessionFactory object. + /// </summary> + /// <param name="callback">The callback object for notifications.</param> + public + SessionFactoryHelper(SessionCallback callback) + { + _callback = callback; + _initData = new Ice.InitializationData(); + _initData.properties = Ice.Util.createProperties(); + setDefaultProperties(); + } + + /// <summary> + /// Creates a SessionFactory object. + /// </summary> + /// <param name="initData">The initialization data to use when creating the communicator.</param> + /// <param name="callback">The callback object for notifications.</param> + public + SessionFactoryHelper(Ice.InitializationData initData, SessionCallback callback) + { + _callback = callback; + _initData = initData; + if(_initData.properties == null) + { + _initData.properties = Ice.Util.createProperties(); + } + setDefaultProperties(); + } + + /// <summary> + /// Creates a SessionFactory object. + /// </summary> + /// <param name="properties">The properties to use when creating the communicator.</param> + /// <param name="callback">The callback object for notifications.</param> + public + SessionFactoryHelper(Ice.Properties properties, SessionCallback callback) + { + if(properties == null) + { + throw new Ice.InitializationException( + "Attempt to create a SessionFactoryHelper with a null Properties argument"); + } + _callback = callback; + _initData = new Ice.InitializationData(); + _initData.properties = properties; + setDefaultProperties(); + } + + /// <summary> + /// Set the router object identity. + /// </summary> + /// <param name="identity">The Glacier2 router's identity.</param> + public void + setRouterIdentity(Ice.Identity identity) + { + lock(this) + { + _identity = identity; + } + } + + /// <summary> + /// Returns the object identity of the Glacier2 router. + /// </summary> + /// <returns> The Glacier2 router's identity.</returns> + public Ice.Identity + getRouterIdentity() + { + lock(this) + { + return _identity; + } + } + + /// <summary> + /// Sets the host on which the Glacier2 router runs. + /// </summary> + /// <param name="hostname">The host name (or IP address) of the router host.</param> + public void + setRouterHost(string hostname) + { + lock(this) + { + _routerHost = hostname; + } + } + + /// <summary> + /// Returns the host on which the Glacier2 router runs. + /// </summary> + /// <returns>The Glacier2 router host.</returns> + public string + getRouterHost() + { + lock(this) + { + return _routerHost; + } + } + + /// <summary> + /// Sets whether to connect with the Glacier2 router securely. + /// </summary> + /// <param name="secure">If true, the client connects to the router + /// via SSL; otherwise, the client connects via TCP.</param> + [Obsolete("This method is deprecated. Use SessionFactoryHelper.setProtocol instead.")] + public void + setSecure(bool secure) + { + setProtocol(secure ? "ssl" : "tcp"); + } + + /// <summary> + /// Returns whether the session factory will establish a secure connection to the Glacier2 router. + /// </summary> + /// <returns>The secure flag.</returns> + [Obsolete("This method is deprecated. Use SessionFactoryHelper.getProtocol instead.")] + public bool + getSecure() + { + return getProtocol().Equals("ssl"); + } + + /// <summary> + /// Sets the protocol that will be used by the session factory to establish the connection.. + /// </summary> + /// <param name="protocol">The protocol.</param> + public void + setProtocol(String protocol) + { + lock(this) + { + if(protocol == null) + { + throw new ArgumentException("You must use a valid protocol"); + } + + if(!protocol.Equals("tcp") && + !protocol.Equals("ssl") && + !protocol.Equals("wss") && + !protocol.Equals("ws")) + { + throw new ArgumentException("Unknown protocol `" + protocol + "'"); + } + _protocol = protocol; + } + } + + /// <summary> + /// Returns the protocol that will be used by the session factory to establish the connection. + /// </summary> + /// <returns>The protocol.</returns> + public String + getProtocol() + { + lock(this) + { + return _protocol; + } + } + + /// <summary> + /// Sets the connect and connection timeout for the Glacier2 router. + /// </summary> + /// <param name="timeoutMillisecs">The timeout in milliseconds. A zero + /// or negative timeout value indicates that the router proxy has no + /// associated timeout.</param> + public void + setTimeout(int timeoutMillisecs) + { + lock(this) + { + _timeout = timeoutMillisecs; + } + } + + /// <summary> + /// Returns the connect and connection timeout associated with the Glacier2 router. + /// </summary> + /// <returns>The timeout.</returns> + public int + getTimeout() + { + lock(this) + { + return _timeout; + } + } + + /// <summary> + /// Sets the Glacier2 router port to connect to. + /// </summary> + /// <param name="port">The port. If 0, then the default port (4063 for TCP or + /// 4064 for SSL) is used.</param> + public void + setPort(int port) + { + lock(this) + { + _port = port; + } + } + + /// <summary> + /// Returns the Glacier2 router port to connect to. + /// </summary> + /// <returns>The port.</returns> + public int + getPort() + { + lock(this) + { + return getPortInternal(); + } + } + + private int + getPortInternal() + { + return _port == 0 ? ((_protocol.Equals("ssl") || + _protocol.Equals("wss"))? GLACIER2_SSL_PORT : GLACIER2_TCP_PORT) : _port; + } + + /** + * Returns the initialization data used to initialize the communicator. + * + * @return The initialization data. + */ + public Ice.InitializationData + getInitializationData() + { + lock(this) + { + return _initData; + } + } + + /// <summary> + /// Sets the request context to use while establishing a connection to the Glacier2 router. + /// </summary> + /// <param name="context">The request context.</param> + public void + setConnectContext(Dictionary<string, string> context) + { + lock(this) + { + _context = context; + } + } + + /// <summary> + /// Determines whether the session should create an object adapter that the client + /// can use for receiving callbacks. + /// </summary> + /// <param name="useCallbacks">True if the session should create an object adapter.</param> + public void + setUseCallbacks(bool useCallbacks) + { + lock(this) + { + _useCallbacks = useCallbacks; + } + } + + /// <summary> + /// Indicates whether a newly-created session will also create an object adapter that + /// the client can use for receiving callbaks. + /// </summary> + /// <returns>True if the session will create an object adapter.</returns> + public bool + getUseCallbacks() + { + lock(this) + { + return _useCallbacks; + } + } + + /// <summary> + /// Connects to the Glacier2 router using the associated SSL credentials. + /// + /// Once the connection is established, SesssionCallback.connected is called on + /// the callback object; upon failure, SessionCallback.connectFailed is called + /// with the exception. + /// </summary> + /// <returns>The connected session.</returns> + public SessionHelper + connect() + { + lock(this) + { + SessionHelper session = new SessionHelper(_callback, createInitData(), getRouterFinderStr(), _useCallbacks); + session.connect(_context); + return session; + } + } + + /// <summary> + /// Connect the Glacier2 session using user name and password credentials. + /// + /// Once the connection is established, SessionCallback.connected is called on + /// the callback object; upon failure, SessionCallback.connectFailed is called + /// with the exception. + /// </summary> + /// <param name="username">The user name.</param> + /// <param name="password">The password.</param> + /// <returns>The connected session.</returns> + public SessionHelper + connect( string username, string password) + { + lock(this) + { + SessionHelper session = new SessionHelper(_callback, createInitData(), getRouterFinderStr(), _useCallbacks); + session.connect(username, password, _context); + return session; + } + } + + private Ice.InitializationData + createInitData() + { + // + // Clone the initialization data and properties. + // + Ice.InitializationData initData = (Ice.InitializationData)_initData.Clone(); + initData.properties = initData.properties.ice_clone_(); + + if(initData.properties.getProperty("Ice.Default.Router").Length == 0 && _identity != null) + { + initData.properties.setProperty("Ice.Default.Router", createProxyStr(_identity)); + } + + // + // If using a secure connection setup the IceSSL plug-in, if IceSSL + // plug-in has already been setup we don't want to override the + // configuration so it can be loaded from a custom location. + // + if((_protocol.Equals("ssl") || _protocol.Equals("wss")) && + initData.properties.getProperty("Ice.Plugin.IceSSL").Length == 0) + { + initData.properties.setProperty("Ice.Plugin.IceSSL", "IceSSL:IceSSL.PluginFactory"); + } + + return initData; + } + + private string + getRouterFinderStr() + { + Ice.Identity ident = new Ice.Identity("RouterFinder", "Ice"); + return createProxyStr(ident); + } + + private string + createProxyStr(Ice.Identity ident) + { + StringBuilder sb = new StringBuilder(); + sb.Append("\""); + sb.Append(Ice.Util.identityToString(ident)); + sb.Append("\":"); + sb.Append(_protocol + " -p "); + sb.Append(getPortInternal()); + sb.Append(" -h \""); + sb.Append(_routerHost); + sb.Append("\""); + if(_timeout > 0) + { + sb.Append(" -t "); + sb.Append(_timeout); + } + return sb.ToString(); + } + + private void + setDefaultProperties() + { + _initData.properties.setProperty("Ice.RetryIntervals", "-1"); + } + + private SessionCallback _callback; + private string _routerHost = "localhost"; + private Ice.InitializationData _initData; + private Ice.Identity _identity = null; + private string _protocol = "ssl"; + private int _port = 0; + private int _timeout = 10000; + private Dictionary<string, string> _context; + private bool _useCallbacks = true; + private static int GLACIER2_SSL_PORT = 4064; + private static int GLACIER2_TCP_PORT = 4063; +} + +} diff --git a/csharp/src/Glacier2/SessionHelper.cs b/csharp/src/Glacier2/SessionHelper.cs new file mode 100644 index 00000000000..f9432d6003f --- /dev/null +++ b/csharp/src/Glacier2/SessionHelper.cs @@ -0,0 +1,526 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Diagnostics; +using System.Threading; +using System.Collections.Generic; + +namespace Glacier2 +{ + +/// <summary> +/// A helper class for using Glacier2 with GUI applications. +/// </summary> +public class SessionHelper +{ + private class ConnectionCallbackI : Ice.ConnectionCallback + { + internal ConnectionCallbackI(SessionHelper sessionHelper) + { + _sessionHelper = sessionHelper; + } + + public void heartbeat(Ice.Connection con) + { + + } + + public void closed(Ice.Connection con) + { + _sessionHelper.destroy(); + } + + private readonly SessionHelper _sessionHelper; + } + + /// <summary> + /// Creates a Glacier2 session. + /// </summary> + /// <param name="callback">The callback for notifications about session + /// establishment.</param> + /// <param name="initData">The Ice.InitializationData for initializing + /// the communicator.</param> + /// <param name="finderStr">The stringified Ice.RouterFinder proxy.</param> + /// <param name="useCallbacks">True if the session should create an object adapter for receiving callbacks.</param> + public SessionHelper(SessionCallback callback, Ice.InitializationData initData, string finderStr, bool useCallbacks) + { + _callback = callback; + _initData = initData; + _finderStr = finderStr; + _useCallbacks = useCallbacks; + } + + /// <summary> + /// Destroys the Glacier2 session. + /// + /// Once the session has been destroyed, SessionCallback.disconnected is + /// called on the associated callback object. + /// </summary> + public void + destroy() + { + lock(this) + { + if(_destroy) + { + return; + } + _destroy = true; + if(!_connected) + { + // + // In this case a connecting session is being + // destroyed. The communicator and session will be + // destroyed when the connection establishment has + // completed. + // + return; + } + _session = null; + _connected = false; + // + // Run destroyInternal in a thread because it makes remote invocations. + // + Thread t = new Thread(new ThreadStart(destroyInternal)); + t.Start(); + } + } + + /// <summary> + /// Returns the session's communicator object. + /// </summary> + /// <returns>The communicator.</returns> + public Ice.Communicator + communicator() + { + lock(this) + { + return _communicator; + } + } + + /// <summary> + /// Returns the category to be used in the identities of all of + /// the client's callback objects. Clients must use this category + /// for the router to forward callback requests to the intended + /// client. + /// </summary> + /// <returns>The category. Throws SessionNotExistException + /// No session exists</returns> + public string + categoryForClient() + { + lock(this) + { + if(_router == null) + { + throw new SessionNotExistException(); + } + + return _category; + } + } + + /// <summary> + /// Adds a servant to the callback object adapter's Active Servant + /// Map with a UUID. + /// </summary> + /// <param name="servant">The servant to add.</param> + /// <returns>The proxy for the servant. Throws SessionNotExistException + /// if no session exists.</returns> + public Ice.ObjectPrx + addWithUUID(Ice.Object servant) + { + lock(this) + { + if(_router == null) + { + throw new SessionNotExistException(); + } + + return internalObjectAdapter().add(servant, new Ice.Identity(Guid.NewGuid().ToString(), _category)); + } + } + + /// <summary> + /// Returns the Glacier2 session proxy. If the session hasn't been + /// established yet, or the session has already been destroyed, + /// throws SessionNotExistException. + /// </summary> + /// <returns>The session proxy, or throws SessionNotExistException + /// if no session exists.</returns> + public Glacier2.SessionPrx + session() + { + lock(this) + { + if(_session == null) + { + throw new SessionNotExistException(); + } + return _session; + } + } + + /// <summary> + /// Returns true if there is an active session, otherwise returns false. + /// </summary> + /// <returns>true if session exists or false if no session exists.</returns> + public bool + isConnected() + { + lock(this) + { + return _connected; + } + } + + /// <summary> + /// Returns an object adapter for callback objects, creating it if necessary. + /// </summary> + /// <return>The object adapter. Throws SessionNotExistException + /// if no session exists.</return> + public Ice.ObjectAdapter + objectAdapter() + { + return internalObjectAdapter(); + } + + private Ice.ObjectAdapter + internalObjectAdapter() + { + lock(this) + { + if(_router == null) + { + throw new SessionNotExistException(); + } + if(!_useCallbacks) + { + throw new Ice.InitializationException( + "Object adapter not available, call SessionFactoryHelper.setUseCallbacks(true)"); + } + return _adapter; + } + } + + /// <summary> + /// Connects to the Glacier2 router using the associated SSL credentials. + /// + /// Once the connection is established, SessionCallback.connected is called on + /// the callback object; upon failure, SessionCallback.exception is called with + /// the exception. + /// </summary> + /// <param name="context">The request context to use when creating the session.</param> + public void + connect(Dictionary<string, string> context) + { + lock(this) + { + connectImpl((RouterPrx router) => + { + return router.createSessionFromSecureConnection(context); + }); + } + } + + /// <summary> + /// Connects a Glacier2 session using user name and password credentials. + /// + /// Once the connection is established, SessionCallback.connected is called on the callback object; + /// upon failure SessionCallback.exception is called with the exception. + /// </summary> + /// <param name="username">The user name.</param> + /// <param name="password">The password.</param> + /// <param name="context">The request context to use when creating the session.</param> + public void + connect(string username, string password, Dictionary<string, string> context) + { + lock(this) + { + connectImpl((RouterPrx router) => + { + return router.createSession(username, password, context); + }); + } + } + + private void + connected(RouterPrx router, SessionPrx session) + { + // + // Remote invocation should be done without acquiring a mutex lock. + // + Debug.Assert(router != null); + Ice.Connection conn = router.ice_getCachedConnection(); + string category = router.getCategoryForClient(); + int acmTimeout = 0; + try + { + acmTimeout = router.getACMTimeout(); + } + catch(Ice.OperationNotExistException) + { + } + + if(acmTimeout <= 0) + { + acmTimeout = (int)router.getSessionTimeout(); + } + + // + // We create the callback object adapter here because createObjectAdapter internally + // makes synchronous RPCs to the router. We can't create the OA on-demand when the + // client calls objectAdapter() or addWithUUID() because they can be called from the + // GUI thread. + // + if(_useCallbacks) + { + Debug.Assert(_adapter == null); + _adapter = _communicator.createObjectAdapterWithRouter("", router); + _adapter.activate(); + } + + lock(this) + { + _router = router; + + if(_destroy) + { + // + // Run destroyInternal in a thread because it makes remote invocations. + // + Thread t = new Thread(new ThreadStart(destroyInternal)); + t.Start(); + return; + } + + // + // Cache the category. + // + _category = category; + + // + // Assign the session after _destroy is checked. + // + _session = session; + _connected = true; + + if(acmTimeout > 0) + { + Ice.Connection connection = _router.ice_getCachedConnection(); + Debug.Assert(connection != null); + connection.setACM(acmTimeout, Ice.Util.None, Ice.ACMHeartbeat.HeartbeatAlways); + connection.setCallback(new ConnectionCallbackI(this)); + } + } + + dispatchCallback(() => + { + try + { + _callback.connected(this); + } + catch(Glacier2.SessionNotExistException) + { + destroy(); + } + }, conn); + } + + private void + destroyInternal() + { + Glacier2.RouterPrx router; + Ice.Communicator communicator; + lock(this) + { + Debug.Assert(_destroy); + if(_router == null) + { + return; + } + router = _router; + _router = null; + + communicator = _communicator; + + Debug.Assert(communicator != null); + } + + try + { + router.destroySession(); + } + catch(Ice.ConnectionLostException) + { + // + // Expected if another thread invoked on an object from the session concurrently. + // + } + catch(SessionNotExistException) + { + // + // This can also occur. + // + } + catch(Exception e) + { + // + // Not expected. + // + communicator.getLogger().warning("SessionHelper: unexpected exception when destroying the session:\n" + e); + } + + try + { + communicator.destroy(); + } + catch(Exception) + { + } + + // Notify the callback that the session is gone. + dispatchCallback(() => + { + _callback.disconnected(this); + }, null); + } + + delegate Glacier2.SessionPrx ConnectStrategy(Glacier2.RouterPrx router); + + private void + connectImpl(ConnectStrategy factory) + { + Debug.Assert(!_destroy); + + try + { + _communicator = Ice.Util.initialize(_initData); + } + catch(Ice.LocalException ex) + { + _destroy = true; + new Thread( + new ThreadStart(() => + { + dispatchCallback(() => + { + _callback.connectFailed(this, ex); + }, + null); + })).Start(); + return; + } + + Ice.RouterFinderPrx finder = Ice.RouterFinderPrxHelper.uncheckedCast(_communicator.stringToProxy(_finderStr)); + new Thread(new ThreadStart(() => + { + if(_communicator.getDefaultRouter() == null) + { + try + { + _communicator.setDefaultRouter(finder.getRouter()); + } + catch(Exception) + { + // + // In case of error getting router identity from RouterFinder use default identity. + // + Ice.Identity ident = new Ice.Identity("router", "Glacier2"); + _communicator.setDefaultRouter(Ice.RouterPrxHelper.uncheckedCast(finder.ice_identity(ident))); + } + } + + try + { + dispatchCallbackAndWait(() => + { + _callback.createdCommunicator(this); + }); + + Glacier2.RouterPrx routerPrx = Glacier2.RouterPrxHelper.uncheckedCast(_communicator.getDefaultRouter()); + Glacier2.SessionPrx session = factory(routerPrx); + connected(routerPrx, session); + } + catch(Exception ex) + { + try + { + _communicator.destroy(); + } + catch(Exception) + { + } + dispatchCallback(() => + { + _callback.connectFailed(this, ex); + }, null); + } + })).Start(); + } + +#if COMPACT + private void + dispatchCallback(Ice.VoidAction callback, Ice.Connection conn) +#else + private void + dispatchCallback(System.Action callback, Ice.Connection conn) +#endif + { + if(_initData.dispatcher != null) + { + _initData.dispatcher(callback, conn); + } + else + { + callback(); + } + } + +#if COMPACT + private void + dispatchCallbackAndWait(Ice.VoidAction callback) +#else + private void + dispatchCallbackAndWait(System.Action callback) +#endif + { + if(_initData.dispatcher != null) + { + EventWaitHandle h = new ManualResetEvent(false); + _initData.dispatcher(() => + { + callback(); + h.Set(); + }, null); + h.WaitOne(); + } + else + { + callback(); + } + } + + private readonly Ice.InitializationData _initData; + private Ice.Communicator _communicator; + private Ice.ObjectAdapter _adapter; + private Glacier2.RouterPrx _router; + private Glacier2.SessionPrx _session; + private bool _connected = false; + private string _category; + private string _finderStr; + private bool _useCallbacks; + + private readonly SessionCallback _callback; + private bool _destroy = false; +} + +} diff --git a/csharp/src/Glacier2/generated/.gitignore b/csharp/src/Glacier2/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/Glacier2/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/Ice/.depend.mak b/csharp/src/Ice/.depend.mak new file mode 100644 index 00000000000..48e527e7f4a --- /dev/null +++ b/csharp/src/Ice/.depend.mak @@ -0,0 +1,144 @@ + +BuiltinSequences.cs: \ + "$(slicedir)\Ice\BuiltinSequences.ice" + +Communicator.cs: \ + "$(slicedir)\Ice\Communicator.ice" \ + "$(slicedir)/Ice/LoggerF.ice" \ + "$(slicedir)/Ice/InstrumentationF.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/ObjectFactoryF.ice" \ + "$(slicedir)/Ice/RouterF.ice" \ + "$(slicedir)/Ice/LocatorF.ice" \ + "$(slicedir)/Ice/PluginF.ice" \ + "$(slicedir)/Ice/ImplicitContextF.ice" \ + "$(slicedir)/Ice/Current.ice" \ + "$(slicedir)/Ice/ConnectionF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/Properties.ice" \ + "$(slicedir)/Ice/PropertiesAdmin.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/FacetMap.ice" + +Connection.cs: \ + "$(slicedir)\Ice\Connection.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Endpoint.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/EndpointF.ice" + +Current.cs: \ + "$(slicedir)\Ice\Current.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/ConnectionF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Version.ice" + +Endpoint.cs: \ + "$(slicedir)\Ice\Endpoint.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/EndpointF.ice" + +EndpointTypes.cs: \ + "$(slicedir)\Ice\EndpointTypes.ice" + +FacetMap.cs: \ + "$(slicedir)\Ice\FacetMap.ice" + +Identity.cs: \ + "$(slicedir)\Ice\Identity.ice" + +ImplicitContext.cs: \ + "$(slicedir)\Ice\ImplicitContext.ice" \ + "$(slicedir)/Ice/LocalException.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/Current.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/ConnectionF.ice" + +Instrumentation.cs: \ + "$(slicedir)\Ice\Instrumentation.ice" \ + "$(slicedir)/Ice/EndpointF.ice" \ + "$(slicedir)/Ice/ConnectionF.ice" \ + "$(slicedir)/Ice/Current.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Version.ice" + +LocalException.cs: \ + "$(slicedir)\Ice\LocalException.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +Locator.cs: \ + "$(slicedir)\Ice\Locator.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/ProcessF.ice" + +Logger.cs: \ + "$(slicedir)\Ice\Logger.ice" + +Metrics.cs: \ + "$(slicedir)\Ice\Metrics.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +ObjectAdapter.cs: \ + "$(slicedir)\Ice\ObjectAdapter.ice" \ + "$(slicedir)/Ice/CommunicatorF.ice" \ + "$(slicedir)/Ice/ServantLocatorF.ice" \ + "$(slicedir)/Ice/LocatorF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/FacetMap.ice" \ + "$(slicedir)/Ice/Endpoint.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/EndpointF.ice" + +ObjectFactory.cs: \ + "$(slicedir)\Ice\ObjectFactory.ice" + +Plugin.cs: \ + "$(slicedir)\Ice\Plugin.ice" \ + "$(slicedir)/Ice/LoggerF.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +Process.cs: \ + "$(slicedir)\Ice\Process.ice" + +Properties.cs: \ + "$(slicedir)\Ice\Properties.ice" \ + "$(slicedir)/Ice/PropertiesAdmin.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +PropertiesAdmin.cs: \ + "$(slicedir)\Ice\PropertiesAdmin.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +RemoteLogger.cs: \ + "$(slicedir)\Ice\RemoteLogger.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +Router.cs: \ + "$(slicedir)\Ice\Router.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +ServantLocator.cs: \ + "$(slicedir)\Ice\ServantLocator.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/Current.ice" \ + "$(slicedir)/Ice/ConnectionF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Version.ice" + +SliceChecksumDict.cs: \ + "$(slicedir)\Ice\SliceChecksumDict.ice" + +Version.cs: \ + "$(slicedir)\Ice\Version.ice" diff --git a/csharp/src/Ice/ACM.cs b/csharp/src/Ice/ACM.cs new file mode 100644 index 00000000000..33936b81907 --- /dev/null +++ b/csharp/src/Ice/ACM.cs @@ -0,0 +1,356 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Diagnostics; + using System.Collections.Generic; + + public sealed class ACMConfig : System.ICloneable + { + internal ACMConfig(bool server) + { + timeout = 60 * 1000; + heartbeat = Ice.ACMHeartbeat.HeartbeatOnInvocation; + close = server ? Ice.ACMClose.CloseOnInvocation : Ice.ACMClose.CloseOnInvocationAndIdle; + } + + public ACMConfig(Ice.Properties p, Ice.Logger l, string prefix, ACMConfig dflt) + { + Debug.Assert(prefix != null); + + string timeoutProperty; + if((prefix.Equals("Ice.ACM.Client") || prefix.Equals("Ice.ACM.Server")) && + p.getProperty(prefix + ".Timeout").Length == 0) + { + timeoutProperty = prefix; // Deprecated property. + } + else + { + timeoutProperty = prefix + ".Timeout"; + }; + + timeout = p.getPropertyAsIntWithDefault(timeoutProperty, dflt.timeout / 1000) * 1000; + + int hb = p.getPropertyAsIntWithDefault(prefix + ".Heartbeat", (int)dflt.heartbeat); + if(hb >= (int)Ice.ACMHeartbeat.HeartbeatOff && hb <= (int)Ice.ACMHeartbeat.HeartbeatAlways) + { + heartbeat = (Ice.ACMHeartbeat)hb; + } + else + { + l.warning("invalid value for property `" + prefix + ".Heartbeat" + + "', default value will be used instead"); + heartbeat = dflt.heartbeat; + } + + int cl = p.getPropertyAsIntWithDefault(prefix + ".Close", (int)dflt.close); + if(cl >= (int)Ice.ACMClose.CloseOff && cl <= (int)Ice.ACMClose.CloseOnIdleForceful) + { + close = (Ice.ACMClose)cl; + } + else + { + l.warning("invalid value for property `" + prefix + ".Close" + + "', default value will be used instead"); + close = dflt.close; + } + } + + public object Clone() + { + return this.MemberwiseClone(); + } + + public int timeout; + public Ice.ACMHeartbeat heartbeat; + public Ice.ACMClose close; + }; + + public interface ACMMonitor : TimerTask + { + void add(Ice.ConnectionI con); + void remove(Ice.ConnectionI con); + void reap(Ice.ConnectionI con); + + ACMMonitor acm(Ice.Optional<int> timeout, Ice.Optional<Ice.ACMClose> c, Ice.Optional<Ice.ACMHeartbeat> h); + Ice.ACM getACM(); + }; + + class FactoryACMMonitor : ACMMonitor + { + internal class Change + { + internal Change(Ice.ConnectionI connection, bool remove) + { + this.connection = connection; + this.remove = remove; + } + + public readonly Ice.ConnectionI connection; + public readonly bool remove; + }; + + internal FactoryACMMonitor(Instance instance, ACMConfig config) + { + _instance = instance; + _config = config; + } + + internal void destroy() + { + lock(this) + { + if(_instance == null) + { + return; + } + _instance = null; + _connections.Clear(); + _changes.Clear(); + } + } + + public void add(Ice.ConnectionI connection) + { + if(_config.timeout == 0) + { + return; + } + + lock(this) + { + if(_connections.Count == 0) + { + _connections.Add(connection); + _instance.timer().scheduleRepeated(this, _config.timeout / 2); + } + else + { + _changes.Add(new Change(connection, false)); + } + } + } + + public void remove(Ice.ConnectionI connection) + { + if(_config.timeout == 0) + { + return; + } + + lock(this) + { + Debug.Assert(_instance != null); + _changes.Add(new Change(connection, true)); + } + } + + public void reap(Ice.ConnectionI connection) + { + lock(this) + { + _reapedConnections.Add(connection); + } + } + + public ACMMonitor acm(Ice.Optional<int> timeout, Ice.Optional<Ice.ACMClose> c, Ice.Optional<Ice.ACMHeartbeat> h) + { + Debug.Assert(_instance != null); + + ACMConfig config = (ACMConfig)_config.Clone(); + if(timeout.HasValue) + { + config.timeout = timeout.Value * 1000; // To milliseconds + } + if(c.HasValue) + { + config.close = c.Value; + } + if(h.HasValue) + { + config.heartbeat = h.Value; + } + return new ConnectionACMMonitor(this, _instance.timer(), config); + } + + public Ice.ACM getACM() + { + Ice.ACM acm = new Ice.ACM(); + acm.timeout = _config.timeout / 1000; + acm.close = _config.close; + acm.heartbeat = _config.heartbeat; + return acm; + } + + internal List<Ice.ConnectionI> swapReapedConnections() + { + lock(this) + { + if(_reapedConnections.Count == 0) + { + return null; + } + List<Ice.ConnectionI> connections = _reapedConnections; + _reapedConnections = new List<Ice.ConnectionI>(); + return connections; + } + } + + public void runTimerTask() + { + lock(this) + { + if(_instance == null) + { + return; + } + + foreach(Change change in _changes) + { + if(change.remove) + { + _connections.Remove(change.connection); + } + else + { + _connections.Add(change.connection); + } + } + _changes.Clear(); + + if(_connections.Count == 0) + { + _instance.timer().cancel(this); + return; + } + } + + + // + // Monitor connections outside the thread synchronization, so + // that connections can be added or removed during monitoring. + // + long now = Time.currentMonotonicTimeMillis(); + foreach(Ice.ConnectionI connection in _connections) + { + try + { + connection.monitor(now, _config); + } + catch(System.Exception ex) + { + handleException(ex); + } + } + } + + internal void handleException(System.Exception ex) + { + lock(this) + { + if(_instance == null) + { + return; + } + _instance.initializationData().logger.error("exception in connection monitor:\n" + ex); + } + } + + private Instance _instance; + readonly private ACMConfig _config; + + private HashSet<Ice.ConnectionI> _connections = new HashSet<Ice.ConnectionI>(); + private List<Change> _changes = new List<Change>(); + private List<Ice.ConnectionI> _reapedConnections = new List<Ice.ConnectionI>(); + }; + + internal class ConnectionACMMonitor : ACMMonitor + { + internal ConnectionACMMonitor(FactoryACMMonitor parent, Timer timer, ACMConfig config) + { + _parent = parent; + _timer = timer; + _config = config; + } + + public void add(Ice.ConnectionI connection) + { + lock(this) + { + Debug.Assert(_connection == null); + _connection = connection; + if(_config.timeout > 0) + { + _timer.scheduleRepeated(this, _config.timeout / 2); + } + } + } + + public void remove(Ice.ConnectionI connection) + { + lock(this) + { + Debug.Assert(_connection == connection); + _connection = null; + if(_config.timeout > 0) + { + _timer.cancel(this); + } + } + } + + public void reap(Ice.ConnectionI connection) + { + _parent.reap(connection); + } + + public ACMMonitor acm(Ice.Optional<int> timeout, Ice.Optional<Ice.ACMClose> c, Ice.Optional<Ice.ACMHeartbeat> h) + { + return _parent.acm(timeout, c, h); + } + + public Ice.ACM getACM() + { + Ice.ACM acm = new Ice.ACM(); + acm.timeout = _config.timeout / 1000; + acm.close = _config.close; + acm.heartbeat = _config.heartbeat; + return acm; + } + + public void runTimerTask() + { + Ice.ConnectionI connection; + lock(this) + { + if(_connection == null) + { + return; + } + connection = _connection; + } + + try + { + connection.monitor(Time.currentMonotonicTimeMillis(), _config); + } + catch(System.Exception ex) + { + _parent.handleException(ex); + } + } + + readonly private FactoryACMMonitor _parent; + readonly private Timer _timer; + readonly private ACMConfig _config; + + private Ice.ConnectionI _connection; + }; +}
\ No newline at end of file diff --git a/csharp/src/Ice/AMDCallback.cs b/csharp/src/Ice/AMDCallback.cs new file mode 100644 index 00000000000..b4b9889c350 --- /dev/null +++ b/csharp/src/Ice/AMDCallback.cs @@ -0,0 +1,26 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// AMDCallback is the interface from which all AMD callbacks are derived. + /// </summary> + public interface AMDCallback + { + /// <summary> + /// Indicates to the Ice run time that an operation completed + /// with a run-time exception. + /// </summary> + /// <param name="ex">The encoded Ice run-time exception. Note that, if ex + /// is a user exception, the caller receives UnknownUserException. + /// Use ice_response to raise user exceptions.</param> + void ice_exception(System.Exception ex); + } +} diff --git a/csharp/src/Ice/Acceptor.cs b/csharp/src/Ice/Acceptor.cs new file mode 100644 index 00000000000..33cef1e1548 --- /dev/null +++ b/csharp/src/Ice/Acceptor.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Net.Sockets; + + public interface Acceptor + { + void close(); + EndpointI listen(); + bool startAccept(AsyncCallback callback, object state); + void finishAccept(); + Transceiver accept(); + string protocol(); + string ToString(); + string toDetailedString(); + } + +} diff --git a/csharp/src/Ice/Application.cs b/csharp/src/Ice/Application.cs new file mode 100644 index 00000000000..937d5e7f801 --- /dev/null +++ b/csharp/src/Ice/Application.cs @@ -0,0 +1,936 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#if !SILVERLIGHT + +namespace Ice +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Threading; + + internal static class NativeMethods + { +#if !COMPACT && !UNITY + // + // Technically it's not necessary to wrap DllImport in conditional compilation because + // the binding occurs at run time and it will never be executed on Mono. However, it + // causes problems for the Compact Framework. + // + [DllImport("kernel32.dll")] + [return: MarshalAsAttribute(UnmanagedType.Bool)] + internal static extern bool + SetConsoleCtrlHandler(CtrlCEventHandler eh, [MarshalAsAttribute(UnmanagedType.Bool)]bool add); +#endif + } + + /// <summary> + /// The signal policy for Ice.Application signal handling. + /// </summary> + public enum SignalPolicy + { + /// <summary> + /// If a signal is received, Ice.Application reacts to the signal + /// by calling Communicator.destroy or Communicator.shutdown, + /// or by calling a custom shutdown hook installed by the application. + /// </summary> + HandleSignals, + + /// <summary> + /// Any signal that is received is not intercepted and takes the default action. + /// </summary> + NoSignalHandling + } + + /// <summary> + /// Utility base class that makes it easy to correctly initialize and finalize + /// the Ice run time, as well as handle signals. Unless the application specifies + /// a logger, Application installs a per-process logger that logs to the standard + /// error output. + /// Applications must create a derived class that implements the run method. + /// A program can contain only one instance of this class. + /// </summary> + public abstract class Application + { + /// <summary> + /// Called once the communicator has been initialized. The derived class must + /// implement run, which is the application's starting method. + /// </summary> + /// <param name="args">The argument vector for the application. Application + /// scans the argument vector passed to main for options that are + /// specific to the Ice run time and removes them; therefore, the vector passed + /// to run is free from Ice-related options and contains only options + /// and arguments that are application-specific.</param> + /// <returns>The run method should return zero for successful termination, and + /// non-zero otherwise. Application.main returns the value returned by run.</returns> + public abstract int run(string[] args); + + /// <summary> + /// Override this method to provide a custom application interrupt + /// hook. You must call callbackOnInterrupt for this method + /// to be called. Note that the interruptCallback can be called + /// concurrently with any other thread (including main) in your + /// application--take appropriate concurrency precautions. + /// </summary> + /// <param name="sig">The cause of the interrupt.</param> + public virtual void interruptCallback(int sig) + { + } + + /// <summary> + /// Initializes an instance that calls Communicator.shutdown if a signal is received. + /// </summary> + public Application() + { + } + + /// <summary> + /// Initializes an instance that handles signals according to the signal policy. + /// </summary> + /// <param name="signalPolicy">Determines how to respond to signals.</param> + public Application(SignalPolicy signalPolicy) + { + signalPolicy__ = signalPolicy; + } + + /// <summary> + /// The application must call main after it has + /// instantiated the derived class. main creates + /// a communicator, establishes the specified signal policy, and, + /// once run returns, destroys the communicator. + /// The method prints an error message for any exception that propagates + /// out of run and ensures that the communicator is + /// destroyed correctly even if run completes abnormally. + /// </summary> + /// <param name="args">The arguments for the application (as passed to Main(string[]) + /// by the operating system.</param> + /// <returns>The value returned by run. If run terminates with an exception, + /// the return value is non-zero.</returns> + public int main(string[] args) + { + return main(args, new InitializationData()); + } + + /// <summary> + /// The application must call main after it has + /// instantiated the derived class. main creates + /// a communicator, establishes the specified signal policy, and, + /// once run returns, destroys the communicator. + /// The method prints an error message for any exception that propagates + /// out of run and ensures that the communicator is + /// destroyed correctly even if run completes abnormally. + /// </summary> + /// <param name="args">The arguments for the application (as passed to Main(string[]) + /// by the operating system.</param> + /// <param name="configFile">The configuration file with which to initialize + /// Ice properties.</param> + /// <returns>The value returned by run. If run terminates with an exception, + /// the return value is non-zero.</returns> + public int main(string[] args, string configFile) + { + if(Util.getProcessLogger() is ConsoleLoggerI) + { + Util.setProcessLogger(new ConsoleLoggerI(appName__)); + } + + InitializationData initData = new InitializationData(); + if(configFile != null) + { + try + { + initData.properties = Util.createProperties(); + initData.properties.load(configFile); + } + catch(Ice.Exception ex) + { + Util.getProcessLogger().error(ex.ToString()); + return 1; + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("unknown exception:\n" + ex); + return 1; + } + } + return main(args, initData); + } + + /// <summary> + /// The application must call main after it has + /// instantiated the derived class. main creates + /// a communicator, establishes the specified signal policy, and, + /// once run returns, destroys the communicator. + /// The method prints an error message for any exception that propagates + /// out of run and ensures that the communicator is + /// destroyed correctly even if run completes abnormally. + /// </summary> + /// <param name="args">The arguments for the application (as passed to Main(string[]) + /// by the operating system.</param> + /// <param name="initializationData">Additional data used to initialize the communicator.</param> + /// <returns>The value returned by run. If run terminates with an exception, + /// the return value is non-zero.</returns> + public int main(string[] args, InitializationData initializationData) + { + if(Util.getProcessLogger() is ConsoleLoggerI) + { + Util.setProcessLogger(new ConsoleLoggerI(appName__)); + } + + if(communicator__ != null) + { + Util.getProcessLogger().error("only one instance of the Application class can be used"); + return 1; + } + + // + // We parse the properties here to extract Ice.ProgramName. + // + InitializationData initData; + if(initializationData != null) + { + initData = (InitializationData)initializationData.Clone(); + } + else + { + initData = new InitializationData(); + } + + try + { + initData.properties = Util.createProperties(ref args, initData.properties); + } + catch(Ice.Exception ex) + { + Util.getProcessLogger().error(ex.ToString()); + return 1; + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("unknown exception:\n" + ex); + return 1; + } + appName__ = initData.properties.getPropertyWithDefault("Ice.ProgramName", appName__); + + nohup__ = initData.properties.getPropertyAsInt("Ice.Nohup") > 0; + _application = this; + + int status; +#if COMPACT || UNITY + status = doMain(args, initData); +#else + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + if(IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows) + { + _signals = new WindowsSignals(); + } + else + { + _signals = new MonoSignals(); + } + _signals.register(_handler); + + status = doMain(args, initData); + + _signals.destroy(); + _signals = null; + } + else + { + status = doMain(args, initData); + } +#endif + + return status; + } + + /// <summary> + /// Returns the application name (which is also the value of Ice.ProgramName. + /// This method is useful mainly for error messages that + /// include the application name. Because appName is a static method, it is available from anywhere + /// in the program. + /// </summary> + /// <returns>The name of the application.</returns> + public static string appName() + { + return appName__; + } + + /// <summary> + /// Returns the communicator for the application. Because communicator is a static method, + /// it permits access to the communicator from anywhere in the program. Note that, as a consequence, + /// you cannot have more than one instance of Application in a program. + /// </summary> + /// <returns>The communicator for the application.</returns> + public static Communicator communicator() + { + return communicator__; + } + + /// <summary> + /// Instructs Application to call Communicator.destroy on receipt of a signal. + /// This is default signal handling policy established by the default constructor. + /// </summary> + public static void destroyOnInterrupt() + { + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + lock(mutex__) + { + if(_callback == _holdCallback) + { + released__ = true; + System.Threading.Monitor.Pulse(mutex__); + } + _callback = _destroyCallback; + } + } + else + { + Util.getProcessLogger().warning( + "interrupt method called on Application configured to not handle interrupts."); + } + } + + /// <summary> + /// Instructs Application to call Communicator.shutdown on receipt of a signal. + /// </summary> + public static void shutdownOnInterrupt() + { + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + lock(mutex__) + { + if(_callback == _holdCallback) + { + released__ = true; + System.Threading.Monitor.Pulse(mutex__); + } + _callback = _shutdownCallback; + } + } + else + { + Util.getProcessLogger().warning( + "interrupt method called on Application configured to not handle interrupts."); + } + } + + /// <summary> + /// Instructs Application to ignore signals. + /// </summary> + public static void ignoreInterrupt() + { + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + lock(mutex__) + { + if(_callback == _holdCallback) + { + released__ = true; + System.Threading.Monitor.Pulse(mutex__); + } + _callback = null; + } + } + else + { + Util.getProcessLogger().warning( + "interrupt method called on Application configured to not handle interrupts."); + } + } + + /// <summary> + /// Instructs Application to call interruptCallback on receipt of a signal. + /// The derived class can intercept signals by overriding interruptCallback. + /// </summary> + public static void callbackOnInterrupt() + { + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + lock(mutex__) + { + if(_callback == _holdCallback) + { + released__ = true; + System.Threading.Monitor.Pulse(mutex__); + } + _callback = _userCallback; + } + } + else + { + Util.getProcessLogger().warning( + "interrupt method called on Application configured to not handle interrupts."); + } + } + + /// <summary> + /// Instructs Application to call to hold signals. + /// </summary> + public static void holdInterrupt() + { + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + lock(mutex__) + { + if(_callback != _holdCallback) + { + _previousCallback = _callback; + released__ = false; + _callback = _holdCallback; + } + // else, we were already holding signals + } + } + else + { + Util.getProcessLogger().warning( + "interrupt method called on Application configured to not handle interrupts."); + } + } + + /// <summary> + /// Instructs Application respond to signals. If a signal arrived since the last call + /// to holdInterrupt, it is delivered once you call releaseInterrupt. + /// </summary> + public static void releaseInterrupt() + { + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + lock(mutex__) + { + if(_callback == _holdCallback) + { + // + // Note that it's very possible no signal is held; + // in this case the callback is just replaced and + // setting released__ to true and signalling this + // will do no harm. + // + + released__ = true; + _callback = _previousCallback; + System.Threading.Monitor.Pulse(mutex__); + } + // Else nothing to release. + } + } + else + { + Util.getProcessLogger().warning( + "interrupt method called on Application configured to not handle interrupts."); + } + } + + /// <summary> + /// Determines whether the application shut down intentionally or was forced to shut down due to a signal. + /// This is useful for logging purposes. + /// </summary> + /// <returns>True if a signal caused the communicator to shut down; false otherwise.</returns> + public static bool interrupted() + { + lock(mutex__) + { + return interrupted__; + } + } + + protected virtual int doMain(string[] args, InitializationData initData) + { + int status = 0; + + try + { + // + // If the process logger is the default logger, we replace it with a + // a logger which is using the program name for the prefix. + // + if(initData.properties.getProperty("Ice.ProgramName").Length > 0 && + Util.getProcessLogger() is ConsoleLoggerI) + { + Util.setProcessLogger(new ConsoleLoggerI(initData.properties.getProperty("Ice.ProgramName"))); + } + + communicator__ = Util.initialize(ref args, initData); + destroyed__ = false; + + // + // The default is to destroy when a signal is received. + // + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + destroyOnInterrupt(); + } + + status = run(args); + } + catch(Ice.Exception ex) + { + Util.getProcessLogger().error(ex.ToString()); + status = 1; + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("unknown exception:\n" + ex); + status = 1; + } + + // + // Don't want any new interrupt. And at this point + // (post-run), it would not make sense to release a held + // signal to run shutdown or destroy. + // + if(signalPolicy__ == SignalPolicy.HandleSignals) + { + ignoreInterrupt(); + } + + lock(mutex__) + { + while(callbackInProgress__) + { + System.Threading.Monitor.Wait(mutex__); + } + if(destroyed__) + { + communicator__ = null; + } + else + { + destroyed__ = true; + // + // communicator__ != null means that it will be destroyed + // next; destroyed__ == true ensures that any + // remaining callback won't do anything + // + } + _application = null; + } + + if(communicator__ != null) + { + try + { + communicator__.destroy(); + } + catch(Ice.Exception ex) + { + Util.getProcessLogger().error(ex.ToString()); + status = 1; + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("unknown exception:\n" + ex); + status = 1; + } + communicator__ = null; + } + + return status; + } + + // + // First-level handler. + // + private static void signalHandler(int sig) + { + Callback callback; + + lock(mutex__) + { + callback = _callback; + } + + if(callback != null) + { + try + { + callback(sig); + } + catch(System.Exception) + { + Debug.Assert(false); + } + } + } + + // + // The callbacks to be invoked from the handler. + // + private static void holdInterruptCallback(int sig) + { + Callback callback = null; + lock(mutex__) + { + while(!released__) + { + System.Threading.Monitor.Wait(mutex__); + } + + if(destroyed__) + { + // + // Being destroyed by main thread + // + return; + } + + callback = _callback; + } + + if(callback != null) + { + callback(sig); + } + } + + // + // The callbacks to be invoked from the handler. + // + private static void destroyOnInterruptCallback(int sig) + { + lock(mutex__) + { + if(destroyed__) + { + // + // Being destroyed by main thread + // + return; + } + if(nohup__ && sig == SIGHUP) + { + return; + } + + Debug.Assert(!callbackInProgress__); + callbackInProgress__ = true; + interrupted__ = true; + destroyed__ = true; + } + + try + { + Debug.Assert(communicator__ != null); + communicator__.destroy(); + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("(while destroying in response to signal " + sig + "):\n" + ex); + } + + lock(mutex__) + { + callbackInProgress__ = false; + System.Threading.Monitor.Pulse(mutex__); + } + } + + private static void shutdownOnInterruptCallback(int sig) + { + lock(mutex__) + { + if(destroyed__) + { + // + // Being destroyed by main thread + // + return; + } + if(nohup__ && sig == SIGHUP) + { + return; + } + + Debug.Assert(!callbackInProgress__); + callbackInProgress__ = true; + interrupted__ = true; + } + + try + { + Debug.Assert(communicator__ != null); + communicator__.shutdown(); + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("(while shutting down in response to signal " + sig + "):\n" + ex); + } + + lock(mutex__) + { + callbackInProgress__ = false; + System.Threading.Monitor.Pulse(mutex__); + } + } + + private static void userCallbackOnInterruptCallback(int sig) + { + lock(mutex__) + { + if(destroyed__) + { + // + // Being destroyed by main thread + // + return; + } + // For SIGHUP the user callback is always called. It can + // decide what to do. + Debug.Assert(!callbackInProgress__); + callbackInProgress__ = true; + interrupted__ = true; + } + + try + { + Debug.Assert(_application != null); + _application.interruptCallback(sig); + } + catch(System.Exception ex) + { + Util.getProcessLogger().error("(while interrupting in response to signal " + sig + "):\n" + ex); + } + + lock(mutex__) + { + callbackInProgress__ = false; + System.Threading.Monitor.Pulse(mutex__); + } + } + + protected static object mutex__ = new object(); + protected static bool callbackInProgress__ = false; + protected static bool destroyed__ = false; + protected static bool interrupted__ = false; + protected static bool released__ = false; + protected static bool nohup__ = false; + protected static SignalPolicy signalPolicy__ = SignalPolicy.HandleSignals; + + private delegate void Callback(int sig); + private static readonly Callback _destroyCallback = new Callback(destroyOnInterruptCallback); + private static readonly Callback _shutdownCallback = new Callback(shutdownOnInterruptCallback); + private static readonly Callback _holdCallback = new Callback(holdInterruptCallback); + private static readonly Callback _userCallback = new Callback(userCallbackOnInterruptCallback); + + private static Callback _callback = null; // Current callback + private static Callback _previousCallback; // Remembers prev. callback when signals are held + + // + // We use FriendlyName instead of Process.GetCurrentProcess().ProcessName because the latter + // is terribly slow. (It takes around 1 second!) + // + protected static string appName__ = AppDomain.CurrentDomain.FriendlyName; + protected static Communicator communicator__; + private static Application _application; + + private static int SIGHUP; + static Application() + { + if(IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows) + { + SIGHUP = 5; // CTRL_LOGOFF_EVENT, from wincon.h + } + else + { + SIGHUP = 1; + } + } + + private delegate void SignalHandler(int sig); + private static readonly SignalHandler _handler = new SignalHandler(signalHandler); +#if !COMPACT && !UNITY + private Signals _signals; + + private interface Signals + { + void register(SignalHandler handler); + void destroy(); + } + + private class MonoSignals : Signals + { + public void register(SignalHandler handler) + { + _handler = handler; + _destroyed = false; + + try + { + // + // Signal handling in Mono is provided in the Mono.Unix.Native namespace. + // We use reflection to do the equivalent of the following: + // + // Stdlib.signal(Signum.SIGHUP, delegate); + // Stdlib.signal(Signum.SIGINT, delegate); + // Stdlib.signal(Signum.SIGTERM, delegate); + // + // We don't use conditional compilation so that the Ice assembly can be + // used without change on Windows and Mono. + // + Assembly a = Assembly.Load( + "Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"); + Type sigs = a.GetType("Mono.Unix.Native.Signum"); + object SIGHUP = Enum.Parse(sigs, "SIGHUP"); + object SIGINT = Enum.Parse(sigs, "SIGINT"); + object SIGTERM = Enum.Parse(sigs, "SIGTERM"); + Type stdlib = a.GetType("Mono.Unix.Native.Stdlib"); + MethodInfo method = stdlib.GetMethod("signal", BindingFlags.Static | BindingFlags.Public); + Type delType = a.GetType("Mono.Unix.Native.SignalHandler"); + Delegate del = Delegate.CreateDelegate(delType, this, "callback"); + object[] args = new object[2]; + args[0] = SIGHUP; + args[1] = del; + method.Invoke(null, args); + args[0] = SIGINT; + args[1] = del; + method.Invoke(null, args); + args[0] = SIGTERM; + args[1] = del; + method.Invoke(null, args); + + // + // Doing certain activities within Mono's signal dispatch thread + // can cause the VM to crash, so we use a separate thread to invoke + // the handler. + // + _thread = new Thread(new ThreadStart(run)); + _thread.IsBackground = true; + _thread.Name = "Ice.Application.SignalThread"; + _thread.Start(); + } + catch(System.DllNotFoundException) + { + // + // The class Mono.Unix.Native.Stdlib requires libMonoPosixHelper.so. Mono raises + // DllNotFoundException if it cannot be found in the shared library search path. + // + Util.getProcessLogger().warning("unable to initialize signals"); + } + } + + public void destroy() + { + lock(_m) + { + _destroyed = true; + System.Threading.Monitor.Pulse(_m); + } + + if(_thread != null) + { + _thread.Join(); + _thread = null; + } + } + + private void callback(int sig) + { + lock(_m) + { + _signals.Add(sig); + System.Threading.Monitor.Pulse(_m); + } + } + + private void run() + { + while(true) + { + List<int> signals = null; + bool destroyed = false; + + lock(_m) + { + if(!_destroyed && _signals.Count == 0) + { + System.Threading.Monitor.Wait(_m); + } + + if(_signals.Count > 0) + { + signals = _signals; + _signals = new List<int>(); + } + + destroyed = _destroyed; + } + + if(signals != null) + { + foreach(int sig in signals) + { + _handler(sig); + } + } + + if(destroyed) + { + break; + } + } + } + + private static SignalHandler _handler; + private static bool _destroyed; + private static object _m = new object(); + private static Thread _thread; + private static List<int> _signals = new List<int>(); + } + + private class WindowsSignals : Signals + { +#if MANAGED + public void register(SignalHandler handler) + { + _handler = handler; + Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs args) + { + args.Cancel = true; + _handler(0); + }; + } + + public void destroy() + { + } +#else + public void register(SignalHandler handler) + { + _handler = handler; + _callback = new CtrlCEventHandler(callback); + + bool rc = NativeMethods.SetConsoleCtrlHandler(_callback, true); + Debug.Assert(rc); + } + + public void destroy() + { + } + + private CtrlCEventHandler _callback; + + private bool callback(int sig) + { + _handler(sig); + return true; + } +#endif + private SignalHandler _handler; + } +#endif + } + + delegate bool CtrlCEventHandler(int sig); +} +#endif diff --git a/csharp/src/Ice/Arrays.cs b/csharp/src/Ice/Arrays.cs new file mode 100644 index 00000000000..de6485c54d9 --- /dev/null +++ b/csharp/src/Ice/Arrays.cs @@ -0,0 +1,122 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; + +namespace IceUtilInternal +{ + + public sealed class Arrays + { + public static bool Equals(object[] arr1, object[] arr2) + { + if(object.ReferenceEquals(arr1, arr2)) + { + return true; + } + + if((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) + { + return false; + } + + if(arr1.Length == arr2.Length) + { + for(int i = 0; i < arr1.Length; i++) + { + if(arr1[i] == null) + { + if(arr2[i] != null) + { + return false; + } + } + else if(!arr1[i].Equals(arr2[i])) + { + return false; + } + } + + return true; + } + + return false; + } + + public static bool Equals(Array arr1, Array arr2) + { + if(object.ReferenceEquals(arr1, arr2)) + { + return true; + } + + if((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) + { + return false; + } + + if(arr1.Length == arr2.Length) + { + IEnumerator e1 = arr1.GetEnumerator(); + IEnumerator e2 = arr2.GetEnumerator(); + while(e1.MoveNext()) + { + e2.MoveNext(); + if(e1.Current == null) + { + if(e2.Current != null) + { + return false; + } + } + else if(!e1.Current.Equals(e2.Current)) + { + return false; + } + } + + return true; + } + + return false; + } + + public static int GetHashCode(object[] arr) + { + int h = 5381; + + for(int i = 0; i < arr.Length; i++) + { + object o = arr[i]; + if(o != null) + { + IceInternal.HashUtil.hashAdd(ref h, o); + } + } + + return h; + } + + public static int GetHashCode(Array arr) + { + int h = 0; + + foreach(object o in arr) + { + if(o != null) + { + IceInternal.HashUtil.hashAdd(ref h, o); + } + } + + return h; + } + } +} diff --git a/csharp/src/Ice/AssemblyInfo.cs b/csharp/src/Ice/AssemblyInfo.cs new file mode 100644 index 00000000000..d3280805a75 --- /dev/null +++ b/csharp/src/Ice/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("Ice")] +[assembly: AssemblyDescription("Ice core run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Ice for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/Ice/AssemblyUtil.cs b/csharp/src/Ice/AssemblyUtil.cs new file mode 100644 index 00000000000..0d97f9a9970 --- /dev/null +++ b/csharp/src/Ice/AssemblyUtil.cs @@ -0,0 +1,288 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using System.Threading; + + public sealed class AssemblyUtil + { + public enum Runtime { DotNET, Mono }; + public enum Platform { Windows, NonWindows }; + + static AssemblyUtil() + { + PlatformID id = Environment.OSVersion.Platform; + if( id == PlatformID.Win32NT + || id == PlatformID.Win32S + || id == PlatformID.Win32Windows + || id == PlatformID.WinCE) + { + platform_ = Platform.Windows; + } + else + { + platform_ = Platform.NonWindows; + } + + if(System.Type.GetType("Mono.Runtime") != null) + { + runtime_ = Runtime.Mono; + } + else + { + runtime_ = Runtime.DotNET; + } + + System.Version v = System.Environment.Version; + runtimeMajor_ = v.Major; + runtimeMinor_ = v.Minor; + runtimeBuild_ = v.Build; + runtimeRevision_ = v.Revision; + + v = System.Environment.OSVersion.Version; + xp_ = v.Major == 5 && v.Minor == 1; // Are we running on XP? + + osx_ = false; +#if COMPACT || SILVERLIGHT + // + // Populate the _iceAssemblies list with the fully-qualified names + // of the standard Ice assemblies. The fully-qualified name looks + // like this: + // + // Ice, Version=X.Y.Z.0, Culture=neutral, PublicKeyToken=... + // + string name = Assembly.GetExecutingAssembly().FullName; + _iceAssemblies.Add(name); + int pos = name.IndexOf(','); + if(pos >= 0 && pos < name.Length - 1) + { + // + // Strip off the leading assembly name and use the remainder of the + // string to compose the names of the other standard assemblies. + // + string suffix = name.Substring(pos + 1); + _iceAssemblies.Add("Glacier2," + suffix); + _iceAssemblies.Add("IceBox," + suffix); + _iceAssemblies.Add("IceGrid," + suffix); + _iceAssemblies.Add("IcePatch2," + suffix); + _iceAssemblies.Add("IceStorm," + suffix); + } +#elif !UNITY + if (platform_ == Platform.NonWindows) + { + try + { + Assembly a = Assembly.Load( + "Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"); + Type syscall = a.GetType("Mono.Unix.Native.Syscall"); + if(syscall != null) + { + MethodInfo method = syscall.GetMethod("uname", BindingFlags.Static | BindingFlags.Public); + if(method != null) + { + object[] p = new object[1]; + method.Invoke(null, p); + if(p[0] != null) + { + Type utsname = a.GetType("Mono.Unix.Native.Utsname"); + osx_ = ((string)utsname.GetField("sysname").GetValue(p[0])).Equals("Darwin"); + } + } + } + } + catch(System.Exception) + { + } + } +#endif + } + + public static Type findType(Instance instance, string csharpId) + { + lock(_mutex) + { + Type t; + if (_typeTable.TryGetValue(csharpId, out t)) + { + return t; + } +#if COMPACT || SILVERLIGHT + string[] assemblies = instance.factoryAssemblies(); + for(int i = 0; i < assemblies.Length; ++i) + { + string s = csharpId + "," + assemblies[i]; + if((t = Type.GetType(s)) != null) + { + _typeTable[csharpId] = t; + return t; + } + } + // + // As a last resort, look for the type in the standard Ice assemblies. + // This avoids the need for a program to set a property such as: + // + // Ice.FactoryAssemblies=Ice + // + foreach(string a in _iceAssemblies) + { + string s = csharpId + "," + a; + if((t = Type.GetType(s)) != null) + { + _typeTable[csharpId] = t; + return t; + } + } +#else + loadAssemblies(); // Lazy initialization + foreach (Assembly a in _loadedAssemblies.Values) + { + if((t = a.GetType(csharpId)) != null) + { + _typeTable[csharpId] = t; + return t; + } + } +#endif + } + return null; + } + +#if !COMPACT && !SILVERLIGHT + public static Type[] findTypesWithPrefix(string prefix) + { + LinkedList<Type> l = new LinkedList<Type>(); + + lock(_mutex) + { + loadAssemblies(); // Lazy initialization + foreach(Assembly a in _loadedAssemblies.Values) + { + Type[] types = a.GetTypes(); + foreach(Type t in types) + { + if(t.AssemblyQualifiedName.IndexOf(prefix, StringComparison.Ordinal) == 0) + { + l.AddLast(t); + } + } + } + } + + Type[] result = new Type[l.Count]; + if(l.Count > 0) + { + l.CopyTo(result, 0); + } + return result; + } +#endif + + public static object createInstance(Type t) + { + try + { + return Activator.CreateInstance(t); + } + catch(MemberAccessException) + { + return null; + } + } + +#if !COMPACT && !SILVERLIGHT + // + // Make sure that all assemblies that are referenced by this process + // are actually loaded. This is necessary so we can use reflection + // on any type in any assembly because the type we are after will + // most likely not be in the current assembly and, worse, may be + // in an assembly that has not been loaded yet. (Type.GetType() + // is no good because it looks only in the calling object's assembly + // and mscorlib.dll.) + // + private static void loadAssemblies() + { + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + List<Assembly> newAssemblies = null; + foreach(Assembly a in assemblies) + { + if(!_loadedAssemblies.Contains(a.FullName)) + { + if(newAssemblies == null) + { + newAssemblies = new List<Assembly>(); + } + newAssemblies.Add(a); + _loadedAssemblies[a.FullName] = a; + } + } + if(newAssemblies != null) + { + foreach(Assembly a in newAssemblies) + { + loadReferencedAssemblies(a); + } + } + } + + private static void loadReferencedAssemblies(Assembly a) + { + AssemblyName[] names = a.GetReferencedAssemblies(); + foreach(AssemblyName name in names) + { + if(!_loadedAssemblies.ContainsKey(name.FullName)) + { + try + { + Assembly ra = Assembly.Load(name); + // + // The value of name.FullName may not match that of ra.FullName, so + // we record the assembly using both keys. + // + _loadedAssemblies[name.FullName] = ra; + _loadedAssemblies[ra.FullName] = ra; + loadReferencedAssemblies(ra); + } + catch(System.Exception) + { + // Ignore assemblies that cannot be loaded. + } + } + } + } + + private static Hashtable _loadedAssemblies = new Hashtable(); // <string, Assembly> pairs. +#else + private static List<string> _iceAssemblies = new List<string>(); +#endif + private static Dictionary<string, Type> _typeTable = new Dictionary<string, Type>(); // <type name, Type> pairs. + private static object _mutex = new object(); + + public readonly static Runtime runtime_; // Either DotNET or Mono + // + // Versioning is: Major.Minor.Build.Revision. (Yes, really. It is not Major.Minor.Revision.Build, as + // one might expect.) If a part of a version number (such as revision) is not defined, it is -1. + // + public readonly static int runtimeMajor_; + public readonly static int runtimeMinor_; + public readonly static int runtimeBuild_; + public readonly static int runtimeRevision_; + + public readonly static Platform platform_; + public readonly static bool xp_; + public readonly static bool osx_; + } + +} diff --git a/csharp/src/Ice/AsyncIOThread.cs b/csharp/src/Ice/AsyncIOThread.cs new file mode 100644 index 00000000000..4b4ae5428f2 --- /dev/null +++ b/csharp/src/Ice/AsyncIOThread.cs @@ -0,0 +1,208 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Net; + using System.Threading; + + public class AsyncIOThread + { + internal AsyncIOThread(Instance instance) + { + _instance = instance; + + _thread = new HelperThread(this); + updateObserver(); +#if !SILVERLIGHT + if(instance.initializationData().properties.getProperty("Ice.ThreadPriority").Length > 0) + { + ThreadPriority priority = IceInternal.Util.stringToThreadPriority( + instance.initializationData().properties.getProperty("Ice.ThreadPriority")); + _thread.Start(priority); + } + else + { + _thread.Start(ThreadPriority.Normal); + } +#else + _thread.Start(); +#endif + } + + public void + updateObserver() + { + lock(this) + { + Ice.Instrumentation.CommunicatorObserver obsv = _instance.initializationData().observer; + if(obsv != null) + { + _observer = obsv.getThreadObserver("Communicator", + _thread.getName(), + Ice.Instrumentation.ThreadState.ThreadStateIdle, + _observer); + if(_observer != null) + { + _observer.attach(); + } + } + } + } + + public void queue(ThreadPoolWorkItem callback) + { + lock(this) + { + Debug.Assert(!_destroyed); + _queue.AddLast(callback); + System.Threading.Monitor.Pulse(this); + } + } + + public void destroy() + { + lock(this) + { + Debug.Assert(!_destroyed); + _destroyed = true; + System.Threading.Monitor.Pulse(this); + } + } + + public void joinWithThread() + { + if(_thread != null) + { + _thread.Join(); + } + } + + public void run() + { + LinkedList<ThreadPoolWorkItem> queue = new LinkedList<ThreadPoolWorkItem>(); + bool inUse = false; + while(true) + { + lock(this) + { + if(_observer != null && inUse) + { + _observer.stateChanged(Ice.Instrumentation.ThreadState.ThreadStateInUseForIO, + Ice.Instrumentation.ThreadState.ThreadStateIdle); + inUse = false; + } + + if(_destroyed && _queue.Count == 0) + { + break; + } + + while(!_destroyed && _queue.Count == 0) + { + System.Threading.Monitor.Wait(this); + } + + LinkedList<ThreadPoolWorkItem> tmp = queue; + queue = _queue; + _queue = tmp; + + if(_observer != null) + { + _observer.stateChanged(Ice.Instrumentation.ThreadState.ThreadStateIdle, + Ice.Instrumentation.ThreadState.ThreadStateInUseForIO); + inUse = true; + } + } + + foreach(ThreadPoolWorkItem cb in queue) + { + try + { + cb(); + } + catch(Ice.LocalException ex) + { + string s = "exception in asynchronous IO thread:\n" + ex; + _instance.initializationData().logger.error(s); + } + catch(System.Exception ex) + { + string s = "unknown exception in asynchronous IO thread:\n" + ex; + _instance.initializationData().logger.error(s); + } + } + queue.Clear(); + } + + if(_observer != null) + { + _observer.detach(); + } + } + + private Instance _instance; + private bool _destroyed; + private LinkedList<ThreadPoolWorkItem> _queue = new LinkedList<ThreadPoolWorkItem>(); + private Ice.Instrumentation.ThreadObserver _observer; + + private sealed class HelperThread + { + internal HelperThread(AsyncIOThread asyncIOThread) + { + _asyncIOThread = asyncIOThread; + _name = _asyncIOThread._instance.initializationData().properties.getProperty("Ice.ProgramName"); + if(_name.Length > 0) + { + _name += "-"; + } + _name += "Ice.AsyncIOThread"; + } + + public void Join() + { + _thread.Join(); + } + + public string getName() + { + return _name; + } + +#if !SILVERLIGHT + public void Start(ThreadPriority priority) +#else + public void Start() +#endif + { + _thread = new Thread(new ThreadStart(Run)); + _thread.IsBackground = true; + _thread.Name = _name; +#if !SILVERLIGHT + _thread.Priority = priority; +#endif + _thread.Start(); + } + + public void Run() + { + _asyncIOThread.run(); + } + + private AsyncIOThread _asyncIOThread; + private string _name; + private Thread _thread; + } + + private HelperThread _thread; + } +} diff --git a/csharp/src/Ice/AsyncResult.cs b/csharp/src/Ice/AsyncResult.cs new file mode 100644 index 00000000000..3e48d72a418 --- /dev/null +++ b/csharp/src/Ice/AsyncResult.cs @@ -0,0 +1,698 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// + /// <summary> + /// Callback that requires the application to down-cast the proxy. + /// </summary> + /// + public delegate void AsyncCallback(AsyncResult r); + + /// + /// <summary> + /// Callback for the successful completion of an operation + /// that returns no data. + /// </summary> + /// + public delegate void OnewayCallback(); + + /// + /// <summary> + /// Callback for the successful completion of an operation + /// that returns no data. + /// </summary> + /// + public delegate void SentCallback(bool sentSynchronously); + + /// + /// <summary> + /// Called when an invocation raises an exception. + /// </summary> + /// + public delegate void ExceptionCallback(Ice.Exception ex); + + /// + /// <summary> + /// <!-- TODO --> + /// </summary> + public interface AsyncResult : System.IAsyncResult + { + void cancel(); + + Ice.Communicator getCommunicator(); + + Ice.Connection getConnection(); + + ObjectPrx getProxy(); + + bool isCompleted_(); + void waitForCompleted(); + + bool isSent(); + void waitForSent(); + + void throwLocalException(); + + bool sentSynchronously(); + + string getOperation(); + + AsyncResult whenSent(Ice.AsyncCallback cb); + AsyncResult whenSent(Ice.SentCallback cb); + + AsyncResult whenCompleted(Ice.ExceptionCallback excb); + } + + public interface AsyncResult<T> : AsyncResult + { + AsyncResult<T> whenCompleted(T cb, Ice.ExceptionCallback excb); + + new AsyncResult<T> whenCompleted(Ice.ExceptionCallback excb); + new AsyncResult<T> whenSent(Ice.SentCallback cb); + } +} + +namespace IceInternal +{ + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + + public delegate void ProxyTwowayCallback<T>(Ice.AsyncResult result, T cb, Ice.ExceptionCallback excb); + public delegate void ProxyOnewayCallback<T>(T cb); + + public class AsyncResultI : Ice.AsyncResult + { + public virtual void cancel() + { + cancel(new Ice.InvocationCanceledException()); + } + + public Ice.Communicator getCommunicator() + { + return _communicator; + } + + public virtual Ice.Connection getConnection() + { + return null; + } + + public virtual Ice.ObjectPrx getProxy() + { + return null; + } + + public bool isCompleted_() + { + lock(this) + { + return (state_ & StateDone) != 0; + } + } + + public void waitForCompleted() + { + lock(this) + { + while((state_ & StateDone) == 0) + { + System.Threading.Monitor.Wait(this); + } + } + } + + public bool isSent() + { + lock(this) + { + return (state_ & StateSent) != 0; + } + } + + public void waitForSent() + { + lock(this) + { + while((state_ & StateSent) == 0 && _exception == null) + { + System.Threading.Monitor.Wait(this); + } + } + } + + public void throwLocalException() + { + lock(this) + { + if(_exception != null) + { + throw _exception; + } + } + } + + public bool sentSynchronously() + { + return sentSynchronously_; // No lock needed + } + + // + // Implementation of System.IAsyncResult properties + // + + public bool IsCompleted + { + get + { + return isCompleted_(); + } + } + + public bool CompletedSynchronously + { + get + { + if(getProxy() != null && getProxy().ice_isTwoway()) + { + return false; + } + return sentSynchronously_; + } + } + + public object AsyncState + { + get + { + return _cookie; // No lock needed, cookie is immutable. + } + } + + public WaitHandle AsyncWaitHandle + { + get + { + lock(this) + { + if(_waitHandle == null) + { +#if SILVERLIGHT + _waitHandle = new ManualResetEvent(false); +#else + _waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset); +#endif + } + if((state_ & StateDone) != 0) + { + _waitHandle.Set(); + } + return _waitHandle; + } + } + } + + public Ice.AsyncResult whenSent(Ice.AsyncCallback cb) + { + lock(this) + { + if(cb == null) + { + throw new System.ArgumentException("callback is null"); + } + if(_sentCallback != null) + { + throw new System.ArgumentException("sent callback already set"); + } + _sentCallback = cb; + if((state_ & StateSent) == 0) + { + return this; + } + } + + if(sentSynchronously_) + { + try + { + _sentCallback(this); + } + catch(System.Exception ex) + { + warning(ex); + } + } + else + { + instance_.clientThreadPool().dispatch(() => + { + try + { + _sentCallback(this); + } + catch(System.Exception ex) + { + warning(ex); + } + }, cachedConnection_); + } + return this; + } + + public Ice.AsyncResult whenSent(Ice.SentCallback cb) + { + lock(this) + { + if(cb == null) + { + throw new System.ArgumentException("callback is null"); + } + if(_sentCallback != null) + { + throw new System.ArgumentException("sent callback already set"); + } + _sentCallback = (Ice.AsyncResult result) => + { + cb(result.sentSynchronously()); + }; + if((state_ & StateSent) == 0) + { + return this; + } + } + + if(sentSynchronously_) + { + try + { + cb(true); + } + catch(System.Exception ex) + { + warning(ex); + } + } + else + { + instance_.clientThreadPool().dispatch(() => + { + try + { + cb(false); + } + catch(System.Exception ex) + { + warning(ex); + } + }, cachedConnection_); + } + return this; + } + + public Ice.AsyncResult whenCompletedWithAsyncCallback(Ice.AsyncCallback cb) + { + setCompletedCallback(cb); + return this; + } + + public Ice.AsyncResult whenCompleted(Ice.ExceptionCallback cb) + { + if(cb == null) + { + throw new System.ArgumentException("callback is null"); + } + lock(this) + { + if(exceptionCallback_ != null) + { + throw new System.ArgumentException("callback already set"); + } + exceptionCallback_ = cb; + } + setCompletedCallback(getCompletedCallback()); + return this; + } + + public string getOperation() + { + return _operation; + } + + public void invokeSent(Ice.AsyncCallback cb) + { + Debug.Assert(cb != null); + try + { + cb(this); + } + catch(System.Exception ex) + { + warning(ex); + } + + if(observer_ != null) + { + Ice.ObjectPrx proxy = getProxy(); + if(proxy == null || !proxy.ice_isTwoway()) + { + observer_.detach(); + observer_ = null; + } + } + } + + public void invokeSentAsync(Ice.AsyncCallback cb) + { + // + // This is called when it's not safe to call the exception callback synchronously + // from this thread. Instead the exception callback is called asynchronously from + // the client thread pool. + // + Debug.Assert(cb != null); + try + { + instance_.clientThreadPool().dispatch(() => + { + invokeSent(cb); + }, cachedConnection_); + } + catch(Ice.CommunicatorDestroyedException) + { + } + } + + public void invokeCompleted(Ice.AsyncCallback cb) + { + Debug.Assert(cb != null); + try + { + cb(this); + } + catch(System.Exception ex) + { + warning(ex); + } + + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + } + + public void invokeCompletedAsync(Ice.AsyncCallback cb) + { + // + // This is called when it's not safe to call the exception callback synchronously + // from this thread. Instead the exception callback is called asynchronously from + // the client thread pool. + // + Debug.Assert(cb != null); + + // CommunicatorDestroyedException is the only exception that can propagate directly. + instance_.clientThreadPool().dispatch(() => + { + invokeCompleted(cb); + }, cachedConnection_); + } + + public virtual void cancelable(CancellationHandler handler) + { + lock(this) + { + if(_cancellationException != null) + { + try + { + throw _cancellationException; + } + finally + { + _cancellationException = null; + } + } + _cancellationHandler = handler; + } + } + + public bool wait() + { + lock(this) + { + if((state_ & StateEndCalled) != 0) + { + throw new System.ArgumentException("end_ method called more than once"); + } + state_ |= StateEndCalled; + while((state_ & StateDone) == 0) + { + System.Threading.Monitor.Wait(this); + } + if(_exception != null) + { + throw _exception; + } + return (state_ & StateOK) != 0; + } + } + + public virtual void cacheMessageBuffers() + { + } + + protected AsyncResultI(Ice.Communicator communicator, Instance instance, string op, object cookie) + { + instance_ = instance; + sentSynchronously_ = false; + state_ = 0; + + _communicator = communicator; + _operation = op; + _exception = null; + _cookie = cookie; + } + + protected Ice.AsyncCallback sent(bool done) + { + lock(this) + { + Debug.Assert(_exception == null); + + bool alreadySent = (state_ & StateSent) != 0; + state_ |= StateSent; + if(done) + { + state_ |= StateDone | StateOK; + _cancellationHandler = null; + if(observer_ != null && _sentCallback == null) + { + observer_.detach(); + observer_ = null; + } + + // + // For oneway requests after the data has been sent + // the buffers can be reused unless this is a + // collocated invocation. For collocated invocations + // the buffer won't be reused because it has already + // been marked as cached in invokeCollocated. + // + cacheMessageBuffers(); + } + if(_waitHandle != null) + { + _waitHandle.Set(); + } + System.Threading.Monitor.PulseAll(this); + return !alreadySent ? _sentCallback : null; + } + } + + protected Ice.AsyncCallback finished(bool ok) + { + lock(this) + { + state_ |= StateDone; + if(ok) + { + state_ |= StateOK; + } + _cancellationHandler = null; + if(_completedCallback == null) + { + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + } + if(_waitHandle != null) + { + _waitHandle.Set(); + } + System.Threading.Monitor.PulseAll(this); + return _completedCallback; + } + } + + protected Ice.AsyncCallback finished(Ice.Exception ex) + { + lock(this) + { + state_ |= StateDone; + _exception = ex; + _cancellationHandler = null; + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + if(_completedCallback == null) + { + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + } + if(_waitHandle != null) + { + _waitHandle.Set(); + } + System.Threading.Monitor.PulseAll(this); + return _completedCallback; + } + } + + protected void setCompletedCallback(Ice.AsyncCallback cb) + { + lock(this) + { + if(cb == null) + { + throw new System.ArgumentException("callback is null"); + } + if(_completedCallback != null) + { + throw new System.ArgumentException("callback already set"); + } + _completedCallback = cb; + if((state_ & StateDone) == 0) + { + return; + } + else if((getProxy() == null || !getProxy().ice_isTwoway()) && _exception == null) + { + return; + } + } + + instance_.clientThreadPool().dispatch(() => + { + try + { + cb(this); + } + catch(System.Exception ex) + { + warning(ex); + } + }, cachedConnection_); + } + + protected virtual Ice.AsyncCallback getCompletedCallback() + { + return (Ice.AsyncResult result) => + { + Debug.Assert(exceptionCallback_ != null); + try + { + ((AsyncResultI)result).wait(); + } + catch(Ice.Exception ex) + { + exceptionCallback_(ex); + return; + } + }; + } + + protected void cancel(Ice.LocalException ex) + { + CancellationHandler handler; + lock(this) + { + _cancellationException = ex; + if(_cancellationHandler == null) + { + return; + } + handler = _cancellationHandler; + } + handler.asyncRequestCanceled((OutgoingAsyncBase)this, ex); + } + + protected virtual Ice.Instrumentation.InvocationObserver getObserver() + { + return observer_; + } + + protected static T check<T>(Ice.AsyncResult r, string operation) + { + if(r == null) + { + throw new System.ArgumentException("AsyncResult == null"); + } + if(r.getOperation() != operation) + { + throw new System.ArgumentException("Incorrect operation for end_" + operation + " method: " + + r.getOperation()); + } + if(!(r is T)) + { + throw new System.ArgumentException("Incorrect AsyncResult object for end_" + operation + " method"); + } + return (T)r; + } + + protected void warning(System.Exception ex) + { + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.AMICallback", 1) > 0) + { + instance_.initializationData().logger.warning("exception raised by AMI callback:\n" + ex); + } + } + + protected IceInternal.Instance instance_; + protected Ice.Instrumentation.InvocationObserver observer_; + protected Ice.Connection cachedConnection_; + protected bool sentSynchronously_; + + private readonly Ice.Communicator _communicator; + private readonly string _operation; + private readonly object _cookie; + private Ice.Exception _exception; + private EventWaitHandle _waitHandle; + + private CancellationHandler _cancellationHandler; + private Ice.LocalException _cancellationException; + + private Ice.AsyncCallback _completedCallback; + private Ice.AsyncCallback _sentCallback; + protected Ice.ExceptionCallback exceptionCallback_; + + protected const int StateOK = 0x1; + protected const int StateDone = 0x2; + protected const int StateSent = 0x4; + protected const int StateEndCalled = 0x8; + protected const int StateCachedBuffers = 0x10; + protected int state_; + } +} diff --git a/csharp/src/Ice/Base64.cs b/csharp/src/Ice/Base64.cs new file mode 100644 index 00000000000..c60b800425a --- /dev/null +++ b/csharp/src/Ice/Base64.cs @@ -0,0 +1,276 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceUtilInternal +{ + +public class Base64 +{ + +public static string +encode(byte[] plainSeq) +{ + if(plainSeq == null || plainSeq.Length == 0) + { + return ""; + } + + System.Text.StringBuilder retval = new System.Text.StringBuilder(); + int base64Bytes = (((plainSeq.Length * 4) / 3) + 1); + int newlineBytes = (((base64Bytes * 2) / 76) + 1); + int totalBytes = base64Bytes + newlineBytes; + + retval.Capacity = totalBytes; + + byte by1; + byte by2; + byte by3; + byte by4; + byte by5; + byte by6; + byte by7; + + for(int i = 0; i < plainSeq.Length; i += 3) + { + by1 = plainSeq[i]; + by2 = 0; + by3 = 0; + + if((i + 1) < plainSeq.Length) + { + by2 = plainSeq[i+1]; + } + + if((i + 2) < plainSeq.Length) + { + by3 = plainSeq[i+2]; + } + + by4 = (byte)(by1 >> 2); + by5 = (byte)(((by1 & 0x3) << 4) | (by2 >> 4)); + by6 = (byte)(((by2 & 0xf) << 2) | (by3 >> 6)); + by7 = (byte)(by3 & 0x3f); + + retval.Append(encode(by4)); + retval.Append(encode(by5)); + + if((i + 1) < plainSeq.Length) + { + retval.Append(encode(by6)); + } + else + { + retval.Append('='); + } + + if((i + 2) < plainSeq.Length) + { + retval.Append(encode(by7)); + } + else + { + retval.Append('='); + } + } + + System.Text.StringBuilder outString = new System.Text.StringBuilder(); + outString.Capacity = totalBytes; + int iter = 0; + + while((retval.Length - iter) > 76) + { + outString.Append(retval.ToString().Substring(iter, 76)); + outString.Append("\r\n"); + iter += 76; + } + + outString.Append(retval.ToString().Substring(iter)); + + return outString.ToString(); +} + +public static byte[] +decode(string str) +{ + System.Text.StringBuilder newStr = new System.Text.StringBuilder(); + + newStr.Capacity = str.Length; + + for(int j = 0; j < str.Length; j++) + { + char c = str[j]; + if(isBase64(c)) + { + newStr.Append(c); + } + } + + if(newStr.Length == 0) + { + return null; + } + + // Note: This is how we were previously computing the size of the return + // sequence. The method below is more efficient (and correct). + // size_t lines = str.size() / 78; + // size_t totalBytes = (lines * 76) + (((str.size() - (lines * 78)) * 3) / 4); + + // Figure out how long the final sequence is going to be. + int totalBytes = (newStr.Length * 3 / 4) + 1; + + IceInternal.ByteBuffer retval = IceInternal.ByteBuffer.allocate(totalBytes); + + byte by1; + byte by2; + byte by3; + byte by4; + + char c1, c2, c3, c4; + + int pos = 0; + for(int i = 0; i < newStr.Length; i += 4) + { + c1 = 'A'; + c2 = 'A'; + c3 = 'A'; + c4 = 'A'; + + c1 = newStr[i]; + + if((i + 1) < newStr.Length) + { + c2 = newStr[i + 1]; + } + + if((i + 2) < newStr.Length) + { + c3 = newStr[i + 2]; + } + + if((i + 3) < newStr.Length) + { + c4 = newStr[i + 3]; + } + + by1 = decode(c1); + by2 = decode(c2); + by3 = decode(c3); + by4 = decode(c4); + + retval.put((byte)((by1 << 2) | (by2 >> 4))); + ++pos; + + if(c3 != '=') + { + retval.put((byte)(((by2 & 0xf) << 4) | (by3 >> 2))); + ++pos; + } + + if(c4 != '=') + { + retval.put((byte)(((by3 & 0x3) << 6) | by4)); + ++pos; + } + } + + return retval.toArray(0, pos); +} + +public static bool +isBase64(char c) +{ + if(c >= 'A' && c <= 'Z') + { + return true; + } + + if(c >= 'a' && c <= 'z') + { + return true; + } + + if(c >= '0' && c <= '9') + { + return true; + } + + if(c == '+') + { + return true; + } + + if(c == '/') + { + return true; + } + + if(c == '=') + { + return true; + } + + return false; +} + +private static char +encode(byte uc) +{ + if(uc < 26) + { + return (char)('A' + uc); + } + + if(uc < 52) + { + return (char)('a' + (uc - 26)); + } + + if(uc < 62) + { + return (char)('0' + (uc - 52)); + } + + if(uc == 62) + { + return '+'; + } + + return '/'; +} + +private static byte +decode(char c) +{ + if(c >= 'A' && c <= 'Z') + { + return (byte)(c - 'A'); + } + + if(c >= 'a' && c <= 'z') + { + return (byte)(c - 'a' + 26); + } + + if(c >= '0' && c <= '9') + { + return (byte)(c - '0' + 52); + } + + if(c == '+') + { + return 62; + } + + return 63; +} + +} + +} + diff --git a/csharp/src/Ice/BasicStream.cs b/csharp/src/Ice/BasicStream.cs new file mode 100644 index 00000000000..3160a355bab --- /dev/null +++ b/csharp/src/Ice/BasicStream.cs @@ -0,0 +1,5409 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; +#if !COMPACT && !SILVERLIGHT + using System.Runtime.InteropServices; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters.Binary; +#endif + using System.Threading; + +#if !MANAGED && !COMPACT && !SILVERLIGHT + internal static class NativeMethods + { + [DllImport("bzip2.dll")] + internal static extern IntPtr BZ2_bzlibVersion(); + + [DllImport("bzip2.dll")] + internal static extern int BZ2_bzBuffToBuffCompress(byte[] dest, + ref int destLen, + byte[] source, + int sourceLen, + int blockSize100k, + int verbosity, + int workFactor); + + [DllImport("bzip2.dll")] + internal static extern int BZ2_bzBuffToBuffDecompress(byte[] dest, + ref int destLen, + byte[] source, + int sourceLen, + int small, + int verbosity); + } +#endif + + public class BasicStream + { + + static BasicStream() + { +#if MANAGED || COMPACT || SILVERLIGHT + // + // Protocol compression is not supported when using managed code. + // + _bzlibInstalled = false; +#else + // + // Simple trick to find out whether bzip2 is + // installed: Call the BZ2_bzlibVersion() function in the + // library. If we get an exception, the library is + // not available. + // + _bzlibInstalled = false; + + // + // We are setting the library name here because, under Mono, we don't know the exact library name. + // In addition, the FileName member of the BadImageFormatException is the empty string, even though + // it should provide the name of the library. + // + string lib = AssemblyUtil.runtime_ == AssemblyUtil.Runtime.Mono ? "bzip2 library" : "bzip2.dll"; + try + { + NativeMethods.BZ2_bzlibVersion(); + _bzlibInstalled = true; + } + catch(DllNotFoundException) + { + // Expected -- bzip2.dll not installed or not in PATH. + } + catch(EntryPointNotFoundException) + { + Console.Error.WriteLine("warning: found " + lib + " but entry point BZ2_bzlibVersion is missing."); + } + catch(BadImageFormatException ex) + { + if(ex.FileName != null && ex.FileName.Length != 0) + { + lib = ex.FileName; // Future-proof: we'll do the right thing if the FileName member is non-empty. + } + Console.Error.Write("warning: " + lib + " could not be loaded (likely due to 32/64-bit mismatch)."); + if(IntPtr.Size == 8) + { + Console.Error.Write(" Make sure the directory containing the 64-bit " + lib + " is in your PATH."); + } + Console.Error.WriteLine(); + } +#endif + } + + public BasicStream(Instance instance, Ice.EncodingVersion encoding) + { + initialize(instance, encoding); + _buf = new Buffer(); + } + + public BasicStream(Instance instance, Ice.EncodingVersion encoding, byte[] data) + { + initialize(instance, encoding); + _buf = new Buffer(data); + } + + private void initialize(Instance instance, Ice.EncodingVersion encoding) + { + instance_ = instance; + _closure = null; + _encoding = encoding; + + _readEncapsStack = null; + _writeEncapsStack = null; + _readEncapsCache = null; + _writeEncapsCache = null; + + _sliceObjects = true; + + _startSeq = -1; + } + + // + // This function allows this object to be reused, rather than + // reallocated. + // + public void reset() + { + _buf.reset(); + clear(); + } + + public void clear() + { + if(_readEncapsStack != null) + { + Debug.Assert(_readEncapsStack.next == null); + _readEncapsStack.next = _readEncapsCache; + _readEncapsCache = _readEncapsStack; + _readEncapsStack = null; + _readEncapsCache.reset(); + } + + if(_writeEncapsStack != null) + { + Debug.Assert(_writeEncapsStack.next == null); + _writeEncapsStack.next = _writeEncapsCache; + _writeEncapsCache = _writeEncapsStack; + _writeEncapsStack = null; + _writeEncapsCache.reset(); + } + + _startSeq = -1; + + _sliceObjects = true; + } + + public Instance instance() + { + return instance_; + } + + public object closure() + { + return _closure; + } + + public object closure(object p) + { + object prev = _closure; + _closure = p; + return prev; + } + + public void swap(BasicStream other) + { + Debug.Assert(instance_ == other.instance_); + + Buffer tmpBuf = other._buf; + other._buf = _buf; + _buf = tmpBuf; + + object tmpClosure = other._closure; + other._closure = _closure; + _closure = tmpClosure; + + // + // Swap is never called for BasicStreams that have encapsulations being read/write. However, + // encapsulations might still be set in case marshalling or un-marshalling failed. We just + // reset the encapsulations if there are still some set. + // + resetEncaps(); + other.resetEncaps(); + + int tmpStartSeq = other._startSeq; + other._startSeq = _startSeq; + _startSeq = tmpStartSeq; + + int tmpMinSeqSize = other._minSeqSize; + other._minSeqSize = _minSeqSize; + _minSeqSize = tmpMinSeqSize; + } + + public void resetEncaps() + { + _readEncapsStack = null; + _writeEncapsStack = null; + } + + public void resize(int sz, bool reading) + { + _buf.resize(sz, reading); + _buf.b.position(sz); + } + + public Buffer prepareWrite() + { + _buf.b.limit(_buf.size()); + _buf.b.position(0); + return _buf; + } + + public Buffer getBuffer() + { + return _buf; + } + + public void startWriteObject(Ice.SlicedData data) + { + Debug.Assert(_writeEncapsStack != null && _writeEncapsStack.encoder != null); + _writeEncapsStack.encoder.startInstance(SliceType.ObjectSlice, data); + } + + public void endWriteObject() + { + Debug.Assert(_writeEncapsStack != null && _writeEncapsStack.encoder != null); + _writeEncapsStack.encoder.endInstance(); + } + + public void startReadObject() + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + _readEncapsStack.decoder.startInstance(SliceType.ObjectSlice); + } + + public Ice.SlicedData endReadObject(bool preserve) + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + return _readEncapsStack.decoder.endInstance(preserve); + } + + public void startWriteException(Ice.SlicedData data) + { + Debug.Assert(_writeEncapsStack != null && _writeEncapsStack.encoder != null); + _writeEncapsStack.encoder.startInstance(SliceType.ExceptionSlice, data); + } + + public void endWriteException() + { + Debug.Assert(_writeEncapsStack != null && _writeEncapsStack.encoder != null); + _writeEncapsStack.encoder.endInstance(); + } + + public void startReadException() + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + _readEncapsStack.decoder.startInstance(SliceType.ExceptionSlice); + } + + public Ice.SlicedData endReadException(bool preserve) + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + return _readEncapsStack.decoder.endInstance(preserve); + } + + public void startWriteEncaps() + { + // + // If no encoding version is specified, use the current write + // encapsulation encoding version if there's a current write + // encapsulation, otherwise, use the stream encoding version. + // + + if(_writeEncapsStack != null) + { + startWriteEncaps(_writeEncapsStack.encoding, _writeEncapsStack.format); + } + else + { + startWriteEncaps(_encoding, Ice.FormatType.DefaultFormat); + } + } + + public void startWriteEncaps(Ice.EncodingVersion encoding, Ice.FormatType format) + { + Protocol.checkSupportedEncoding(encoding); + + WriteEncaps curr = _writeEncapsCache; + if(curr != null) + { + curr.reset(); + _writeEncapsCache = _writeEncapsCache.next; + } + else + { + curr = new WriteEncaps(); + } + curr.next = _writeEncapsStack; + _writeEncapsStack = curr; + + _writeEncapsStack.format = format; + _writeEncapsStack.setEncoding(encoding); + _writeEncapsStack.start = _buf.b.position(); + + writeInt(0); // Placeholder for the encapsulation length. + _writeEncapsStack.encoding.write__(this); + } + + public void endWriteEncaps() + { + Debug.Assert(_writeEncapsStack != null); + + // Size includes size and version. + int start = _writeEncapsStack.start; + int sz = _buf.size() - start; + _buf.b.putInt(start, sz); + + WriteEncaps curr = _writeEncapsStack; + _writeEncapsStack = curr.next; + curr.next = _writeEncapsCache; + _writeEncapsCache = curr; + _writeEncapsCache.reset(); + } + + public void endWriteEncapsChecked() + { + if(_writeEncapsStack == null) + { + throw new Ice.EncapsulationException("not in an encapsulation"); + } + endWriteEncaps(); + } + + public void writeEmptyEncaps(Ice.EncodingVersion encoding) + { + Protocol.checkSupportedEncoding(encoding); + writeInt(6); // Size + encoding.write__(this); + } + + public void writeEncaps(byte[] v) + { + if(v.Length < 6) + { + throw new Ice.EncapsulationException(); + } + expand(v.Length); + _buf.b.put(v); + } + + public Ice.EncodingVersion getWriteEncoding() + { + return _writeEncapsStack != null ? _writeEncapsStack.encoding : _encoding; + } + + public Ice.EncodingVersion startReadEncaps() + { + ReadEncaps curr = _readEncapsCache; + if(curr != null) + { + curr.reset(); + _readEncapsCache = _readEncapsCache.next; + } + else + { + curr = new ReadEncaps(); + } + curr.next = _readEncapsStack; + _readEncapsStack = curr; + + _readEncapsStack.start = _buf.b.position(); + + // + // I don't use readSize() and writeSize() for encapsulations, + // because when creating an encapsulation, I must know in advance + // how many bytes the size information will require in the data + // stream. If I use an Int, it is always 4 bytes. For + // readSize()/writeSize(), it could be 1 or 5 bytes. + // + int sz = readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + if(sz - 4 > _buf.b.remaining()) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + _readEncapsStack.sz = sz; + + Ice.EncodingVersion encoding = new Ice.EncodingVersion(); + encoding.read__(this); + Protocol.checkSupportedEncoding(encoding); // Make sure the encoding is supported. + _readEncapsStack.setEncoding(encoding); + + return encoding; + } + + public void endReadEncaps() + { + Debug.Assert(_readEncapsStack != null); + + if(!_readEncapsStack.encoding_1_0) + { + skipOpts(); + if(_buf.b.position() != _readEncapsStack.start + _readEncapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + } + else if(_buf.b.position() != _readEncapsStack.start + _readEncapsStack.sz) + { + if(_buf.b.position() + 1 != _readEncapsStack.start + _readEncapsStack.sz) + { + throw new Ice.EncapsulationException(); + } + + // + // Ice version < 3.3 had a bug where user exceptions with + // class members could be encoded with a trailing byte + // when dispatched with AMD. So we tolerate an extra byte + // in the encapsulation. + // + try + { + _buf.b.get(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + ReadEncaps curr = _readEncapsStack; + _readEncapsStack = curr.next; + curr.next = _readEncapsCache; + _readEncapsCache = curr; + _readEncapsCache.reset(); + } + + public Ice.EncodingVersion skipEmptyEncaps() + { + int sz = readInt(); + if(sz != 6) + { + throw new Ice.EncapsulationException(); + } + + Ice.EncodingVersion encoding = new Ice.EncodingVersion(); + encoding.read__(this); + return encoding; + } + + public void endReadEncapsChecked() // Used by public stream API. + { + if(_readEncapsStack == null) + { + throw new Ice.EncapsulationException("not in an encapsulation"); + } + endReadEncaps(); + } + + public byte[] readEncaps(out Ice.EncodingVersion encoding) + { + int sz = readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + if(sz - 4 > _buf.b.remaining()) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + encoding = new Ice.EncodingVersion(); + encoding.read__(this); + _buf.b.position(_buf.b.position() - 6); + + byte[] v = new byte[sz]; + try + { + _buf.b.get(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.EncodingVersion getReadEncoding() + { + return _readEncapsStack != null ? _readEncapsStack.encoding : _encoding; + } + + public int getReadEncapsSize() + { + Debug.Assert(_readEncapsStack != null); + return _readEncapsStack.sz - 6; + } + + public Ice.EncodingVersion skipEncaps() + { + int sz = readInt(); + if(sz < 6) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + Ice.EncodingVersion encoding = new Ice.EncodingVersion(); + encoding.read__(this); + try + { + _buf.b.position(_buf.b.position() + sz - 6); + } + catch(ArgumentOutOfRangeException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + return encoding; + } + + public void startWriteSlice(string typeId, int compactId, bool last) + { + Debug.Assert(_writeEncapsStack != null && _writeEncapsStack.encoder != null); + _writeEncapsStack.encoder.startSlice(typeId, compactId, last); + } + + public void endWriteSlice() + { + Debug.Assert(_writeEncapsStack != null && _writeEncapsStack.encoder != null); + _writeEncapsStack.encoder.endSlice(); + } + + public string startReadSlice() // Returns type ID of next slice + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + return _readEncapsStack.decoder.startSlice(); + } + + public void endReadSlice() + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + _readEncapsStack.decoder.endSlice(); + } + + public void skipSlice() + { + Debug.Assert(_readEncapsStack != null && _readEncapsStack.decoder != null); + _readEncapsStack.decoder.skipSlice(); + } + + public void readPendingObjects() + { + if(_readEncapsStack != null && _readEncapsStack.decoder != null) + { + _readEncapsStack.decoder.readPendingObjects(); + } + else if(_readEncapsStack != null ? _readEncapsStack.encoding_1_0 : _encoding.Equals(Ice.Util.Encoding_1_0)) + { + // + // If using the 1.0 encoding and no objects were read, we + // still read an empty sequence of pending objects if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no objects + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + skipSize(); + } + } + + public void writePendingObjects() + { + if(_writeEncapsStack != null && _writeEncapsStack.encoder != null) + { + _writeEncapsStack.encoder.writePendingObjects(); + } + else if(_writeEncapsStack != null ? + _writeEncapsStack.encoding_1_0 : _encoding.Equals(Ice.Util.Encoding_1_0)) + { + // + // If using the 1.0 encoding and no objects were written, we + // still write an empty sequence for pending objects if + // requested (i.e.: if this is called). + // + // This is required by the 1.0 encoding, even if no objects + // are written we do marshal an empty sequence if marshaled + // data types use classes. + // + writeSize(0); + } + } + + public void writeSize(int v) + { + if(v > 254) + { + expand(5); + _buf.b.put((byte)255); + _buf.b.putInt(v); + } + else + { + expand(1); + _buf.b.put((byte)v); + } + } + + public int readSize() + { + try + { + // + // COMPILERFIX: for some reasons _buf.get() doesn't work here on OS X with Mono; + // + //byte b = _buf.b.get(); + byte b = readByte(); + if(b == 255) + { + int v = _buf.b.getInt(); + if(v < 0) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return v; + } + else + { + return b; // byte is unsigned + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public int readAndCheckSeqSize(int minSize) + { + int sz = readSize(); + + if(sz == 0) + { + return 0; + } + + // + // The _startSeq variable points to the start of the sequence for which + // we expect to read at least _minSeqSize bytes from the stream. + // + // If not initialized or if we already read more data than _minSeqSize, + // we reset _startSeq and _minSeqSize for this sequence (possibly a + // top-level sequence or enclosed sequence it doesn't really matter). + // + // Otherwise, we are reading an enclosed sequence and we have to bump + // _minSeqSize by the minimum size that this sequence will require on + // the stream. + // + // The goal of this check is to ensure that when we start un-marshalling + // a new sequence, we check the minimal size of this new sequence against + // the estimated remaining buffer size. This estimatation is based on + // the minimum size of the enclosing sequences, it's _minSeqSize. + // + if(_startSeq == -1 || _buf.b.position() > (_startSeq + _minSeqSize)) + { + _startSeq = _buf.b.position(); + _minSeqSize = sz * minSize; + } + else + { + _minSeqSize += sz * minSize; + } + + // + // If there isn't enough data to read on the stream for the sequence (and + // possibly enclosed sequences), something is wrong with the marshalled + // data: it's claiming having more data that what is possible to read. + // + if(_startSeq + _minSeqSize > _buf.size()) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return sz; + } + + public int startSize() + { + int pos = _buf.b.position(); + writeInt(0); // Placeholder for 32-bit size + return pos; + } + + public void endSize(int pos) + { + Debug.Assert(pos >= 0); + rewriteInt(_buf.b.position() - pos - 4, pos); + } + + public void writeBlob(byte[] v) + { + if(v == null) + { + return; + } + expand(v.Length); + _buf.b.put(v); + } + + public void readBlob(byte[] v) + { + try + { + _buf.b.get(v); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public byte[] readBlob(int sz) + { + if(_buf.b.remaining() < sz) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + byte[] v = new byte[sz]; + try + { + _buf.b.get(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + // Read/write type and tag for optionals + public bool writeOpt(int tag, Ice.OptionalFormat format) + { + Debug.Assert(_writeEncapsStack != null); + if(_writeEncapsStack.encoder != null) + { + return _writeEncapsStack.encoder.writeOpt(tag, format); + } + else + { + return writeOptImpl(tag, format); + } + } + + public bool readOpt(int tag, Ice.OptionalFormat expectedFormat) + { + Debug.Assert(_readEncapsStack != null); + if(_readEncapsStack.decoder != null) + { + return _readEncapsStack.decoder.readOpt(tag, expectedFormat); + } + else + { + return readOptImpl(tag, expectedFormat); + } + } + + public void writeByte(byte v) + { + expand(1); + _buf.b.put(v); + } + + public void writeByte(int tag, Ice.Optional<byte> v) + { + if(v.HasValue) + { + writeByte(tag, v.Value); + } + } + + public void writeByte(int tag, byte v) + { + if(writeOpt(tag, Ice.OptionalFormat.F1)) + { + writeByte(v); + } + } + + public void rewriteByte(byte v, int dest) + { + _buf.b.put(dest, v); + } + + public void writeByteSeq(byte[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length); + _buf.b.put(v); + } + } + + public void writeByteSeq(int count, IEnumerable<byte> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<byte> value = v as List<byte>; + if(value != null) + { + writeByteSeq(value.ToArray()); + return; + } + } + + { + LinkedList<byte> value = v as LinkedList<byte>; + if(value != null) + { + writeSize(count); + expand(count); + IEnumerator<byte> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.put(i.Current); + } + return; + } + } + + { + Queue<byte> value = v as Queue<byte>; + if(value != null) + { + writeByteSeq(value.ToArray()); + return; + } + } + + { + Stack<byte> value = v as Stack<byte>; + if(value != null) + { + writeByteSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count); + foreach(byte b in v) + { + _buf.b.put(b); + } + } + + public void writeByteSeq(int tag, Ice.Optional<byte[]> v) + { + if(v.HasValue) + { + writeByteSeq(tag, v.Value); + } + } + + public void writeByteSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<byte> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeByteSeq(count, v.Value); + } + } + + public void writeByteSeq(int tag, byte[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeByteSeq(v); + } + } + + public void writeByteSeq(int tag, int count, IEnumerable<byte> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeByteSeq(count, v); + } + } + + public void writeSerializable(object o) + { +#if !COMPACT && !SILVERLIGHT + if(o == null) + { + writeSize(0); + return; + } + try + { + StreamWrapper w = new StreamWrapper(this); + IFormatter f = new BinaryFormatter(); + f.Serialize(w, o); + w.Close(); + } + catch(System.Exception ex) + { + throw new Ice.MarshalException("cannot serialize object:", ex); + } +#else + throw new Ice.MarshalException("serialization not supported"); +#endif + } + + public byte readByte() + { + try + { + return _buf.b.get(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<byte> readByte(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F1)) + { + return new Ice.Optional<byte>(readByte()); + } + else + { + return new Ice.Optional<byte>(); + } + } + + public void readByte(int tag, out bool isset, out byte v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F1)) + { + v = readByte(); + } + else + { + v = 0; + } + } + + public byte[] readByteSeq() + { + try + { + int sz = readAndCheckSeqSize(1); + byte[] v = new byte[sz]; + _buf.b.get(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readByteSeq(out List<byte> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<byte>(readByteSeq()); + } + + public void readByteSeq(out LinkedList<byte> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new LinkedList<byte>(readByteSeq()); + } + + public void readByteSeq(out Queue<byte> l) + { + // + // Reading into an array and copy-constructing the + // queue is faster than constructing the queue + // and adding to it one element at a time. + // + l = new Queue<byte>(readByteSeq()); + } + + public void readByteSeq(out Stack<byte> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + byte[] array = readByteSeq(); + Array.Reverse(array); + l = new Stack<byte>(array); + } + + public Ice.Optional<byte[]> readByteSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + return new Ice.Optional<byte[]>(readByteSeq()); + } + else + { + return new Ice.Optional<byte[]>(); + } + } + + public void readByteSeq(int tag, out bool isset, out byte[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + v = readByteSeq(); + } + else + { + v = null; + } + } + + public object readSerializable() + { +#if !COMPACT && !SILVERLIGHT + int sz = readAndCheckSeqSize(1); + if(sz == 0) + { + return null; + } + try + { + StreamWrapper w = new StreamWrapper(sz, this); + IFormatter f = new BinaryFormatter(); + return f.Deserialize(w); + } + catch(System.Exception ex) + { + throw new Ice.MarshalException("cannot deserialize object:", ex); + } +#else + throw new Ice.MarshalException("serialization not supported"); +#endif + } + + public void writeBool(bool v) + { + expand(1); + _buf.b.put(v ? (byte)1 : (byte)0); + } + + public void writeBool(int tag, Ice.Optional<bool> v) + { + if(v.HasValue) + { + writeBool(tag, v.Value); + } + } + + public void writeBool(int tag, bool v) + { + if(writeOpt(tag, Ice.OptionalFormat.F1)) + { + writeBool(v); + } + } + + public void rewriteBool(bool v, int dest) + { + _buf.b.put(dest, v ? (byte)1 : (byte)0); + } + + public void writeBoolSeq(bool[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length); + _buf.b.putBoolSeq(v); + } + } + + public void writeBoolSeq(int count, IEnumerable<bool> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<bool> value = v as List<bool>; + if(value != null) + { + writeBoolSeq(value.ToArray()); + return; + } + } + + { + LinkedList<bool> value = v as LinkedList<bool>; + if(value != null) + { + writeSize(count); + expand(count); + IEnumerator<bool> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.putBool(i.Current); + } + return; + } + } + + { + Queue<bool> value = v as Queue<bool>; + if(value != null) + { + writeBoolSeq(value.ToArray()); + return; + } + } + + { + Stack<bool> value = v as Stack<bool>; + if(value != null) + { + writeBoolSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count); + foreach(bool b in v) + { + _buf.b.putBool(b); + } + } + + public void writeBoolSeq(int tag, Ice.Optional<bool[]> v) + { + if(v.HasValue) + { + writeBoolSeq(tag, v.Value); + } + } + + public void writeBoolSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<bool> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeBoolSeq(count, v.Value); + } + } + + public void writeBoolSeq(int tag, bool[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeBoolSeq(v); + } + } + + public void writeBoolSeq(int tag, int count, IEnumerable<bool> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeBoolSeq(count, v); + } + } + + public bool readBool() + { + try + { + return _buf.b.get() == 1; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<bool> readBool(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F1)) + { + return new Ice.Optional<bool>(readBool()); + } + else + { + return new Ice.Optional<bool>(); + } + } + + public void readBool(int tag, out bool isset, out bool v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F1)) + { + v = readBool(); + } + else + { + v = false; + } + } + + public bool[] readBoolSeq() + { + try + { + int sz = readAndCheckSeqSize(1); + bool[] v = new bool[sz]; + _buf.b.getBoolSeq(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readBoolSeq(out List<bool> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<bool>(readBoolSeq()); + } + + public void readBoolSeq(out LinkedList<bool> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new LinkedList<bool>(readBoolSeq()); + } + + public void readBoolSeq(out Queue<bool> l) + { + // + // Reading into an array and copy-constructing the + // queue is faster than constructing the queue + // and adding to it one element at a time. + // + l = new Queue<bool>(readBoolSeq()); + } + + public void readBoolSeq(out Stack<bool> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + bool[] array = readBoolSeq(); + Array.Reverse(array); + l = new Stack<bool>(array); + } + + public Ice.Optional<bool[]> readBoolSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + return new Ice.Optional<bool[]>(readBoolSeq()); + } + else + { + return new Ice.Optional<bool[]>(); + } + } + + public void readBoolSeq(int tag, out bool isset, out bool[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + v = readBoolSeq(); + } + else + { + v = null; + } + } + + public void writeShort(short v) + { + expand(2); + _buf.b.putShort(v); + } + + public void writeShort(int tag, Ice.Optional<short> v) + { + if(v.HasValue) + { + writeShort(tag, v.Value); + } + } + + public void writeShort(int tag, short v) + { + if(writeOpt(tag, Ice.OptionalFormat.F2)) + { + writeShort(v); + } + } + + public void writeShortSeq(short[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length * 2); + _buf.b.putShortSeq(v); + } + } + + public void writeShortSeq(int count, IEnumerable<short> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<short> value = v as List<short>; + if(value != null) + { + writeShortSeq(value.ToArray()); + return; + } + } + + { + LinkedList<short> value = v as LinkedList<short>; + if(value != null) + { + writeSize(count); + expand(count * 2); + IEnumerator<short> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.putShort(i.Current); + } + return; + } + } + + { + Queue<short> value = v as Queue<short>; + if(value != null) + { + writeShortSeq(value.ToArray()); + return; + } + } + + { + Stack<short> value = v as Stack<short>; + if(value != null) + { + writeShortSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count * 2); + foreach(short s in v) + { + _buf.b.putShort(s); + } + } + + public void writeShortSeq(int tag, Ice.Optional<short[]> v) + { + if(v.HasValue) + { + writeShortSeq(tag, v.Value); + } + } + + public void writeShortSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<short> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(count == 0 ? 1 : count * 2 + (count > 254 ? 5 : 1)); + writeShortSeq(count, v.Value); + } + } + + public void writeShortSeq(int tag, short[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || v.Length == 0 ? 1 : v.Length * 2 + (v.Length > 254 ? 5 : 1)); + writeShortSeq(v); + } + } + + public void writeShortSeq(int tag, int count, IEnumerable<short> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || count == 0 ? 1 : count * 2 + (count > 254 ? 5 : 1)); + writeShortSeq(count, v); + } + } + + public short readShort() + { + try + { + return _buf.b.getShort(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<short> readShort(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F2)) + { + return new Ice.Optional<short>(readShort()); + } + else + { + return new Ice.Optional<short>(); + } + } + + public void readShort(int tag, out bool isset, out short v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F2)) + { + v = readShort(); + } + else + { + v = 0; + } + } + + public short[] readShortSeq() + { + try + { + int sz = readAndCheckSeqSize(2); + short[] v = new short[sz]; + _buf.b.getShortSeq(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readShortSeq(out List<short> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<short>(readShortSeq()); + } + + public void readShortSeq(out LinkedList<short> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new LinkedList<short>(readShortSeq()); + } + + public void readShortSeq(out Queue<short> l) + { + // + // Reading into an array and copy-constructing the + // queue is faster than constructing the queue + // and adding to it one element at a time. + // + l = new Queue<short>(readShortSeq()); + } + + public void readShortSeq(out Stack<short> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + short[] array = readShortSeq(); + Array.Reverse(array); + l = new Stack<short>(array); + } + + public Ice.Optional<short[]> readShortSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + return new Ice.Optional<short[]>(readShortSeq()); + } + else + { + return new Ice.Optional<short[]>(); + } + } + + public void readShortSeq(int tag, out bool isset, out short[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + v = readShortSeq(); + } + else + { + v = null; + } + } + + public void writeInt(int v) + { + expand(4); + _buf.b.putInt(v); + } + + public void writeInt(int tag, Ice.Optional<int> v) + { + if(v.HasValue) + { + writeInt(tag, v.Value); + } + } + + public void writeInt(int tag, int v) + { + if(writeOpt(tag, Ice.OptionalFormat.F4)) + { + writeInt(v); + } + } + + public void rewriteInt(int v, int dest) + { + _buf.b.putInt(dest, v); + } + + public void writeIntSeq(int[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length * 4); + _buf.b.putIntSeq(v); + } + } + + public void writeIntSeq(int count, IEnumerable<int> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<int> value = v as List<int>; + if(value != null) + { + writeIntSeq(value.ToArray()); + return; + } + } + + { + LinkedList<int> value = v as LinkedList<int>; + if(value != null) + { + writeSize(count); + expand(count * 4); + IEnumerator<int> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.putInt(i.Current); + } + return; + } + } + + { + Queue<int> value = v as Queue<int>; + if(value != null) + { + writeIntSeq(value.ToArray()); + return; + } + } + + { + Stack<int> value = v as Stack<int>; + if(value != null) + { + writeIntSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count * 4); + foreach(int i in v) + { + _buf.b.putInt(i); + } + } + + public void writeIntSeq(int tag, Ice.Optional<int[]> v) + { + if(v.HasValue) + { + writeIntSeq(tag, v.Value); + } + } + + public void writeIntSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<int> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(count == 0 ? 1 : count * 4 + (count > 254 ? 5 : 1)); + writeIntSeq(count, v.Value); + } + } + + public void writeIntSeq(int tag, int[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || v.Length == 0 ? 1 : v.Length * 4 + (v.Length > 254 ? 5 : 1)); + writeIntSeq(v); + } + } + + public void writeIntSeq(int tag, int count, IEnumerable<int> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || count == 0 ? 1 : count * 4 + (count > 254 ? 5 : 1)); + writeIntSeq(count, v); + } + } + + public int readInt() + { + try + { + return _buf.b.getInt(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<int> readInt(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F4)) + { + return new Ice.Optional<int>(readInt()); + } + else + { + return new Ice.Optional<int>(); + } + } + + public void readInt(int tag, out bool isset, out int v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F4)) + { + v = readInt(); + } + else + { + v = 0; + } + } + + public int[] readIntSeq() + { + try + { + int sz = readAndCheckSeqSize(4); + int[] v = new int[sz]; + _buf.b.getIntSeq(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readIntSeq(out List<int> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<int>(readIntSeq()); + } + + public void readIntSeq(out LinkedList<int> l) + { + try + { + int sz = readAndCheckSeqSize(4); + l = new LinkedList<int>(); + for(int i = 0; i < sz; ++i) + { + l.AddLast(_buf.b.getInt()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readIntSeq(out Queue<int> l) + { + // + // Reading into an array and copy-constructing the + // queue takes the same time as constructing the queue + // and adding to it one element at a time, so + // we avoid the copy. + // + try + { + int sz = readAndCheckSeqSize(4); + l = new Queue<int>(sz); + for(int i = 0; i < sz; ++i) + { + l.Enqueue(_buf.b.getInt()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readIntSeq(out Stack<int> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + int[] array = readIntSeq(); + Array.Reverse(array); + l = new Stack<int>(array); + } + + public Ice.Optional<int[]> readIntSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + return new Ice.Optional<int[]>(readIntSeq()); + } + else + { + return new Ice.Optional<int[]>(); + } + } + + public void readIntSeq(int tag, out bool isset, out int[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + v = readIntSeq(); + } + else + { + v = null; + } + } + + public void writeLong(long v) + { + expand(8); + _buf.b.putLong(v); + } + + public void writeLong(int tag, Ice.Optional<long> v) + { + if(v.HasValue) + { + writeLong(tag, v.Value); + } + } + + public void writeLong(int tag, long v) + { + if(writeOpt(tag, Ice.OptionalFormat.F8)) + { + writeLong(v); + } + } + + public void writeLongSeq(long[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length * 8); + _buf.b.putLongSeq(v); + } + } + + public void writeLongSeq(int count, IEnumerable<long> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<long> value = v as List<long>; + if(value != null) + { + writeLongSeq(value.ToArray()); + return; + } + } + + { + LinkedList<long> value = v as LinkedList<long>; + if(value != null) + { + writeSize(count); + expand(count * 8); + IEnumerator<long> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.putLong(i.Current); + } + return; + } + } + + { + Queue<long> value = v as Queue<long>; + if(value != null) + { + writeLongSeq(value.ToArray()); + return; + } + } + + { + Stack<long> value = v as Stack<long>; + if(value != null) + { + writeLongSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count * 8); + foreach(long l in v) + { + _buf.b.putLong(l); + } + } + + public void writeLongSeq(int tag, Ice.Optional<long[]> v) + { + if(v.HasValue) + { + writeLongSeq(tag, v.Value); + } + } + + public void writeLongSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<long> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(count == 0 ? 1 : count * 8 + (count > 254 ? 5 : 1)); + writeLongSeq(count, v.Value); + } + } + + public void writeLongSeq(int tag, long[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || v.Length == 0 ? 1 : v.Length * 8 + (v.Length > 254 ? 5 : 1)); + writeLongSeq(v); + } + } + + public void writeLongSeq(int tag, int count, IEnumerable<long> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || count == 0 ? 1 : count * 8 + (count > 254 ? 5 : 1)); + writeLongSeq(count, v); + } + } + + public long readLong() + { + try + { + return _buf.b.getLong(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<long> readLong(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F8)) + { + return new Ice.Optional<long>(readLong()); + } + else + { + return new Ice.Optional<long>(); + } + } + + public void readLong(int tag, out bool isset, out long v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F8)) + { + v = readLong(); + } + else + { + v = 0; + } + } + + public long[] readLongSeq() + { + try + { + int sz = readAndCheckSeqSize(8); + long[] v = new long[sz]; + _buf.b.getLongSeq(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readLongSeq(out List<long> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<long>(readLongSeq()); + } + + public void readLongSeq(out LinkedList<long> l) + { + try + { + int sz = readAndCheckSeqSize(4); + l = new LinkedList<long>(); + for(int i = 0; i < sz; ++i) + { + l.AddLast(_buf.b.getLong()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readLongSeq(out Queue<long> l) + { + // + // Reading into an array and copy-constructing the + // queue takes the same time as constructing the queue + // and adding to it one element at a time, so + // we avoid the copy. + // + try + { + int sz = readAndCheckSeqSize(4); + l = new Queue<long>(sz); + for(int i = 0; i < sz; ++i) + { + l.Enqueue(_buf.b.getLong()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readLongSeq(out Stack<long> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + long[] array = readLongSeq(); + Array.Reverse(array); + l = new Stack<long>(array); + } + + public Ice.Optional<long[]> readLongSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + return new Ice.Optional<long[]>(readLongSeq()); + } + else + { + return new Ice.Optional<long[]>(); + } + } + + public void readLongSeq(int tag, out bool isset, out long[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + v = readLongSeq(); + } + else + { + v = null; + } + } + + public void writeFloat(float v) + { + expand(4); + _buf.b.putFloat(v); + } + + public void writeFloat(int tag, Ice.Optional<float> v) + { + if(v.HasValue) + { + writeFloat(tag, v.Value); + } + } + + public void writeFloat(int tag, float v) + { + if(writeOpt(tag, Ice.OptionalFormat.F4)) + { + writeFloat(v); + } + } + + public void writeFloatSeq(float[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length * 4); + _buf.b.putFloatSeq(v); + } + } + + public void writeFloatSeq(int count, IEnumerable<float> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<float> value = v as List<float>; + if(value != null) + { + writeFloatSeq(value.ToArray()); + return; + } + } + + { + LinkedList<float> value = v as LinkedList<float>; + if(value != null) + { + writeSize(count); + expand(count * 4); + IEnumerator<float> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.putFloat(i.Current); + } + return; + } + } + + { + Queue<float> value = v as Queue<float>; + if(value != null) + { + writeFloatSeq(value.ToArray()); + return; + } + } + + { + Stack<float> value = v as Stack<float>; + if(value != null) + { + writeFloatSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count * 4); + foreach(float f in v) + { + _buf.b.putFloat(f); + } + } + + public void writeFloatSeq(int tag, Ice.Optional<float[]> v) + { + if(v.HasValue) + { + writeFloatSeq(tag, v.Value); + } + } + + public void writeFloatSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<float> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(count == 0 ? 1 : count * 4 + (count > 254 ? 5 : 1)); + writeFloatSeq(count, v.Value); + } + } + + public void writeFloatSeq(int tag, float[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || v.Length == 0 ? 1 : v.Length * 4 + (v.Length > 254 ? 5 : 1)); + writeFloatSeq(v); + } + } + + public void writeFloatSeq(int tag, int count, IEnumerable<float> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || count == 0 ? 1 : count * 4 + (count > 254 ? 5 : 1)); + writeFloatSeq(count, v); + } + } + + public float readFloat() + { + try + { + return _buf.b.getFloat(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<float> readFloat(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F4)) + { + return new Ice.Optional<float>(readFloat()); + } + else + { + return new Ice.Optional<float>(); + } + } + + public void readFloat(int tag, out bool isset, out float v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F4)) + { + v = readFloat(); + } + else + { + v = 0; + } + } + + public float[] readFloatSeq() + { + try + { + int sz = readAndCheckSeqSize(4); + float[] v = new float[sz]; + _buf.b.getFloatSeq(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readFloatSeq(out List<float> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<float>(readFloatSeq()); + } + + public void readFloatSeq(out LinkedList<float> l) + { + try + { + int sz = readAndCheckSeqSize(4); + l = new LinkedList<float>(); + for(int i = 0; i < sz; ++i) + { + l.AddLast(_buf.b.getFloat()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readFloatSeq(out Queue<float> l) + { + // + // Reading into an array and copy-constructing the + // queue takes the same time as constructing the queue + // and adding to it one element at a time, so + // we avoid the copy. + // + try + { + int sz = readAndCheckSeqSize(4); + l = new Queue<float>(sz); + for(int i = 0; i < sz; ++i) + { + l.Enqueue(_buf.b.getFloat()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readFloatSeq(out Stack<float> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + float[] array = readFloatSeq(); + Array.Reverse(array); + l = new Stack<float>(array); + } + + public Ice.Optional<float[]> readFloatSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + return new Ice.Optional<float[]>(readFloatSeq()); + } + else + { + return new Ice.Optional<float[]>(); + } + } + + public void readFloatSeq(int tag, out bool isset, out float[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + v = readFloatSeq(); + } + else + { + v = null; + } + } + + public void writeDouble(double v) + { + expand(8); + _buf.b.putDouble(v); + } + + public void writeDouble(int tag, Ice.Optional<double> v) + { + if(v.HasValue) + { + writeDouble(tag, v.Value); + } + } + + public void writeDouble(int tag, double v) + { + if(writeOpt(tag, Ice.OptionalFormat.F8)) + { + writeDouble(v); + } + } + + public void writeDoubleSeq(double[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + expand(v.Length * 8); + _buf.b.putDoubleSeq(v); + } + } + + public void writeDoubleSeq(int count, IEnumerable<double> v) + { + if(count == 0) + { + writeSize(0); + return; + } + + { + List<double> value = v as List<double>; + if(value != null) + { + writeDoubleSeq(value.ToArray()); + return; + } + } + + { + LinkedList<double> value = v as LinkedList<double>; + if(value != null) + { + writeSize(count); + expand(count * 8); + IEnumerator<double> i = v.GetEnumerator(); + while(i.MoveNext()) + { + _buf.b.putDouble(i.Current); + } + return; + } + } + + { + Queue<double> value = v as Queue<double>; + if(value != null) + { + writeDoubleSeq(value.ToArray()); + return; + } + } + + { + Stack<double> value = v as Stack<double>; + if (value != null) + { + writeDoubleSeq(value.ToArray()); + return; + } + } + + writeSize(count); + expand(count * 8); + foreach(double d in v) + { + _buf.b.putDouble(d); + } + } + + public void writeDoubleSeq(int tag, Ice.Optional<double[]> v) + { + if(v.HasValue) + { + writeDoubleSeq(tag, v.Value); + } + } + + public void writeDoubleSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<double> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(count == 0 ? 1 : count * 8 + (count > 254 ? 5 : 1)); + writeDoubleSeq(count, v.Value); + } + } + + public void writeDoubleSeq(int tag, double[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || v.Length == 0 ? 1 : v.Length * 8 + (v.Length > 254 ? 5 : 1)); + writeDoubleSeq(v); + } + } + + public void writeDoubleSeq(int tag, int count, IEnumerable<double> v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeSize(v == null || count == 0 ? 1 : count * 8 + (count > 254 ? 5 : 1)); + writeDoubleSeq(count, v); + } + } + + public double readDouble() + { + try + { + return _buf.b.getDouble(); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public Ice.Optional<double> readDouble(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.F8)) + { + return new Ice.Optional<double>(readDouble()); + } + else + { + return new Ice.Optional<double>(); + } + } + + public void readDouble(int tag, out bool isset, out double v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.F8)) + { + v = readDouble(); + } + else + { + v = 0; + } + } + + public double[] readDoubleSeq() + { + try + { + int sz = readAndCheckSeqSize(8); + double[] v = new double[sz]; + _buf.b.getDoubleSeq(v); + return v; + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readDoubleSeq(out List<double> l) + { + // + // Reading into an array and copy-constructing the + // list is faster than constructing the list + // and adding to it one element at a time. + // + l = new List<double>(readDoubleSeq()); + } + + public void readDoubleSeq(out LinkedList<double> l) + { + try + { + int sz = readAndCheckSeqSize(4); + l = new LinkedList<double>(); + for(int i = 0; i < sz; ++i) + { + l.AddLast(_buf.b.getDouble()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readDoubleSeq(out Queue<double> l) + { + // + // Reading into an array and copy-constructing the + // queue takes the same time as constructing the queue + // and adding to it one element at a time, so + // we avoid the copy. + // + try + { + int sz = readAndCheckSeqSize(4); + l = new Queue<double>(sz); + for(int i = 0; i < sz; ++i) + { + l.Enqueue(_buf.b.getDouble()); + } + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + } + + public void readDoubleSeq(out Stack<double> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + double[] array = readDoubleSeq(); + Array.Reverse(array); + l = new Stack<double>(array); + } + + public Ice.Optional<double[]> readDoubleSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + return new Ice.Optional<double[]>(readDoubleSeq()); + } + else + { + return new Ice.Optional<double[]>(); + } + } + + public void readDoubleSeq(int tag, out bool isset, out double[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + skipSize(); + v = readDoubleSeq(); + } + else + { + v = null; + } + } + + private static System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding(false, true); + + public void writeString(string v) + { + if(v == null || v.Length == 0) + { + writeSize(0); + return; + } + byte[] arr = utf8.GetBytes(v); + writeSize(arr.Length); + expand(arr.Length); + _buf.b.put(arr); + } + + public void writeString(int tag, Ice.Optional<string> v) + { + if(v.HasValue) + { + writeString(tag, v.Value); + } + } + + public void writeString(int tag, string v) + { + if(writeOpt(tag, Ice.OptionalFormat.VSize)) + { + writeString(v); + } + } + + public void writeStringSeq(string[] v) + { + if(v == null) + { + writeSize(0); + } + else + { + writeSize(v.Length); + for(int i = 0; i < v.Length; i++) + { + writeString(v[i]); + } + } + } + + public void writeStringSeq(int size, IEnumerable<string> e) + { + writeSize(size); + if(size != 0) + { + foreach(string s in e) + { + writeString(s); + } + } + } + + public void writeStringSeq(int tag, Ice.Optional<String[]> v) + { + if(v.HasValue) + { + writeStringSeq(tag, v.Value); + } + } + + public void writeStringSeq<T>(int tag, int count, Ice.Optional<T> v) + where T : IEnumerable<string> + { + if(v.HasValue && writeOpt(tag, Ice.OptionalFormat.FSize)) + { + int pos = startSize(); + writeStringSeq(count, v.Value); + endSize(pos); + } + } + + public void writeStringSeq(int tag, string[] v) + { + if(writeOpt(tag, Ice.OptionalFormat.FSize)) + { + int pos = startSize(); + writeStringSeq(v); + endSize(pos); + } + } + + public void writeStringSeq(int tag, int count, IEnumerable<string> v) + { + if(writeOpt(tag, Ice.OptionalFormat.FSize)) + { + int pos = startSize(); + writeStringSeq(count, v); + endSize(pos); + } + } + + public string readString() + { + int len = readSize(); + + if(len == 0) + { + return ""; + } + + // + // Check the buffer has enough bytes to read. + // + if(_buf.b.remaining() < len) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + try + { + // + // We reuse the _stringBytes array to avoid creating + // excessive garbage + // + if(_stringBytes == null || len > _stringBytes.Length) + { + _stringBytes = new byte[len]; + } + _buf.b.get(_stringBytes, 0, len); + return utf8.GetString(_stringBytes, 0, len); + } + catch(InvalidOperationException ex) + { + throw new Ice.UnmarshalOutOfBoundsException(ex); + } + catch(System.ArgumentException ex) + { + throw new Ice.MarshalException("Invalid UTF8 string", ex); + } + } + + public Ice.Optional<string> readString(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.VSize)) + { + return new Ice.Optional<string>(readString()); + } + else + { + return new Ice.Optional<string>(); + } + } + + public void readString(int tag, out bool isset, out string v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.VSize)) + { + v = readString(); + } + else + { + v = null; + } + } + + public string[] readStringSeq() + { + int sz = readAndCheckSeqSize(1); + string[] v = new string[sz]; + for(int i = 0; i < sz; i++) + { + v[i] = readString(); + } + return v; + } + + public void readStringSeq(out List<string> l) + { + // + // Reading into an array and copy-constructing the + // list is slower than constructing the list + // and adding to it one element at a time. + // + int sz = readAndCheckSeqSize(1); + l = new List<string>(sz); + for(int i = 0; i < sz; ++i) + { + l.Add(readString()); + } + } + + public void readStringSeq(out LinkedList<string> l) + { + // + // Reading into an array and copy-constructing the + // list is slower than constructing the list + // and adding to it one element at a time. + // + int sz = readAndCheckSeqSize(1); + l = new LinkedList<string>(); + for(int i = 0; i < sz; ++i) + { + l.AddLast(readString()); + } + } + + public void readStringSeq(out Queue<string> l) + { + // + // Reading into an array and copy-constructing the + // queue is slower than constructing the queue + // and adding to it one element at a time. + // + int sz = readAndCheckSeqSize(1); + l = new Queue<string>(); + for(int i = 0; i < sz; ++i) + { + l.Enqueue(readString()); + } + } + + public void readStringSeq(out Stack<string> l) + { + // + // Reverse the contents by copying into an array first + // because the stack is marshaled in top-to-bottom order. + // + string[] array = readStringSeq(); + Array.Reverse(array); + l = new Stack<string>(array); + } + + public Ice.Optional<string[]> readStringSeq(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.FSize)) + { + skip(4); + return new Ice.Optional<string[]>(readStringSeq()); + } + else + { + return new Ice.Optional<string[]>(); + } + } + + public void readStringSeq(int tag, out bool isset, out string[] v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.FSize)) + { + skip(4); + v = readStringSeq(); + } + else + { + v = null; + } + } + + public void writeProxy(Ice.ObjectPrx v) + { + instance_.proxyFactory().proxyToStream(v, this); + } + + public void writeProxy(int tag, Ice.Optional<Ice.ObjectPrx> v) + { + if(v.HasValue) + { + writeProxy(tag, v.Value); + } + } + + public void writeProxy(int tag, Ice.ObjectPrx v) + { + if(writeOpt(tag, Ice.OptionalFormat.FSize)) + { + int pos = startSize(); + writeProxy(v); + endSize(pos); + } + } + + public Ice.ObjectPrx readProxy() + { + return instance_.proxyFactory().streamToProxy(this); + } + + public Ice.Optional<Ice.ObjectPrx> readProxy(int tag) + { + if(readOpt(tag, Ice.OptionalFormat.FSize)) + { + skip(4); + return new Ice.Optional<Ice.ObjectPrx>(readProxy()); + } + else + { + return new Ice.Optional<Ice.ObjectPrx>(); + } + } + + public void readProxy(int tag, out bool isset, out Ice.ObjectPrx v) + { + if(isset = readOpt(tag, Ice.OptionalFormat.FSize)) + { + skip(4); + v = readProxy(); + } + else + { + v = null; + } + } + + public void writeEnum(int v, int maxValue) + { + if(isWriteEncoding_1_0()) + { + if(maxValue < 127) + { + writeByte((byte)v); + } + else if(maxValue < 32767) + { + writeShort((short)v); + } + else + { + writeInt(v); + } + } + else + { + writeSize(v); + } + } + + public void writeEnum(int tag, int v, int maxValue) + { + if(writeOpt(tag, Ice.OptionalFormat.Size)) + { + writeEnum(v, maxValue); + } + } + + public int readEnum(int maxValue) + { + if(getReadEncoding().Equals(Ice.Util.Encoding_1_0)) + { + if(maxValue < 127) + { + return readByte(); + } + else if(maxValue < 32767) + { + return readShort(); + } + else + { + return readInt(); + } + } + else + { + return readSize(); + } + } + + public void writeObject(Ice.Object v) + { + initWriteEncaps(); + _writeEncapsStack.encoder.writeObject(v); + } + + public void writeObject<T>(int tag, Ice.Optional<T> v) + where T : Ice.Object + { + if(v.HasValue) + { + writeObject(tag, v.Value); + } + } + + public void writeObject(int tag, Ice.Object v) + { + if(writeOpt(tag, Ice.OptionalFormat.Class)) + { + writeObject(v); + } + } + + public void readObject(IPatcher patcher) + { + initReadEncaps(); + _readEncapsStack.decoder.readObject(patcher); + } + + public void readObject(int tag, IPatcher patcher) + { + if(readOpt(tag, Ice.OptionalFormat.Class)) + { + readObject(patcher); + } + } + + public void writeUserException(Ice.UserException v) + { + initWriteEncaps(); + _writeEncapsStack.encoder.writeUserException(v); + } + + public void throwException(UserExceptionFactory factory) + { + initReadEncaps(); + _readEncapsStack.decoder.throwException(factory); + } + + public void sliceObjects(bool b) + { + _sliceObjects = b; + } + + public bool readOptImpl(int readTag, Ice.OptionalFormat expectedFormat) + { + if(isReadEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + while(true) + { + if(_buf.b.position() >= _readEncapsStack.start + _readEncapsStack.sz) + { + return false; // End of encapsulation also indicates end of optionals. + } + + int v = readByte(); + if(v == OPTIONAL_END_MARKER) + { + _buf.b.position(_buf.b.position() - 1); // Rewind. + return false; + } + + Ice.OptionalFormat format = (Ice.OptionalFormat)(v & 0x07); // First 3 bits. + int tag = v >> 3; + if(tag == 30) + { + tag = readSize(); + } + + if(tag > readTag) + { + int offset = tag < 30 ? 1 : (tag < 255 ? 2 : 6); // Rewind + _buf.b.position(_buf.b.position() - offset); + return false; // No optional data members with the requested tag. + } + else if(tag < readTag) + { + skipOpt(format); // Skip optional data members + } + else + { + if(format != expectedFormat) + { + throw new Ice.MarshalException("invalid optional data member `" + tag + "': unexpected format"); + } + return true; + } + } + } + + public bool writeOptImpl(int tag, Ice.OptionalFormat format) + { + if(isWriteEncoding_1_0()) + { + return false; // Optional members aren't supported with the 1.0 encoding. + } + + int v = (int)format; + if(tag < 30) + { + v |= tag << 3; + writeByte((byte)v); + } + else + { + v |= 0x0F0; // tag = 30 + writeByte((byte)v); + writeSize(tag); + } + return true; + } + + public void skipOpt(Ice.OptionalFormat format) + { + switch(format) + { + case Ice.OptionalFormat.F1: + { + skip(1); + break; + } + case Ice.OptionalFormat.F2: + { + skip(2); + break; + } + case Ice.OptionalFormat.F4: + { + skip(4); + break; + } + case Ice.OptionalFormat.F8: + { + skip(8); + break; + } + case Ice.OptionalFormat.Size: + { + skipSize(); + break; + } + case Ice.OptionalFormat.VSize: + { + skip(readSize()); + break; + } + case Ice.OptionalFormat.FSize: + { + skip(readInt()); + break; + } + case Ice.OptionalFormat.Class: + { + readObject(null); + break; + } + } + } + + public bool skipOpts() + { + // + // Skip remaining un-read optional members. + // + while(true) + { + if(_buf.b.position() >= _readEncapsStack.start + _readEncapsStack.sz) + { + return false; // End of encapsulation also indicates end of optionals. + } + + int v = readByte(); + if(v == OPTIONAL_END_MARKER) + { + return true; + } + + Ice.OptionalFormat format = (Ice.OptionalFormat)(v & 0x07); // Read first 3 bits. + if((v >> 3) == 30) + { + skipSize(); + } + skipOpt(format); + } + } + + public void skip(int size) + { + if(size > _buf.b.remaining()) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + _buf.b.position(_buf.b.position() + size); + } + + public void skipSize() + { + byte b = readByte(); + if(b == 255) + { + skip(4); + } + } + + public int pos() + { + return _buf.b.position(); + } + + public void pos(int n) + { + _buf.b.position(n); + } + + public int size() + { + return _buf.size(); + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + static string getBZ2Error(int error) + { + string rc; + + switch(error) + { + case BZ_SEQUENCE_ERROR: + { + rc = "BZ_SEQUENCE_ERROR"; + break; + } + case BZ_PARAM_ERROR: + { + rc = "BZ_PARAM_ERROR"; + break; + } + case BZ_MEM_ERROR: + { + rc = "BZ_MEM_ERROR"; + break; + } + case BZ_DATA_ERROR: + { + rc = "BZ_DATA_ERROR"; + break; + } + case BZ_DATA_ERROR_MAGIC: + { + rc = "BZ_DATA_ERROR_MAGIC"; + break; + } + case BZ_IO_ERROR: + { + rc = "BZ_IO_ERROR"; + break; + } + case BZ_UNEXPECTED_EOF: + { + rc = "BZ_UNEXPECTED_EOF"; + break; + } + case BZ_OUTBUFF_FULL: + { + rc = "BZ_OUTBUFF_FULL"; + break; + } + case BZ_CONFIG_ERROR: + { + rc = "BZ_CONFIG_ERROR"; + break; + } + default: + { + rc = "Unknown bzip2 error: " + error; + break; + } + } + return rc; + } +#endif + + public static bool compressible() + { + return _bzlibInstalled; + } + + public bool compress(ref BasicStream cstream, int headerSize, int compressionLevel) + { +#if MANAGED || COMPACT || SILVERLIGHT + cstream = this; + return false; +#else + if(!_bzlibInstalled) + { + cstream = this; + return false; + } + + // + // Compress the message body, but not the header. + // + int uncompressedLen = size() - headerSize; + byte[] uncompressed = _buf.b.rawBytes(headerSize, uncompressedLen); + int compressedLen = (int)(uncompressedLen * 1.01 + 600); + byte[] compressed = new byte[compressedLen]; + + int rc = NativeMethods.BZ2_bzBuffToBuffCompress(compressed, ref compressedLen, uncompressed, + uncompressedLen, compressionLevel, 0, 0); + if(rc == BZ_OUTBUFF_FULL) + { + cstream = null; + return false; + } + else if(rc < 0) + { + Ice.CompressionException ex = new Ice.CompressionException("BZ2_bzBuffToBuffCompress failed"); + ex.reason = getBZ2Error(rc); + throw ex; + } + + // + // Don't bother if the compressed data is larger than the + // uncompressed data. + // + if(compressedLen >= uncompressedLen) + { + return false; + } + + cstream = new BasicStream(instance_, _encoding); + cstream.resize(headerSize + 4 + compressedLen, false); + cstream.pos(0); + + // + // Copy the header from the uncompressed stream to the + // compressed one. + // + cstream._buf.b.put(_buf.b.rawBytes(0, headerSize)); + + // + // Add the size of the uncompressed stream before the + // message body. + // + cstream.writeInt(size()); + + // + // Add the compressed message body. + // + cstream._buf.b.put(compressed, 0, compressedLen); + + return true; +#endif + } + + public BasicStream uncompress(int headerSize, int messageSizeMax) + { +#if MANAGED || COMPACT || SILVERLIGHT + return this; +#else + if(!_bzlibInstalled) + { + return this; + } + + pos(headerSize); + int uncompressedSize = readInt(); + if(uncompressedSize <= headerSize) + { + throw new Ice.IllegalMessageSizeException("compressed size <= header size"); + } + if(uncompressedSize > messageSizeMax) + { + IceInternal.Ex.throwMemoryLimitException(uncompressedSize, messageSizeMax); + } + + int compressedLen = size() - headerSize - 4; + byte[] compressed = _buf.b.rawBytes(headerSize + 4, compressedLen); + int uncompressedLen = uncompressedSize - headerSize; + byte[] uncompressed = new byte[uncompressedLen]; + int rc = NativeMethods.BZ2_bzBuffToBuffDecompress(uncompressed, ref uncompressedLen, compressed, + compressedLen, 0, 0); + if(rc < 0) + { + Ice.CompressionException ex = new Ice.CompressionException("BZ2_bzBuffToBuffDecompress failed"); + ex.reason = getBZ2Error(rc); + throw ex; + } + BasicStream ucStream = new BasicStream(instance_, _encoding); + ucStream.resize(uncompressedSize, false); + ucStream.pos(0); + ucStream._buf.b.put(_buf.b.rawBytes(0, headerSize)); + ucStream._buf.b.put(uncompressed, 0, uncompressedLen); + return ucStream; +#endif + } + + public bool isEmpty() + { + return _buf.empty(); + } + + public void expand(int n) + { + _buf.expand(n); + } + + private Ice.Object createObject(string id) + { + Ice.Object obj = null; + + try + { + Type c = AssemblyUtil.findType(instance_, typeToClass(id)); + // + // Ensure the class is instantiable. + // + if(c != null && !c.IsAbstract && !c.IsInterface) + { + obj = (Ice.Object)AssemblyUtil.createInstance(c); + } + } + catch(Exception ex) + { + Ice.NoObjectFactoryException e = new Ice.NoObjectFactoryException(ex); + e.type = id; + throw e; + } + + return obj; + } + + private string getTypeId(int compactId) + { + String className = "IceCompactId.TypeId_" + compactId; + try + { + Type c = AssemblyUtil.findType(instance_, className); + if(c != null) + { + return (string)c.GetField("typeId").GetValue(null); + } + } + catch(Exception) + { + } + return ""; + } + + private sealed class DynamicUserExceptionFactory : UserExceptionFactory + { + internal DynamicUserExceptionFactory(Type c) + { + _class = c; + } + + public void createAndThrow(string typeId) + { + try + { + throw (Ice.UserException)AssemblyUtil.createInstance(_class); + } + catch(Ice.UserException) + { + throw; + } + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + } + + public void destroy() + { + } + + private Type _class; + } + + private Ice.UserException createUserException(string id) + { + Ice.UserException userEx = null; + + try + { + Type c = AssemblyUtil.findType(instance_, typeToClass(id)); + if(c != null) + { + // + // Ensure the class is instantiable. + // + Debug.Assert(!c.IsAbstract && !c.IsInterface); + userEx = (Ice.UserException)AssemblyUtil.createInstance(c); + } + } + catch(Exception ex) + { + throw new Ice.MarshalException(ex); + } + + return userEx; + } + + private static string typeToClass(string id) + { + if(!id.StartsWith("::", StringComparison.Ordinal)) + { + throw new Ice.MarshalException("expected type id but received `" + id + "'"); + } + return id.Substring(2).Replace("::", "."); + } + + // + // Optional data member type. + // + internal const int MemberFormatF1 = 0; + internal const int MemberFormatF2 = 1; + internal const int MemberFormatF4 = 2; + internal const int MemberFormatF8 = 3; + internal const int MemberFormatVSize = 4; + internal const int MemberFormatFSize = 5; + internal const int MemberFormatReserved = 6; + internal const int MemberFormatEndMarker = 7; + + private Instance instance_; + private Buffer _buf; + private object _closure; + private byte[] _stringBytes; // Reusable array for reading strings. + + private enum SliceType { NoSlice, ObjectSlice, ExceptionSlice } + + abstract private class EncapsDecoder + { + internal EncapsDecoder(BasicStream stream, ReadEncaps encaps, bool sliceObjects, ObjectFactoryManager f) + { + _stream = stream; + _encaps = encaps; + _sliceObjects = sliceObjects; + _servantFactoryManager = f; + _typeIdIndex = 0; + _unmarshaledMap = new Dictionary<int, Ice.Object>(); + } + + internal abstract void readObject(IPatcher patcher); + internal abstract void throwException(UserExceptionFactory factory); + + internal abstract void startInstance(SliceType type); + internal abstract Ice.SlicedData endInstance(bool preserve); + internal abstract string startSlice(); + internal abstract void endSlice(); + internal abstract void skipSlice(); + + internal virtual bool readOpt(int tag, Ice.OptionalFormat format) + { + return false; + } + + internal virtual void readPendingObjects() + { + } + + protected string readTypeId(bool isIndex) + { + if(_typeIdMap == null) + { + _typeIdMap = new Dictionary<int, string>(); + } + + if(isIndex) + { + int index = _stream.readSize(); + string typeId; + if(!_typeIdMap.TryGetValue(index, out typeId)) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + return typeId; + } + else + { + string typeId = _stream.readString(); + _typeIdMap.Add(++_typeIdIndex, typeId); + return typeId; + } + } + + protected Ice.Object newInstance(string typeId) + { + // + // Try to find a factory registered for the specific type. + // + Ice.ObjectFactory userFactory = _servantFactoryManager.find(typeId); + Ice.Object v = null; + if(userFactory != null) + { + v = userFactory.create(typeId); + } + + // + // If that fails, invoke the default factory if one has been + // registered. + // + if(v == null) + { + userFactory = _servantFactoryManager.find(""); + if(userFactory != null) + { + v = userFactory.create(typeId); + } + } + + // + // Last chance: try to instantiate the class dynamically. + // + if(v == null) + { + v = _stream.createObject(typeId); + } + + return v; + } + + protected void addPatchEntry(int index, IPatcher patcher) + { + Debug.Assert(index > 0); + + // + // Check if already un-marshalled the object. If that's the case, + // just patch the object smart pointer and we're done. + // + Ice.Object obj; + if(_unmarshaledMap.TryGetValue(index, out obj)) + { + patcher.patch(obj); + return; + } + + if(_patchMap == null) + { + _patchMap = new Dictionary<int, LinkedList<IPatcher>>(); + } + + // + // Add patch entry if the object isn't un-marshalled yet, + // the smart pointer will be patched when the instance is + // un-marshalled. + // + LinkedList<IPatcher> l; + if(!_patchMap.TryGetValue(index, out l)) + { + // + // We have no outstanding instances to be patched for this + // index, so make a new entry in the patch map. + // + l = new LinkedList<IPatcher>(); + _patchMap.Add(index, l); + } + + // + // Append a patch entry for this instance. + // + l.AddLast(patcher); + } + + protected void unmarshal(int index, Ice.Object v) + { + // + // Add the object to the map of un-marshalled objects, this must + // be done before reading the objects (for circular references). + // + _unmarshaledMap.Add(index, v); + + // + // Read the object. + // + v.read__(_stream); + + if(_patchMap != null) + { + // + // Patch all instances now that the object is un-marshalled. + // + LinkedList<IPatcher> l; + if(_patchMap.TryGetValue(index, out l)) + { + Debug.Assert(l.Count > 0); + + // + // Patch all pointers that refer to the instance. + // + foreach(IPatcher p in l) + { + p.patch(v); + } + + // + // Clear out the patch map for that index -- there is nothing left + // to patch for that index for the time being. + // + _patchMap.Remove(index); + } + } + + if((_patchMap == null || _patchMap.Count == 0) && _objectList == null) + { + try + { + v.ice_postUnmarshal(); + } + catch(System.Exception ex) + { + string s = "exception raised by ice_postUnmarshal:\n" + ex; + _stream.instance().initializationData().logger.warning(s); + } + } + else + { + if(_objectList == null) + { + _objectList = new List<Ice.Object>(); + } + _objectList.Add(v); + + if(_patchMap == null || _patchMap.Count == 0) + { + // + // Iterate over the object list and invoke ice_postUnmarshal on + // each object. We must do this after all objects have been + // unmarshaled in order to ensure that any object data members + // have been properly patched. + // + foreach(Ice.Object p in _objectList) + { + try + { + p.ice_postUnmarshal(); + } + catch(System.Exception ex) + { + string s = "exception raised by ice_postUnmarshal:\n" + ex; + _stream.instance().initializationData().logger.warning(s); + } + } + _objectList.Clear(); + } + } + } + + protected readonly BasicStream _stream; + protected readonly ReadEncaps _encaps; + protected readonly bool _sliceObjects; + protected ObjectFactoryManager _servantFactoryManager; + + // Encapsulation attributes for object un-marshalling + protected Dictionary<int, LinkedList<IPatcher> > _patchMap; + + // Encapsulation attributes for object un-marshalling + private Dictionary<int, Ice.Object> _unmarshaledMap; + private Dictionary<int, string> _typeIdMap; + private int _typeIdIndex; + private List<Ice.Object> _objectList; + }; + + private sealed class EncapsDecoder10 : EncapsDecoder + { + internal EncapsDecoder10(BasicStream stream, ReadEncaps encaps, bool sliceObjects, ObjectFactoryManager f) + : base(stream, encaps, sliceObjects, f) + { + _sliceType = SliceType.NoSlice; + } + + internal override void readObject(IPatcher patcher) + { + Debug.Assert(patcher != null); + + // + // Object references are encoded as a negative integer in 1.0. + // + int index = _stream.readInt(); + if(index > 0) + { + throw new Ice.MarshalException("invalid object id"); + } + index = -index; + + if(index == 0) + { + patcher.patch(null); + } + else + { + addPatchEntry(index, patcher); + } + } + + internal override void throwException(UserExceptionFactory factory) + { + Debug.Assert(_sliceType == SliceType.NoSlice); + + // + // User exception with the 1.0 encoding start with a bool flag + // that indicates whether or not the exception has classes. + // + // This allows reading the pending objects even if some part of + // the exception was sliced. + // + bool usesClasses = _stream.readBool(); + + _sliceType = SliceType.ExceptionSlice; + _skipFirstSlice = false; + + // + // Read the first slice header. + // + startSlice(); + string mostDerivedId = _typeId; + while(true) + { + Ice.UserException userEx = null; + + // + // Use a factory if one was provided. + // + if(factory != null) + { + try + { + factory.createAndThrow(_typeId); + } + catch(Ice.UserException ex) + { + userEx = ex; + } + } + + if(userEx == null) + { + userEx = _stream.createUserException(_typeId); + } + + // + // We found the exception. + // + if(userEx != null) + { + userEx.read__(_stream); + if(usesClasses) + { + readPendingObjects(); + } + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + skipSlice(); + try + { + startSlice(); + } + catch(Ice.UnmarshalOutOfBoundsException ex) + { + // + // An oversight in the 1.0 encoding means there is no marker to indicate + // the last slice of an exception. As a result, we just try to read the + // next type ID, which raises UnmarshalOutOfBoundsException when the + // input buffer underflows. + // + // Set the reason member to a more helpful message. + // + ex.reason = "unknown exception type `" + mostDerivedId + "'"; + throw ex; + } + } + } + + internal override void startInstance(SliceType sliceType) + { + Debug.Assert(_sliceType == sliceType); + _skipFirstSlice = true; + } + + internal override Ice.SlicedData endInstance(bool preserve) + { + // + // Read the Ice::Object slice. + // + if(_sliceType == SliceType.ObjectSlice) + { + startSlice(); + int sz = _stream.readSize(); // For compatibility with the old AFM. + if(sz != 0) + { + throw new Ice.MarshalException("invalid Object slice"); + } + endSlice(); + } + + _sliceType = SliceType.NoSlice; + return null; + } + + internal override string startSlice() + { + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(_skipFirstSlice) + { + _skipFirstSlice = false; + return _typeId; + } + + // + // For objects, first read the type ID bool which indicates + // whether or not the type ID is encoded as a string or as an + // index. For exceptions, the type ID is always encoded as a + // string. + // + if(_sliceType == SliceType.ObjectSlice) // For exceptions, the type ID is always encoded as a string + { + bool isIndex = _stream.readBool(); + _typeId = readTypeId(isIndex); + } + else + { + _typeId = _stream.readString(); + } + + _sliceSize = _stream.readInt(); + if(_sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + + return _typeId; + } + + internal override void endSlice() + { + } + + internal override void skipSlice() + { + if(_stream.instance().traceLevels().slicing > 0) + { + Ice.Logger logger = _stream.instance().initializationData().logger; + string slicingCat = _stream.instance().traceLevels().slicingCat; + if(_sliceType == SliceType.ObjectSlice) + { + TraceUtil.traceSlicing("object", _typeId, slicingCat, logger); + } + else + { + TraceUtil.traceSlicing("exception", _typeId, slicingCat, logger); + } + } + + Debug.Assert(_sliceSize >= 4); + _stream.skip(_sliceSize - 4); + } + + internal override void readPendingObjects() + { + int num; + do + { + num = _stream.readSize(); + for(int k = num; k > 0; --k) + { + readInstance(); + } + } + while(num > 0); + + if(_patchMap != null && _patchMap.Count > 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an object, but failed + // to supply the object. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + } + + private void readInstance() + { + int index = _stream.readInt(); + + if(index <= 0) + { + throw new Ice.MarshalException("invalid object id"); + } + + _sliceType = SliceType.ObjectSlice; + _skipFirstSlice = false; + + // + // Read the first slice header. + // + startSlice(); + string mostDerivedId = _typeId; + Ice.Object v = null; + while(true) + { + // + // For the 1.0 encoding, the type ID for the base Object class + // marks the last slice. + // + if(_typeId.Equals(Ice.ObjectImpl.ice_staticId())) + { + throw new Ice.NoObjectFactoryException("", mostDerivedId); + } + + v = newInstance(_typeId); + + // + // We found a factory, we get out of this loop. + // + if(v != null) + { + break; + } + + // + // If object slicing is disabled, stop un-marshalling. + // + if(!_sliceObjects) + { + throw new Ice.NoObjectFactoryException("no object factory found and object slicing is disabled", + _typeId); + } + + // + // Slice off what we don't understand. + // + skipSlice(); + startSlice(); // Read next Slice header for next iteration. + } + + // + // Un-marshal the object and add-it to the map of un-marshaled objects. + // + unmarshal(index, v); + } + + // Object/exception attributes + private SliceType _sliceType; + private bool _skipFirstSlice; + + // Slice attributes + private int _sliceSize; + private string _typeId; + } + + private sealed class EncapsDecoder11 : EncapsDecoder + { + internal EncapsDecoder11(BasicStream stream, ReadEncaps encaps, bool sliceObjects, ObjectFactoryManager f) + : base(stream, encaps, sliceObjects, f) + { + _objectIdIndex = 1; + _current = null; + } + + internal override void readObject(IPatcher patcher) + { + int index = _stream.readSize(); + if(index < 0) + { + throw new Ice.MarshalException("invalid object id"); + } + else if(index == 0) + { + if(patcher != null) + { + patcher.patch(null); + } + } + else if(_current != null && (_current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) != 0) + { + // + // When reading an object within a slice and there's an + // indirect object table, always read an indirect reference + // that points to an object from the indirect object table + // marshaled at the end of the Slice. + // + // Maintain a list of indirect references. Note that the + // indirect index starts at 1, so we decrement it by one to + // derive an index into the indirection table that we'll read + // at the end of the slice. + // + if(patcher != null) + { + if(_current.indirectPatchList == null) + { + _current.indirectPatchList = new Stack<IndirectPatchEntry>(); + } + IndirectPatchEntry e = new IndirectPatchEntry(); + e.index = index - 1; + e.patcher = patcher; + _current.indirectPatchList.Push(e); + } + } + else + { + readInstance(index, patcher); + } + } + + internal override void throwException(UserExceptionFactory factory) + { + Debug.Assert(_current == null); + + push(SliceType.ExceptionSlice); + + // + // Read the first slice header. + // + startSlice(); + string mostDerivedId = _current.typeId; + while(true) + { + Ice.UserException userEx = null; + + // + // Use a factory if one was provided. + // + if(factory != null) + { + try + { + factory.createAndThrow(_current.typeId); + } + catch(Ice.UserException ex) + { + userEx = ex; + } + } + + if(userEx == null) + { + userEx = _stream.createUserException(_current.typeId); + } + + // + // We found the exception. + // + if(userEx != null) + { + userEx.read__(_stream); + throw userEx; + + // Never reached. + } + + // + // Slice off what we don't understand. + // + skipSlice(); + + if((_current.sliceFlags & FLAG_IS_LAST_SLICE) != 0) + { + if(mostDerivedId.StartsWith("::", StringComparison.Ordinal)) + { + throw new Ice.UnknownUserException(mostDerivedId.Substring(2)); + } + else + { + throw new Ice.UnknownUserException(mostDerivedId); + } + } + + startSlice(); + } + } + + internal override void startInstance(SliceType sliceType) + { + Debug.Assert(_current.sliceType == sliceType); + _current.skipFirstSlice = true; + } + + internal override Ice.SlicedData endInstance(bool preserve) + { + Ice.SlicedData slicedData = null; + if(preserve) + { + slicedData = readSlicedData(); + } + if(_current.slices != null) + { + _current.slices.Clear(); + _current.indirectionTables.Clear(); + } + _current = _current.previous; + return slicedData; + } + + internal override string startSlice() + { + // + // If first slice, don't read the header, it was already read in + // readInstance or throwException to find the factory. + // + if(_current.skipFirstSlice) + { + _current.skipFirstSlice = false; + return _current.typeId; + } + + _current.sliceFlags = _stream.readByte(); + + // + // Read the type ID, for object slices the type ID is encoded as a + // string or as an index, for exceptions it's always encoded as a + // string. + // + if(_current.sliceType == SliceType.ObjectSlice) + { + if((_current.sliceFlags & FLAG_HAS_TYPE_ID_COMPACT) == FLAG_HAS_TYPE_ID_COMPACT) // Must + // be + // checked + // 1st! + { + _current.typeId = ""; + _current.compactId = _stream.readSize(); + } + else if((_current.sliceFlags & (FLAG_HAS_TYPE_ID_INDEX | FLAG_HAS_TYPE_ID_STRING)) != 0) + { + _current.typeId = readTypeId((_current.sliceFlags & FLAG_HAS_TYPE_ID_INDEX) != 0); + _current.compactId = -1; + } + else + { + // Only the most derived slice encodes the type ID for the compact format. + _current.typeId = ""; + _current.compactId = -1; + } + } + else + { + _current.typeId = _stream.readString(); + _current.compactId = -1; + } + + // + // Read the slice size if necessary. + // + if((_current.sliceFlags & FLAG_HAS_SLICE_SIZE) != 0) + { + _current.sliceSize = _stream.readInt(); + if(_current.sliceSize < 4) + { + throw new Ice.UnmarshalOutOfBoundsException(); + } + } + else + { + _current.sliceSize = 0; + } + + return _current.typeId; + } + + internal override void endSlice() + { + if((_current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) != 0) + { + _stream.skipOpts(); + } + + // + // Read the indirection table if one is present and transform the + // indirect patch list into patch entries with direct references. + // + if((_current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) != 0) + { + // + // The table is written as a sequence<size> to conserve space. + // + int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)]; + for(int i = 0; i < indirectionTable.Length; ++i) + { + indirectionTable[i] = readInstance(_stream.readSize(), null); + } + + // + // Sanity checks. If there are optional members, it's possible + // that not all object references were read if they are from + // unknown optional data members. + // + if(indirectionTable.Length == 0) + { + throw new Ice.MarshalException("empty indirection table"); + } + if((_current.indirectPatchList == null || _current.indirectPatchList.Count == 0) && + (_current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) == 0) + { + throw new Ice.MarshalException("no references to indirection table"); + } + + // + // Convert indirect references into direct references. + // + if(_current.indirectPatchList != null) + { + foreach(IndirectPatchEntry e in _current.indirectPatchList) + { + Debug.Assert(e.index >= 0); + if(e.index >= indirectionTable.Length) + { + throw new Ice.MarshalException("indirection out of range"); + } + addPatchEntry(indirectionTable[e.index], e.patcher); + } + _current.indirectPatchList.Clear(); + } + } + } + + internal override void skipSlice() + { + if(_stream.instance().traceLevels().slicing > 0) + { + Ice.Logger logger = _stream.instance().initializationData().logger; + string slicingCat = _stream.instance().traceLevels().slicingCat; + if(_current.sliceType == SliceType.ExceptionSlice) + { + TraceUtil.traceSlicing("exception", _current.typeId, slicingCat, logger); + } + else + { + TraceUtil.traceSlicing("object", _current.typeId, slicingCat, logger); + } + } + + int start = _stream.pos(); + + if((_current.sliceFlags & FLAG_HAS_SLICE_SIZE) != 0) + { + Debug.Assert(_current.sliceSize >= 4); + _stream.skip(_current.sliceSize - 4); + } + else + { + if(_current.sliceType == SliceType.ObjectSlice) + { + throw new Ice.NoObjectFactoryException("no object factory found and compact format prevents " + + "slicing (the sender should use the sliced format " + + "instead)", _current.typeId); + } + else + { + if(_current.typeId.StartsWith("::", StringComparison.Ordinal)) + { + throw new Ice.UnknownUserException(_current.typeId.Substring(2)); + } + else + { + throw new Ice.UnknownUserException(_current.typeId); + } + } + } + + // + // Preserve this slice. + // + Ice.SliceInfo info = new Ice.SliceInfo(); + info.typeId = _current.typeId; + info.compactId = _current.compactId; + info.hasOptionalMembers = (_current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) != 0; + info.isLastSlice = (_current.sliceFlags & FLAG_IS_LAST_SLICE) != 0; + ByteBuffer b = _stream.getBuffer().b; + int end = b.position(); + int dataEnd = end; + if(info.hasOptionalMembers) + { + // + // Don't include the optional member end marker. It will be re-written by + // endSlice when the sliced data is re-written. + // + --dataEnd; + } + info.bytes = new byte[dataEnd - start]; + b.position(start); + b.get(info.bytes); + b.position(end); + + if(_current.slices == null) + { + _current.slices = new List<Ice.SliceInfo>(); + _current.indirectionTables = new List<int[]>(); + } + + // + // Read the indirect object table. We read the instances or their + // IDs if the instance is a reference to an already un-marhsaled + // object. + // + // The SliceInfo object sequence is initialized only if + // readSlicedData is called. + // + if((_current.sliceFlags & FLAG_HAS_INDIRECTION_TABLE) != 0) + { + int[] indirectionTable = new int[_stream.readAndCheckSeqSize(1)]; + for(int i = 0; i < indirectionTable.Length; ++i) + { + indirectionTable[i] = readInstance(_stream.readSize(), null); + } + _current.indirectionTables.Add(indirectionTable); + } + else + { + _current.indirectionTables.Add(null); + } + + _current.slices.Add(info); + } + + internal override bool readOpt(int readTag, Ice.OptionalFormat expectedFormat) + { + if(_current == null) + { + return _stream.readOptImpl(readTag, expectedFormat); + } + else if((_current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) != 0) + { + return _stream.readOptImpl(readTag, expectedFormat); + } + return false; + } + + private int readInstance(int index, IPatcher patcher) + { + Debug.Assert(index > 0); + + if(index > 1) + { + if(patcher != null) + { + addPatchEntry(index, patcher); + } + return index; + } + + push(SliceType.ObjectSlice); + + // + // Get the object ID before we start reading slices. If some + // slices are skiped, the indirect object table are still read and + // might read other objects. + // + index = ++_objectIdIndex; + + // + // Read the first slice header. + // + startSlice(); + string mostDerivedId = _current.typeId; + Ice.Object v = null; + Ice.CompactIdResolver compactIdResolver = _stream.instance().initializationData().compactIdResolver; + while(true) + { + if(_current.compactId >= 0) + { + // + // Translate a compact (numeric) type ID into a string type ID. + // + _current.typeId = ""; + if(compactIdResolver != null) + { + try + { + _current.typeId = compactIdResolver(_current.compactId); + } + catch(Ice.LocalException) + { + throw; + } + catch(System.Exception ex) + { + throw new Ice.MarshalException("exception in CompactIdResolver for ID " + + _current.compactId, ex); + } + } + if(_current.typeId.Length == 0) + { + _current.typeId = _stream.getTypeId(_current.compactId); + } + } + + if(_current.typeId.Length > 0) + { + v = newInstance(_current.typeId); + + // + // We found a factory, we get out of this loop. + // + if(v != null) + { + break; + } + } + + // + // If object slicing is disabled, stop un-marshalling. + // + if(!_sliceObjects) + { + throw new Ice.NoObjectFactoryException("no object factory found and object slicing is disabled", + _current.typeId); + } + + // + // Slice off what we don't understand. + // + skipSlice(); + + // + // If this is the last slice, keep the object as an opaque + // UnknownSlicedData object. + // + if((_current.sliceFlags & FLAG_IS_LAST_SLICE) != 0) + { + // + // Provide a factory with an opportunity to supply the object. + // We pass the "::Ice::Object" ID to indicate that this is the + // last chance to preserve the object. + // + v = newInstance(Ice.ObjectImpl.ice_staticId()); + if(v == null) + { + v = new Ice.UnknownSlicedObject(mostDerivedId); + } + + break; + } + + startSlice(); // Read next Slice header for next iteration. + } + + // + // Un-marshal the object + // + unmarshal(index, v); + + if(_current == null && _patchMap != null && _patchMap.Count > 0) + { + // + // If any entries remain in the patch map, the sender has sent an index for an object, but failed + // to supply the object. + // + throw new Ice.MarshalException("index for class received, but no instance"); + } + + if(patcher != null) + { + patcher.patch(v); + } + return index; + } + + private Ice.SlicedData readSlicedData() + { + if(_current.slices == null) // No preserved slices. + { + return null; + } + + // + // The _indirectionTables member holds the indirection table for each slice + // in _slices. + // + Debug.Assert(_current.slices.Count == _current.indirectionTables.Count); + for(int n = 0; n < _current.slices.Count; ++n) + { + // + // We use the "objects" list in SliceInfo to hold references + // to the target objects. Note that the objects might not have + // been read yet in the case of a circular reference to an + // enclosing object. + // + int[] table = _current.indirectionTables[n]; + Ice.SliceInfo info = _current.slices[n]; + info.objects = new Ice.Object[table != null ? table.Length : 0]; + for(int j = 0; j < info.objects.Length; ++j) + { + IPatcher patcher = new ArrayPatcher<Ice.Object>(Ice.ObjectImpl.ice_staticId(), info.objects, j); + addPatchEntry(table[j], patcher); + } + } + + return new Ice.SlicedData(_current.slices.ToArray()); + } + + private void push(SliceType sliceType) + { + if(_current == null) + { + _current = new InstanceData(null); + } + else + { + _current = _current.next == null ? new InstanceData(_current) : _current.next; + } + _current.sliceType = sliceType; + _current.skipFirstSlice = false; + } + + private sealed class IndirectPatchEntry + { + public int index; + public IPatcher patcher; + } + + private sealed class InstanceData + { + internal InstanceData(InstanceData previous) + { + if(previous != null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + } + + // Instance attributes + internal SliceType sliceType; + internal bool skipFirstSlice; + internal List<Ice.SliceInfo> slices; // Preserved slices. + internal List<int[]> indirectionTables; + + // Slice attributes + internal byte sliceFlags; + internal int sliceSize; + internal string typeId; + internal int compactId; + internal Stack<IndirectPatchEntry> indirectPatchList; + + internal InstanceData previous; + internal InstanceData next; + }; + private InstanceData _current; + + private int _objectIdIndex; // The ID of the next object to un-marshal. + }; + + abstract private class EncapsEncoder + { + protected EncapsEncoder(BasicStream stream, WriteEncaps encaps) + { + _stream = stream; + _encaps = encaps; + _typeIdIndex = 0; + _marshaledMap = new Dictionary<Ice.Object, int>(); + } + + internal abstract void writeObject(Ice.Object v); + internal abstract void writeUserException(Ice.UserException v); + + internal abstract void startInstance(SliceType type, Ice.SlicedData data); + internal abstract void endInstance(); + internal abstract void startSlice(string typeId, int compactId, bool last); + internal abstract void endSlice(); + + internal virtual bool writeOpt(int tag, Ice.OptionalFormat format) + { + return false; + } + + internal virtual void writePendingObjects() + { + } + + protected int registerTypeId(string typeId) + { + if(_typeIdMap == null) + { + _typeIdMap = new Dictionary<string, int>(); + } + + int p; + if(_typeIdMap.TryGetValue(typeId, out p)) + { + return p; + } + else + { + _typeIdMap.Add(typeId, ++_typeIdIndex); + return -1; + } + } + + protected readonly BasicStream _stream; + protected readonly WriteEncaps _encaps; + + // Encapsulation attributes for object marshalling. + protected readonly Dictionary<Ice.Object, int> _marshaledMap; + + // Encapsulation attributes for object marshalling. + private Dictionary<string, int> _typeIdMap; + private int _typeIdIndex; + }; + + private sealed class EncapsEncoder10 : EncapsEncoder + { + internal EncapsEncoder10(BasicStream stream, WriteEncaps encaps) : base(stream, encaps) + { + _sliceType = SliceType.NoSlice; + _objectIdIndex = 0; + _toBeMarshaledMap = new Dictionary<Ice.Object, int>(); + } + + internal override void writeObject(Ice.Object v) + { + // + // Object references are encoded as a negative integer in 1.0. + // + if(v != null) + { + _stream.writeInt(-registerObject(v)); + } + else + { + _stream.writeInt(0); + } + } + + internal override void writeUserException(Ice.UserException v) + { + // + // User exception with the 1.0 encoding start with a bool + // flag that indicates whether or not the exception uses + // classes. + // + // This allows reading the pending objects even if some part of + // the exception was sliced. + // + bool usesClasses = v.usesClasses__(); + _stream.writeBool(usesClasses); + v.write__(_stream); + if(usesClasses) + { + writePendingObjects(); + } + } + + internal override void startInstance(SliceType sliceType, Ice.SlicedData sliceData) + { + _sliceType = sliceType; + } + + internal override void endInstance() + { + if(_sliceType == SliceType.ObjectSlice) + { + // + // Write the Object slice. + // + startSlice(Ice.ObjectImpl.ice_staticId(), -1, true); + _stream.writeSize(0); // For compatibility with the old AFM. + endSlice(); + } + _sliceType = SliceType.NoSlice; + } + + internal override void startSlice(string typeId, int compactId, bool last) + { + // + // For object slices, encode a bool to indicate how the type ID + // is encoded and the type ID either as a string or index. For + // exception slices, always encode the type ID as a string. + // + if(_sliceType == SliceType.ObjectSlice) + { + int index = registerTypeId(typeId); + if(index < 0) + { + _stream.writeBool(false); + _stream.writeString(typeId); + } + else + { + _stream.writeBool(true); + _stream.writeSize(index); + } + } + else + { + _stream.writeString(typeId); + } + + _stream.writeInt(0); // Placeholder for the slice length. + + _writeSlice = _stream.pos(); + } + + internal override void endSlice() + { + // + // Write the slice length. + // + int sz = _stream.pos() - _writeSlice + 4; + _stream.rewriteInt(sz, _writeSlice - 4); + } + + internal override void writePendingObjects() + { + while(_toBeMarshaledMap.Count > 0) + { + // + // Consider the to be marshalled objects as marshalled now, + // this is necessary to avoid adding again the "to be + // marshalled objects" into _toBeMarshaledMap while writing + // objects. + // + foreach(KeyValuePair<Ice.Object, int> e in _toBeMarshaledMap) + { + _marshaledMap.Add(e.Key, e.Value); + } + + Dictionary<Ice.Object, int> savedMap = _toBeMarshaledMap; + _toBeMarshaledMap = new Dictionary<Ice.Object, int>(); + _stream.writeSize(savedMap.Count); + foreach(KeyValuePair<Ice.Object, int> p in savedMap) + { + // + // Ask the instance to marshal itself. Any new class + // instances that are triggered by the classes marshaled + // are added to toBeMarshaledMap. + // + _stream.writeInt(p.Value); + + try + { + p.Key.ice_preMarshal(); + } + catch(System.Exception ex) + { + string s = "exception raised by ice_preMarshal:\n" + ex; + _stream.instance().initializationData().logger.warning(s); + } + + p.Key.write__(_stream); + } + } + _stream.writeSize(0); // Zero marker indicates end of sequence of sequences of instances. + } + + private int registerObject(Ice.Object v) + { + Debug.Assert(v != null); + + // + // Look for this instance in the to-be-marshaled map. + // + int p; + if(_toBeMarshaledMap.TryGetValue(v, out p)) + { + return p; + } + + // + // Didn't find it, try the marshaled map next. + // + if(_marshaledMap.TryGetValue(v, out p)) + { + return p; + } + + // + // We haven't seen this instance previously, create a new + // index, and insert it into the to-be-marshaled map. + // + _toBeMarshaledMap.Add(v, ++_objectIdIndex); + return _objectIdIndex; + } + + // Instance attributes + private SliceType _sliceType; + + // Slice attributes + private int _writeSlice; // Position of the slice data members + + // Encapsulation attributes for object marshalling. + private int _objectIdIndex; + private Dictionary<Ice.Object, int> _toBeMarshaledMap; + }; + + private sealed class EncapsEncoder11 : EncapsEncoder + { + internal EncapsEncoder11(BasicStream stream, WriteEncaps encaps) : base(stream, encaps) + { + _current = null; + _objectIdIndex = 1; + } + + internal override void writeObject(Ice.Object v) + { + if(v == null) + { + _stream.writeSize(0); + } + else if(_current != null && _encaps.format == Ice.FormatType.SlicedFormat) + { + if(_current.indirectionTable == null) + { + _current.indirectionTable = new List<Ice.Object>(); + _current.indirectionMap = new Dictionary<Ice.Object, int>(); + } + + // + // If writting an object within a slice and using the sliced + // format, write an index from the object indirection table. + // + int index; + if(!_current.indirectionMap.TryGetValue(v, out index)) + { + _current.indirectionTable.Add(v); + int idx = _current.indirectionTable.Count; // Position + 1 (0 is reserved for nil) + _current.indirectionMap.Add(v, idx); + _stream.writeSize(idx); + } + else + { + _stream.writeSize(index); + } + } + else + { + writeInstance(v); // Write the instance or a reference if already marshaled. + } + } + + internal override void writeUserException(Ice.UserException v) + { + v.write__(_stream); + } + + internal override void startInstance(SliceType sliceType, Ice.SlicedData data) + { + if(_current == null) + { + _current = new InstanceData(null); + } + else + { + _current = _current.next == null ? new InstanceData(_current) : _current.next; + } + _current.sliceType = sliceType; + _current.firstSlice = true; + + if(data != null) + { + writeSlicedData(data); + } + } + + internal override void endInstance() + { + _current = _current.previous; + } + + internal override void startSlice(string typeId, int compactId, bool last) + { + Debug.Assert((_current.indirectionTable == null || _current.indirectionTable.Count == 0) && + (_current.indirectionMap == null || _current.indirectionMap.Count == 0)); + + _current.sliceFlagsPos = _stream.pos(); + + _current.sliceFlags = (byte)0; + if(_encaps.format == Ice.FormatType.SlicedFormat) + { + _current.sliceFlags |= FLAG_HAS_SLICE_SIZE; // Encode the slice size if using the sliced format. + } + if(last) + { + _current.sliceFlags |= FLAG_IS_LAST_SLICE; // This is the last slice. + } + + _stream.writeByte((byte)0); // Placeholder for the slice flags + + // + // For object slices, encode the flag and the type ID either as a + // string or index. For exception slices, always encode the type + // ID a string. + // + if(_current.sliceType == SliceType.ObjectSlice) + { + // + // Encode the type ID (only in the first slice for the compact + // encoding). + // + if(_encaps.format == Ice.FormatType.SlicedFormat || _current.firstSlice) + { + if(compactId >= 0) + { + _current.sliceFlags |= FLAG_HAS_TYPE_ID_COMPACT; + _stream.writeSize(compactId); + } + else + { + int index = registerTypeId(typeId); + if(index < 0) + { + _current.sliceFlags |= FLAG_HAS_TYPE_ID_STRING; + _stream.writeString(typeId); + } + else + { + _current.sliceFlags |= FLAG_HAS_TYPE_ID_INDEX; + _stream.writeSize(index); + } + } + } + } + else + { + _stream.writeString(typeId); + } + + if((_current.sliceFlags & FLAG_HAS_SLICE_SIZE) != 0) + { + _stream.writeInt(0); // Placeholder for the slice length. + } + + _current.writeSlice = _stream.pos(); + _current.firstSlice = false; + } + + internal override void endSlice() + { + // + // Write the optional member end marker if some optional members + // were encoded. Note that the optional members are encoded before + // the indirection table and are included in the slice size. + // + if((_current.sliceFlags & FLAG_HAS_OPTIONAL_MEMBERS) != 0) + { + _stream.writeByte((byte)OPTIONAL_END_MARKER); + } + + // + // Write the slice length if necessary. + // + if((_current.sliceFlags & FLAG_HAS_SLICE_SIZE) != 0) + { + int sz = _stream.pos() - _current.writeSlice + 4; + _stream.rewriteInt(sz, _current.writeSlice - 4); + } + + // + // Only write the indirection table if it contains entries. + // + if(_current.indirectionTable != null && _current.indirectionTable.Count > 0) + { + Debug.Assert(_encaps.format == Ice.FormatType.SlicedFormat); + _current.sliceFlags |= FLAG_HAS_INDIRECTION_TABLE; + + // + // Write the indirection object table. + // + _stream.writeSize(_current.indirectionTable.Count); + foreach(Ice.Object v in _current.indirectionTable) + { + writeInstance(v); + } + _current.indirectionTable.Clear(); + _current.indirectionMap.Clear(); + } + + // + // Finally, update the slice flags. + // + _stream.rewriteByte(_current.sliceFlags, _current.sliceFlagsPos); + } + + internal override bool writeOpt(int tag, Ice.OptionalFormat format) + { + if(_current == null) + { + return _stream.writeOptImpl(tag, format); + } + else + { + if(_stream.writeOptImpl(tag, format)) + { + _current.sliceFlags |= FLAG_HAS_OPTIONAL_MEMBERS; + return true; + } + else + { + return false; + } + } + } + + private void writeSlicedData(Ice.SlicedData slicedData) + { + Debug.Assert(slicedData != null); + + // + // We only remarshal preserved slices if we are using the sliced + // format. Otherwise, we ignore the preserved slices, which + // essentially "slices" the object into the most-derived type + // known by the sender. + // + if(_encaps.format != Ice.FormatType.SlicedFormat) + { + return; + } + + foreach(Ice.SliceInfo info in slicedData.slices) + { + startSlice(info.typeId, info.compactId, info.isLastSlice); + + // + // Write the bytes associated with this slice. + // + _stream.writeBlob(info.bytes); + + if(info.hasOptionalMembers) + { + _current.sliceFlags |= FLAG_HAS_OPTIONAL_MEMBERS; + } + + // + // Make sure to also re-write the object indirection table. + // + if(info.objects != null && info.objects.Length > 0) + { + if(_current.indirectionTable == null) + { + _current.indirectionTable = new List<Ice.Object>(); + _current.indirectionMap = new Dictionary<Ice.Object, int>(); + } + foreach(Ice.Object o in info.objects) + { + _current.indirectionTable.Add(o); + } + } + + endSlice(); + } + } + + private void writeInstance(Ice.Object v) + { + Debug.Assert(v != null); + + // + // If the instance was already marshaled, just write it's ID. + // + int p; + if(_marshaledMap.TryGetValue(v, out p)) + { + _stream.writeSize(p); + return; + } + + // + // We haven't seen this instance previously, create a new ID, + // insert it into the marshaled map, and write the instance. + // + _marshaledMap.Add(v, ++_objectIdIndex); + + try + { + v.ice_preMarshal(); + } + catch(System.Exception ex) + { + string s = "exception raised by ice_preMarshal:\n" + ex; + _stream.instance().initializationData().logger.warning(s); + } + + _stream.writeSize(1); // Object instance marker. + v.write__(_stream); + } + + private sealed class InstanceData + { + internal InstanceData(InstanceData previous) + { + if(previous != null) + { + previous.next = this; + } + this.previous = previous; + this.next = null; + } + + // Instance attributes + internal SliceType sliceType; + internal bool firstSlice; + + // Slice attributes + internal byte sliceFlags; + internal int writeSlice; // Position of the slice data members + internal int sliceFlagsPos; // Position of the slice flags + internal List<Ice.Object> indirectionTable; + internal Dictionary<Ice.Object, int> indirectionMap; + + internal InstanceData previous; + internal InstanceData next; + }; + private InstanceData _current; + + private int _objectIdIndex; // The ID of the next object to marhsal + }; + + private sealed class ReadEncaps + { + internal void reset() + { + decoder = null; + } + + internal void setEncoding(Ice.EncodingVersion encoding) + { + this.encoding = encoding; + encoding_1_0 = encoding.Equals(Ice.Util.Encoding_1_0); + } + + internal int start; + internal int sz; + internal Ice.EncodingVersion encoding; + internal bool encoding_1_0; + + internal EncapsDecoder decoder; + + internal ReadEncaps next; + } + + private sealed class WriteEncaps + { + internal void reset() + { + encoder = null; + } + + internal void setEncoding(Ice.EncodingVersion encoding) + { + this.encoding = encoding; + encoding_1_0 = encoding.Equals(Ice.Util.Encoding_1_0); + } + + internal int start; + internal Ice.EncodingVersion encoding; + internal bool encoding_1_0; + internal Ice.FormatType format = Ice.FormatType.DefaultFormat; + + internal EncapsEncoder encoder; + + internal WriteEncaps next; + } + + // + // The encoding version to use when there's no encapsulation to + // read from or write to. This is for example used to read message + // headers or when the user is using the streaming API with no + // encapsulation. + // + private Ice.EncodingVersion _encoding; + + private bool isReadEncoding_1_0() + { + return _readEncapsStack != null ? _readEncapsStack.encoding_1_0 : _encoding.Equals(Ice.Util.Encoding_1_0); + } + + private bool isWriteEncoding_1_0() + { + return _writeEncapsStack != null ? _writeEncapsStack.encoding_1_0 : _encoding.Equals(Ice.Util.Encoding_1_0); + } + + private ReadEncaps _readEncapsStack; + private WriteEncaps _writeEncapsStack; + private ReadEncaps _readEncapsCache; + private WriteEncaps _writeEncapsCache; + + private void initReadEncaps() + { + if(_readEncapsStack == null) // Lazy initialization + { + _readEncapsStack = _readEncapsCache; + if(_readEncapsStack != null) + { + _readEncapsCache = _readEncapsCache.next; + } + else + { + _readEncapsStack = new ReadEncaps(); + } + _readEncapsStack.setEncoding(_encoding); + _readEncapsStack.sz = _buf.b.limit(); + } + + if(_readEncapsStack.decoder == null) // Lazy initialization. + { + ObjectFactoryManager factoryMgr = instance_.servantFactoryManager(); + if(_readEncapsStack.encoding_1_0) + { + _readEncapsStack.decoder = new EncapsDecoder10(this, _readEncapsStack, _sliceObjects, factoryMgr); + } + else + { + _readEncapsStack.decoder = new EncapsDecoder11(this, _readEncapsStack, _sliceObjects, factoryMgr); + }; + } + } + + private void initWriteEncaps() + { + if(_writeEncapsStack == null) // Lazy initialization + { + _writeEncapsStack = _writeEncapsCache; + if(_writeEncapsStack != null) + { + _writeEncapsCache = _writeEncapsCache.next; + } + else + { + _writeEncapsStack = new WriteEncaps(); + } + _writeEncapsStack.setEncoding(_encoding); + } + + if(_writeEncapsStack.format == Ice.FormatType.DefaultFormat) + { + _writeEncapsStack.format = instance_.defaultsAndOverrides().defaultFormat; + } + + if(_writeEncapsStack.encoder == null) // Lazy initialization. + { + if(_writeEncapsStack.encoding_1_0) + { + _writeEncapsStack.encoder = new EncapsEncoder10(this, _writeEncapsStack); + } + else + { + _writeEncapsStack.encoder = new EncapsEncoder11(this, _writeEncapsStack); + } + } + } + + private bool _sliceObjects; + + private int _startSeq; + private int _minSeqSize; + + private const byte OPTIONAL_END_MARKER = 0xFF; + + private const byte FLAG_HAS_TYPE_ID_STRING = (byte)(1<<0); + private const byte FLAG_HAS_TYPE_ID_INDEX = (byte)(1<<1); + private const byte FLAG_HAS_TYPE_ID_COMPACT = (byte)(1<<1 | 1<<0); + private const byte FLAG_HAS_OPTIONAL_MEMBERS = (byte)(1<<2); + private const byte FLAG_HAS_INDIRECTION_TABLE = (byte)(1<<3); + private const byte FLAG_HAS_SLICE_SIZE = (byte)(1<<4); + private const byte FLAG_IS_LAST_SLICE = (byte)(1<<5); + + private static bool _bzlibInstalled; + + const int BZ_SEQUENCE_ERROR = -1; + const int BZ_PARAM_ERROR = -2; + const int BZ_MEM_ERROR = -3; + const int BZ_DATA_ERROR = -4; + const int BZ_DATA_ERROR_MAGIC = -5; + const int BZ_IO_ERROR = -6; + const int BZ_UNEXPECTED_EOF = -7; + const int BZ_OUTBUFF_FULL = -8; + const int BZ_CONFIG_ERROR = -9; + } + +} diff --git a/csharp/src/Ice/BatchRequestInterceptor.cs b/csharp/src/Ice/BatchRequestInterceptor.cs new file mode 100644 index 00000000000..7cd79d63991 --- /dev/null +++ b/csharp/src/Ice/BatchRequestInterceptor.cs @@ -0,0 +1,57 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + public interface BatchRequest + { + /// <summary> + /// Confirms the queuing of the batch request. + /// </summary> + void enqueue(); + + /// <summary> + /// Get the marshalled size of the request. + /// </summary> + /// <returns>The request size.</returns> + int getSize(); + + /// <summary> + /// Get the name of the operation + /// </summary> + /// <returns>The request operation.</returns> + string getOperation(); + + /// <summary> + /// The proxy used to invoke the batch request. + /// </summary> + /// <returns>The request proxy.</returns> + Ice.ObjectPrx getProxy(); + }; + + /// <summary> + /// Base interface for listening to batch request queues. + /// </summary> + public interface BatchRequestInterceptor + { + /// <summary> + /// Called by the Ice runtime when a batch request is about to be + /// added to the batch request queue of a proxy or connection. + /// + /// The implementation of this method must call enqueue() on the + /// request to confirm its addition to the queue, if not called + /// the request isn't added to the queue. The implementation can + /// raise an Ice local exception to notify the caller of a failure. + /// </summary> + /// <param name="request">The batch request.</param> + /// <param name="queueBatchRequestCount">The number of batch request queued.</param> + /// <param name="queueBatchRequestSize">The size of the queued batch requests.</param> + void enqueue(Ice.BatchRequest request, int queueBatchRequestCount, int queueBatchRequestSize); + }; +}; diff --git a/csharp/src/Ice/BatchRequestQueue.cs b/csharp/src/Ice/BatchRequestQueue.cs new file mode 100644 index 00000000000..2973c6fae12 --- /dev/null +++ b/csharp/src/Ice/BatchRequestQueue.cs @@ -0,0 +1,244 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Diagnostics; + +namespace IceInternal +{ + sealed class BatchRequestI : Ice.BatchRequest + { + public BatchRequestI(BatchRequestQueue queue) + { + _queue = queue; + } + + public void reset(Ice.ObjectPrx proxy, string operation, int size) + { + _proxy = proxy; + _operation = operation; + _size = size; + } + + public void enqueue() + { + _queue.enqueueBatchRequest(); + } + + public Ice.ObjectPrx getProxy() + { + return _proxy; + } + + public string getOperation() + { + return _operation; + } + + public int getSize() + { + return _size; + } + + private BatchRequestQueue _queue; + private Ice.ObjectPrx _proxy; + private string _operation; + private int _size; + }; + + + public sealed class BatchRequestQueue + { + public BatchRequestQueue(Instance instance, bool datagram) + { + Ice.InitializationData initData = instance.initializationData(); + _interceptor = initData.batchRequestInterceptor; + _batchStreamInUse = false; + _batchRequestNum = 0; + _batchStream = new BasicStream(instance, Ice.Util.currentProtocolEncoding); + _batchStream.writeBlob(Protocol.requestBatchHdr); + _batchMarker = _batchStream.size(); + _request = new BatchRequestI(this); + + _maxSize = instance.batchAutoFlushSize(); + if(_maxSize > 0 && datagram) + { + int udpSndSize = initData.properties.getPropertyAsIntWithDefault("Ice.UDP.SndSize", + 65535 - _udpOverhead); + if(udpSndSize < _maxSize) + { + _maxSize = udpSndSize; + } + } + } + + public void + prepareBatchRequest(BasicStream os) + { + lock(this) + { + if(_exception != null) + { + throw _exception; + } + waitStreamInUse(false); + _batchStreamInUse = true; + _batchStream.swap(os); + } + } + + public void + finishBatchRequest(BasicStream os, Ice.ObjectPrx proxy, string operation) + { + // + // No need for synchronization, no other threads are supposed + // to modify the queue since we set _batchStreamInUse to true. + // + Debug.Assert(_batchStreamInUse); + _batchStream.swap(os); + + try + { + _batchStreamCanFlush = true; // Allow flush to proceed even if the stream is marked in use. + + if(_maxSize > 0 && _batchStream.size() >= _maxSize) + { + proxy.begin_ice_flushBatchRequests(); // Auto flush + } + + Debug.Assert(_batchMarker < _batchStream.size()); + if(_interceptor != null) + { + _request.reset(proxy, operation, _batchStream.size() - _batchMarker); + _interceptor.enqueue(_request, _batchRequestNum, _batchMarker); + } + else + { + _batchMarker = _batchStream.size(); + ++_batchRequestNum; + } + } + finally + { + lock(this) + { + _batchStream.resize(_batchMarker, false); + _batchStreamInUse = false; + _batchStreamCanFlush = false; + System.Threading.Monitor.PulseAll(this); + } + } + } + + public void + abortBatchRequest(BasicStream os) + { + lock(this) + { + if(_batchStreamInUse) + { + _batchStream.swap(os); + _batchStream.resize(_batchMarker, false); + _batchStreamInUse = false; + System.Threading.Monitor.PulseAll(this); + } + } + } + + public int + swap(BasicStream os) + { + lock(this) + { + if(_batchRequestNum == 0) + { + return 0; + } + + waitStreamInUse(true); + + byte[] lastRequest = null; + if(_batchMarker < _batchStream.size()) + { + lastRequest = new byte[_batchStream.size() - _batchMarker]; + Buffer buffer = _batchStream.getBuffer(); + buffer.b.position(_batchMarker); + buffer.b.get(lastRequest); + _batchStream.resize(_batchMarker, false); + } + + int requestNum = _batchRequestNum; + _batchStream.swap(os); + + // + // Reset the batch. + // + _batchRequestNum = 0; + _batchStream.writeBlob(Protocol.requestBatchHdr); + _batchMarker = _batchStream.size(); + if(lastRequest != null) + { + _batchStream.writeBlob(lastRequest); + } + return requestNum; + } + } + + public void + destroy(Ice.LocalException ex) + { + lock(this) + { + _exception = ex; + } + } + + public bool + isEmpty() + { + lock(this) + { + return _batchStream.size() == Protocol.requestBatchHdr.Length; + } + } + + private void + waitStreamInUse(bool flush) + { + // + // This is similar to a mutex lock in that the stream is + // only "locked" while marshaling. As such we don't permit the wait + // to be interrupted. Instead the interrupted status is saved and + // restored. + // + while(_batchStreamInUse && !(flush && _batchStreamCanFlush)) + { + System.Threading.Monitor.Wait(this); + } + } + + internal void enqueueBatchRequest() + { + Debug.Assert(_batchMarker < _batchStream.size()); + _batchMarker = _batchStream.size(); + ++_batchRequestNum; + } + + private Ice.BatchRequestInterceptor _interceptor; + private BasicStream _batchStream; + private bool _batchStreamInUse; + private bool _batchStreamCanFlush; + private int _batchRequestNum; + private int _batchMarker; + private BatchRequestI _request; + private Ice.LocalException _exception; + private int _maxSize; + + private static int _udpOverhead = 20 + 8; + } +}; diff --git a/csharp/src/Ice/Buffer.cs b/csharp/src/Ice/Buffer.cs new file mode 100644 index 00000000000..c0d1747a103 --- /dev/null +++ b/csharp/src/Ice/Buffer.cs @@ -0,0 +1,195 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Diagnostics; + + // + // An instance of ByteBuffer cannot grow beyond its initial capacity. + // This class wraps a ByteBuffer and supports reallocation. + // + public class Buffer + { + public Buffer() : this(ByteBuffer.ByteOrder.LITTLE_ENDIAN) + { + } + + public Buffer(ByteBuffer.ByteOrder order) + { + b = _emptyBuffer; + _size = 0; + _capacity = 0; + _order = order; + } + + public Buffer(byte[] data) : this(data, ByteBuffer.ByteOrder.LITTLE_ENDIAN) + { + } + + public Buffer(byte[] data, ByteBuffer.ByteOrder order) + { + b = ByteBuffer.wrap(data); + b.order(order); + _size = data.Length; + _capacity = 0; + _order = order; + } + + public int size() + { + return _size; + } + + public bool empty() + { + return _size == 0; + } + + public void clear() + { + b = _emptyBuffer; + _size = 0; + _capacity = 0; + } + + // + // Call expand(n) to add room for n additional bytes. Note that expand() + // examines the current position of the buffer first; we don't want to + // expand the buffer if the caller is writing to a location that is + // already in the buffer. + // + public void expand(int n) + { + int sz = (b == _emptyBuffer) ? n : b.position() + n; + if(sz > _size) + { + resize(sz, false); + } + } + + public void resize(int n, bool reading) + { + Debug.Assert(b == _emptyBuffer || _capacity > 0); + + if(n == 0) + { + clear(); + } + else if(n > _capacity) + { + reserve(n); + } + _size = n; + + // + // When used for reading, we want to set the buffer's limit to the new size. + // + if(reading) + { + b.limit(_size); + } + } + + public void reset() + { + if(_size > 0 && _size * 2 < _capacity) + { + // + // If the current buffer size is smaller than the + // buffer capacity, we shrink the buffer memory to the + // current size. This is to avoid holding on to too much + // memory if it's not needed anymore. + // + if(++_shrinkCounter > 2) + { + reserve(_size); + _shrinkCounter = 0; + } + } + else + { + _shrinkCounter = 0; + } + _size = 0; + if(b != _emptyBuffer) + { + b.limit(b.capacity()); + b.position(0); + } + } + + private void reserve(int n) + { + Debug.Assert(_capacity == b.capacity()); + + if(n > _capacity) + { + _capacity = System.Math.Max(n, 2 * _capacity); + _capacity = System.Math.Max(240, _capacity); + } + else if(n < _capacity) + { + _capacity = n; + } + else + { + return; + } + + try + { + ByteBuffer buf = ByteBuffer.allocate(_capacity); + + if(b == _emptyBuffer) + { + b = buf; + } + else + { + int pos = b.position(); + b.position(0); + b.limit(System.Math.Min(_capacity, b.capacity())); + buf.put(b); + b = buf; + b.limit(b.capacity()); + b.position(pos); + } + + b.order(_order); + } + catch(System.OutOfMemoryException) + { + _capacity = b.capacity(); // Restore the previous capacity + throw; + } + catch(System.Exception ex) + { + _capacity = b.capacity(); // Restore the previous capacity. + Ice.MarshalException e = new Ice.MarshalException(ex); + e.reason = "unexpected exception while trying to allocate a ByteBuffer:\n" + ex; + throw e; + } + finally + { + Debug.Assert(_capacity == b.capacity()); + } + } + + public ByteBuffer b; + // Sentinel used for null buffer. + static private ByteBuffer _emptyBuffer = new ByteBuffer(); + + private int _size; + private int _capacity; // Cache capacity to avoid excessive method calls. + private int _shrinkCounter; + private ByteBuffer.ByteOrder _order; + } + +} diff --git a/csharp/src/Ice/ByteBuffer.cs b/csharp/src/Ice/ByteBuffer.cs new file mode 100644 index 00000000000..a736c01679d --- /dev/null +++ b/csharp/src/Ice/ByteBuffer.cs @@ -0,0 +1,999 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Runtime.InteropServices; + +namespace IceInternal +{ + public class ByteBuffer + { + + public ByteBuffer() + { + _order = ByteOrder.BIG_ENDIAN; + } + + public enum ByteOrder { BIG_ENDIAN, LITTLE_ENDIAN }; + + public static ByteOrder nativeOrder() + { + return NO._o; + } + + public ByteOrder order() + { + return _order; + } + + public ByteBuffer order(ByteOrder bo) + { + _order = bo; + return this; + } + + public static ByteBuffer allocate(int capacity) + { + if(capacity < 0) + { + throwOutOfRange("capacity", capacity, "capacity must be non-negative"); + } + ByteBuffer ret = new ByteBuffer(); + ret._position = 0; + ret._limit = capacity; + ret._capacity = capacity; + ret._bytes = new byte[capacity]; + ret._valBytes = new ValBytes(); + return ret; + } + + public static ByteBuffer wrap(byte[] bytes) + { + ByteBuffer ret = new ByteBuffer(); + ret._position = 0; + ret._limit = bytes.Length; + ret._capacity = bytes.Length; + ret._bytes = bytes; + ret._valBytes = new ValBytes(); + return ret; + } + + public int position() + { + return _position; + } + + public ByteBuffer position(int pos) + { + if(pos < 0) + { + throwOutOfRange("pos", pos, "position must be non-negative"); + } + if(pos > _limit) + { + throwOutOfRange("pos", pos, "position must be less than limit"); + } + _position = pos; + return this; + } + + public int limit() + { + return _limit; + } + + public ByteBuffer limit(int newLimit) + { + if(newLimit < 0) + { + throwOutOfRange("newLimit", newLimit, "limit must be non-negative"); + } + if(newLimit > _capacity) + { + throwOutOfRange("newLimit", newLimit, "limit must be less than capacity"); + } + _limit = newLimit; + return this; + } + + public void clear() + { + _position = 0; + _limit = _capacity; + } + + public void flip() + { + _limit = _position; + _position = 0; + } + + public void compact() + { + if(_position < _limit) + { + int n = _limit - _position; + System.Buffer.BlockCopy(_bytes, _position, _bytes, 0, n); + _position = n; + } + else + { + _position = 0; + } + _limit = _capacity; + } + + public int remaining() + { + return _limit - _position; + } + + public bool hasRemaining() + { + return _position < _limit; + } + + public int capacity() + { + return _capacity; + } + + public byte[] toArray() + { + int len = remaining(); + byte[] rc = new byte[len]; + System.Buffer.BlockCopy(_bytes, 0, rc, 0, len); + return rc; + } + + public byte[] toArray(int startIndex, int length) + { + if(startIndex < 0) + { + throwOutOfRange("startIndex", startIndex, "startIndex must be non-negative"); + } + if(startIndex >= _position) + { + throwOutOfRange("startIndex", startIndex, "startIndex must be less than position"); + } + if(length < 0) + { + throwOutOfRange("length", length, "length must be non-negative"); + } + if(startIndex + length > _position) + { + throw new ArgumentException("startIndex + length must not exceed end mark of buffer"); + } + byte[] rc = new byte[length]; + System.Buffer.BlockCopy(_bytes, startIndex, rc, 0, length); + return rc; + } + + public ByteBuffer put(ByteBuffer buf) + { + int len = buf.remaining(); + checkOverflow(len); + System.Buffer.BlockCopy(buf._bytes, buf._position, _bytes, _position, len); + _position += len; + return this; + } + + public byte get() + { + checkUnderflow(1); + return System.Buffer.GetByte(_bytes, _position++); + } + + public byte get(int pos) + { + return System.Buffer.GetByte(_bytes, pos); + } + + public ByteBuffer get(byte[] b) + { + return get(b, 0, System.Buffer.ByteLength(b)); + } + + public ByteBuffer get(byte[] b, int offset, int length) + { + if(offset < 0) + { + throwOutOfRange("offset", offset, "offset must be non-negative"); + } + if(offset + length > System.Buffer.ByteLength(b)) + { + throwOutOfRange("length", length, "insufficient room beyond given offset in destination array"); + } + checkUnderflow(length); + System.Buffer.BlockCopy(_bytes, _position, b, offset, length); + _position += length; + return this; + } + + public ByteBuffer put(byte b) + { + checkOverflow(1); + System.Buffer.SetByte(_bytes, _position++, b); + return this; + } + + public ByteBuffer put(int pos, byte b) + { + System.Buffer.SetByte(_bytes, pos, b); + return this; + } + + public ByteBuffer put(byte[] b) + { + return put(b, 0, System.Buffer.ByteLength(b)); + } + + public ByteBuffer put(byte[]b, int offset, int length) + { + if(offset < 0) + { + throwOutOfRange("offset", offset, "offset must be non-negative"); + } + if(offset + length > System.Buffer.ByteLength(b)) + { + throwOutOfRange("length", length, "insufficient data beyond given offset in source array"); + } + if(length > 0) + { + checkOverflow(length); + System.Buffer.BlockCopy(b, offset, _bytes, _position, length); + _position += length; + } + return this; + } + + public bool getBool() + { + return get() == 1; + } + + public void getBoolSeq(bool[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkUnderflow(len); + System.Buffer.BlockCopy(_bytes, _position, seq, 0, len); + _position += len; + } + + public ByteBuffer putBool(bool b) + { + return put(b ? (byte)1 : (byte)0); + } + + public ByteBuffer putBoolSeq(bool[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkOverflow(len); + System.Buffer.BlockCopy(seq, 0, _bytes, _position, len); + _position += len; + return this; + } + + [StructLayout(LayoutKind.Explicit)] + struct ValBytes + { + [FieldOffset(0)] + public short shortVal; + + [FieldOffset(0)] + public int intVal; + + [FieldOffset(0)] + public long longVal; + + [FieldOffset(0)] + public float floatVal; + + [FieldOffset(0)] + public double doubleVal; + + [FieldOffset(0)] + public byte b0; + [FieldOffset(1)] + public byte b1; + [FieldOffset(2)] + public byte b2; + [FieldOffset(3)] + public byte b3; + [FieldOffset(4)] + public byte b4; + [FieldOffset(5)] + public byte b5; + [FieldOffset(6)] + public byte b6; + [FieldOffset(7)] + public byte b7; + } + + public short getShort() + { + short v = getShort(_position); + _position += 2; + return v; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public short getShort(int pos) + { + checkUnderflow(pos, 2); + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[pos]) + { + _valBytes.shortVal = *((short*)p); + } +#else + _valBytes.b0 = _bytes[pos]; + _valBytes.b1 = _bytes[pos + 1]; +#endif + } + else + { + _valBytes.b1 = _bytes[pos]; + _valBytes.b0 = _bytes[pos + 1]; + } + return _valBytes.shortVal; + } + + public void getShortSeq(short[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkUnderflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(_bytes, _position, seq, 0, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 2; + _valBytes.b1 = _bytes[index]; + _valBytes.b0 = _bytes[index + 1]; + seq[i] = _valBytes.shortVal; + } + } + _position += len; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public ByteBuffer putShort(short val) + { + checkOverflow(2); + _valBytes.shortVal = val; + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + *((short*)p) = _valBytes.shortVal; + } +#else + _bytes[_position] = _valBytes.b0; + _bytes[_position + 1] = _valBytes.b1; +#endif + } + else + { + _bytes[_position + 1] = _valBytes.b0; + _bytes[_position] = _valBytes.b1; + } + _position += 2; + return this; + } + + public ByteBuffer putShortSeq(short[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkOverflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(seq, 0, _bytes, _position, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 2; + _valBytes.shortVal = seq[i]; + _bytes[index + 1] = _valBytes.b0; + _bytes[index] = _valBytes.b1; + } + } + _position += len; + return this; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public int getInt() + { + checkUnderflow(4); + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + _valBytes.intVal = *((int*)p); + } +#else + _valBytes.b0 = _bytes[_position]; + _valBytes.b1 = _bytes[_position + 1]; + _valBytes.b2 = _bytes[_position + 2]; + _valBytes.b3 = _bytes[_position + 3]; +#endif + } + else + { + _valBytes.b3 = _bytes[_position]; + _valBytes.b2 = _bytes[_position + 1]; + _valBytes.b1 = _bytes[_position + 2]; + _valBytes.b0 = _bytes[_position + 3]; + } + _position += 4; + return _valBytes.intVal; + } + + public void getIntSeq(int[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkUnderflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(_bytes, _position, seq, 0, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 4; + _valBytes.b3 = _bytes[index]; + _valBytes.b2 = _bytes[index + 1]; + _valBytes.b1 = _bytes[index + 2]; + _valBytes.b0 = _bytes[index + 3]; + seq[i] = _valBytes.intVal; + } + } + _position += len; + } + + public ByteBuffer putInt(int val) + { + putInt(_position, val); + _position += 4; + return this; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public ByteBuffer putInt(int pos, int val) + { + if(pos < 0) + { + throwOutOfRange("pos", pos, "position must be non-negative"); + } + if(pos + 4 > _limit) + { + throwOutOfRange("pos", pos, "position must be less than limit - 4"); + } + _valBytes.intVal = val; + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[pos]) + { + *((int*)p) = _valBytes.intVal; + } +#else + _bytes[pos] = _valBytes.b0; + _bytes[pos + 1] = _valBytes.b1; + _bytes[pos + 2] = _valBytes.b2; + _bytes[pos + 3] = _valBytes.b3; +#endif + } + else + { + _bytes[pos + 3] = _valBytes.b0; + _bytes[pos + 2] = _valBytes.b1; + _bytes[pos + 1] = _valBytes.b2; + _bytes[pos] = _valBytes.b3; + } + return this; + } + + public ByteBuffer putIntSeq(int[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkOverflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(seq, 0, _bytes, _position, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 4; + _valBytes.intVal = seq[i]; + _bytes[index + 3] = _valBytes.b0; + _bytes[index + 2] = _valBytes.b1; + _bytes[index + 1] = _valBytes.b2; + _bytes[index] = _valBytes.b3; + } + } + _position += len; + return this; + } + + public long getLong() + { + long v = getLong(_position); + _position += 8; + return v; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public long getLong(int pos) + { + checkUnderflow(pos, 8); + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[pos]) + { + _valBytes.longVal = *((long*)p); + } +#else + _valBytes.b0 = _bytes[pos]; + _valBytes.b1 = _bytes[pos + 1]; + _valBytes.b2 = _bytes[pos + 2]; + _valBytes.b3 = _bytes[pos + 3]; + _valBytes.b4 = _bytes[pos + 4]; + _valBytes.b5 = _bytes[pos + 5]; + _valBytes.b6 = _bytes[pos + 6]; + _valBytes.b7 = _bytes[pos + 7]; +#endif + } + else + { + _valBytes.b7 = _bytes[pos]; + _valBytes.b6 = _bytes[pos + 1]; + _valBytes.b5 = _bytes[pos + 2]; + _valBytes.b4 = _bytes[pos + 3]; + _valBytes.b3 = _bytes[pos + 4]; + _valBytes.b2 = _bytes[pos + 5]; + _valBytes.b1 = _bytes[pos + 6]; + _valBytes.b0 = _bytes[pos + 7]; + } + return _valBytes.longVal; + } + + public void getLongSeq(long[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkUnderflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(_bytes, _position, seq, 0, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 8; + _valBytes.b7 = _bytes[index]; + _valBytes.b6 = _bytes[index + 1]; + _valBytes.b5 = _bytes[index + 2]; + _valBytes.b4 = _bytes[index + 3]; + _valBytes.b3 = _bytes[index + 4]; + _valBytes.b2 = _bytes[index + 5]; + _valBytes.b1 = _bytes[index + 6]; + _valBytes.b0 = _bytes[index + 7]; + seq[i] = _valBytes.longVal; + } + } + _position += len; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public ByteBuffer putLong(long val) + { + checkOverflow(8); + _valBytes.longVal = val; + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + *((long*)p) = _valBytes.longVal; + } +#else + _bytes[_position] = _valBytes.b0; + _bytes[_position + 1] = _valBytes.b1; + _bytes[_position + 2] = _valBytes.b2; + _bytes[_position + 3] = _valBytes.b3; + _bytes[_position + 4] = _valBytes.b4; + _bytes[_position + 5] = _valBytes.b5; + _bytes[_position + 6] = _valBytes.b6; + _bytes[_position + 7] = _valBytes.b7; +#endif + } + else + { + _bytes[_position + 7] = _valBytes.b0; + _bytes[_position + 6] = _valBytes.b1; + _bytes[_position + 5] = _valBytes.b2; + _bytes[_position + 4] = _valBytes.b3; + _bytes[_position + 3] = _valBytes.b4; + _bytes[_position + 2] = _valBytes.b5; + _bytes[_position + 1] = _valBytes.b6; + _bytes[_position] = _valBytes.b7; + } + _position += 8; + return this; + } + + public ByteBuffer putLongSeq(long[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkOverflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(seq, 0, _bytes, _position, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 8; + _valBytes.longVal = seq[i]; + _bytes[index + 7] = _valBytes.b0; + _bytes[index + 6] = _valBytes.b1; + _bytes[index + 5] = _valBytes.b2; + _bytes[index + 4] = _valBytes.b3; + _bytes[index + 3] = _valBytes.b4; + _bytes[index + 2] = _valBytes.b5; + _bytes[index + 1] = _valBytes.b6; + _bytes[index] = _valBytes.b7; + } + } + _position += len; + return this; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public float getFloat() + { + checkUnderflow(4); + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + _valBytes.floatVal = *((float*)p); + } +#else + _valBytes.b0 = _bytes[_position]; + _valBytes.b1 = _bytes[_position + 1]; + _valBytes.b2 = _bytes[_position + 2]; + _valBytes.b3 = _bytes[_position + 3]; +#endif + } + else + { + _valBytes.b3 = _bytes[_position]; + _valBytes.b2 = _bytes[_position + 1]; + _valBytes.b1 = _bytes[_position + 2]; + _valBytes.b0 = _bytes[_position + 3]; + } + _position += 4; + return _valBytes.floatVal; + } + + public void getFloatSeq(float[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkUnderflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(_bytes, _position, seq, 0, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 4; + _valBytes.b3 = _bytes[index]; + _valBytes.b2 = _bytes[index + 1]; + _valBytes.b1 = _bytes[index + 2]; + _valBytes.b0 = _bytes[index + 3]; + seq[i] = _valBytes.floatVal; + } + } + _position += len; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public ByteBuffer putFloat(float val) + { + checkOverflow(4); + _valBytes.floatVal = val; + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + *((float*)p) = _valBytes.floatVal; + } +#else + _bytes[_position] = _valBytes.b0; + _bytes[_position + 1] = _valBytes.b1; + _bytes[_position + 2] = _valBytes.b2; + _bytes[_position + 3] = _valBytes.b3; +#endif + } + else + { + _bytes[_position + 3] = _valBytes.b0; + _bytes[_position + 2] = _valBytes.b1; + _bytes[_position + 1] = _valBytes.b2; + _bytes[_position] = _valBytes.b3; + } + _position += 4; + return this; + } + + public ByteBuffer putFloatSeq(float[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkOverflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(seq, 0, _bytes, _position, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 4; + _valBytes.floatVal = seq[i]; + _bytes[index + 3] = _valBytes.b0; + _bytes[index + 2] = _valBytes.b1; + _bytes[index + 1] = _valBytes.b2; + _bytes[index] = _valBytes.b3; + } + } + _position += len; + return this; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public double getDouble() + { + checkUnderflow(8); + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + _valBytes.doubleVal = *((double*)p); + } +#else + _valBytes.b0 = _bytes[_position]; + _valBytes.b1 = _bytes[_position + 1]; + _valBytes.b2 = _bytes[_position + 2]; + _valBytes.b3 = _bytes[_position + 3]; + _valBytes.b4 = _bytes[_position + 4]; + _valBytes.b5 = _bytes[_position + 5]; + _valBytes.b6 = _bytes[_position + 6]; + _valBytes.b7 = _bytes[_position + 7]; +#endif + } + else + { + _valBytes.b7 = _bytes[_position]; + _valBytes.b6 = _bytes[_position + 1]; + _valBytes.b5 = _bytes[_position + 2]; + _valBytes.b4 = _bytes[_position + 3]; + _valBytes.b3 = _bytes[_position + 4]; + _valBytes.b2 = _bytes[_position + 5]; + _valBytes.b1 = _bytes[_position + 6]; + _valBytes.b0 = _bytes[_position + 7]; + } + _position += 8; + return _valBytes.doubleVal; + } + + public void getDoubleSeq(double[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkUnderflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(_bytes, _position, seq, 0, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 8; + _valBytes.b7 = _bytes[index]; + _valBytes.b6 = _bytes[index + 1]; + _valBytes.b5 = _bytes[index + 2]; + _valBytes.b4 = _bytes[index + 3]; + _valBytes.b3 = _bytes[index + 4]; + _valBytes.b2 = _bytes[index + 5]; + _valBytes.b1 = _bytes[index + 6]; + _valBytes.b0 = _bytes[index + 7]; + seq[i] = _valBytes.doubleVal; + } + } + _position += len; + } + +#if !MANAGED && !COMPACT && !SILVERLIGHT + unsafe +#endif + public ByteBuffer putDouble(double val) + { + checkOverflow(8); + _valBytes.doubleVal = val; + if(NO._o == _order) + { +#if !MANAGED && !COMPACT && !SILVERLIGHT + fixed(byte* p = &_bytes[_position]) + { + *((double*)p) = _valBytes.doubleVal; + } +#else + _bytes[_position] = _valBytes.b0; + _bytes[_position + 1] = _valBytes.b1; + _bytes[_position + 2] = _valBytes.b2; + _bytes[_position + 3] = _valBytes.b3; + _bytes[_position + 4] = _valBytes.b4; + _bytes[_position + 5] = _valBytes.b5; + _bytes[_position + 6] = _valBytes.b6; + _bytes[_position + 7] = _valBytes.b7; +#endif + } + else + { + _bytes[_position + 7] = _valBytes.b0; + _bytes[_position + 6] = _valBytes.b1; + _bytes[_position + 5] = _valBytes.b2; + _bytes[_position + 4] = _valBytes.b3; + _bytes[_position + 3] = _valBytes.b4; + _bytes[_position + 2] = _valBytes.b5; + _bytes[_position + 1] = _valBytes.b6; + _bytes[_position] = _valBytes.b7; + } + _position += 8; + return this; + } + + public ByteBuffer putDoubleSeq(double[] seq) + { + int len = System.Buffer.ByteLength(seq); + checkOverflow(len); + if(NO._o == _order) + { + System.Buffer.BlockCopy(seq, 0, _bytes, _position, len); + } + else + { + for(int i = 0; i < seq.Length; ++i) + { + int index = _position + i * 8; + _valBytes.doubleVal = seq[i]; + _bytes[index + 7] = _valBytes.b0; + _bytes[index + 6] = _valBytes.b1; + _bytes[index + 5] = _valBytes.b2; + _bytes[index + 4] = _valBytes.b3; + _bytes[index + 3] = _valBytes.b4; + _bytes[index + 2] = _valBytes.b5; + _bytes[index + 1] = _valBytes.b6; + _bytes[index] = _valBytes.b7; + } + } + _position += len; + return this; + } + + public byte[] rawBytes() + { + return _bytes; + } + + public byte[] rawBytes(int offset, int len) + { + if(offset + len > _limit) + { + throw new InvalidOperationException("buffer underflow"); + } + byte[] rc = new byte[len]; + Array.Copy(_bytes, offset, rc, 0, len); + return rc; + } + + private void checkUnderflow(int size) + { + if(_position + size > _limit) + { + throw new InvalidOperationException("buffer underflow"); + } + } + + private void checkUnderflow(int pos, int size) + { + if(pos + size > _limit) + { + throw new InvalidOperationException("buffer underflow"); + } + } + + private void checkOverflow(int size) + { + if(_position + size > _limit) + { + throw new InvalidOperationException("buffer overflow"); + } + } + + private int _position; + private int _limit; + private int _capacity; + private byte[] _bytes; + private ValBytes _valBytes; + private ByteOrder _order; + + private class NO // Native Order + { + static NO() + { + _o = BitConverter.IsLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; + } + internal static readonly ByteOrder _o; + + private NO() + { + } + } + + private static void throwOutOfRange(string param, object value, string message) + { +#if COMPACT || SILVERLIGHT + throw new ArgumentOutOfRangeException(param, message); +#else + throw new ArgumentOutOfRangeException(param, value, message); +#endif + } + } +} diff --git a/csharp/src/Ice/CollectionBase.cs b/csharp/src/Ice/CollectionBase.cs new file mode 100644 index 00000000000..3c505f58ee6 --- /dev/null +++ b/csharp/src/Ice/CollectionBase.cs @@ -0,0 +1,450 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections.Generic; + +namespace IceInternal +{ +#if !SILVERLIGHT + [Serializable] +#endif + public abstract class CollectionBase<T> : System.Collections.IList + { + protected List<T> list_; + + public CollectionBase() + { + list_ = new List<T>(); + } + + public CollectionBase(int capacity) + { + list_ = new List<T>(capacity); + } + + public CollectionBase(T[] a) + { + if(a == null) + { + throw new ArgumentNullException("a", "Cannot construct collection from null array"); + } + + list_ = new List<T>(a.Length); + list_.AddRange(a); + } + + public CollectionBase(IEnumerable<T> l) + { + if(l == null) + { + throw new ArgumentNullException("l", "Cannot construct collection from null collection"); + } + + list_ = new List<T>(); + list_.AddRange(l); + } + + public static implicit operator List<T>(CollectionBase<T> l) + { + return l.list_; + } + + + public void CopyTo(T[] a__) + { + list_.CopyTo(a__); + } + + public void CopyTo(T[] a__, int i__) + { + list_.CopyTo(a__, i__); + } + + public void CopyTo(int i__, T[] a__, int ai__, int _c_) + { + list_.CopyTo(i__, a__, ai__, _c_); + } + + public T[] ToArray() + { + return list_.ToArray(); + } + + public virtual void TrimToSize() + { + list_.TrimExcess(); + } + + public virtual void Sort() + { + list_.Sort(); + } + + public virtual void Sort(System.Collections.IComparer comparer) + { + list_.Sort(new Comparer(comparer)); + } + + public virtual void Sort(int index, int count, System.Collections.IComparer comparer) + { + list_.Sort(index, count, new Comparer(comparer)); + } + + public virtual void Reverse() + { + list_.Reverse(); + } + + public virtual void Reverse(int index, int count) + { + list_.Reverse(index, count); + } + + public virtual int BinarySearch(T value) + { + return list_.BinarySearch(value); + } + + public virtual int BinarySearch(T value, System.Collections.IComparer comparer) + { + return list_.BinarySearch(value, new Comparer(comparer)); + } + + public virtual int BinarySearch(int index, int count, T value, System.Collections.IComparer comparer) + { + return list_.BinarySearch(index, count, value, new Comparer(comparer)); + } + + public virtual void InsertRange(int index, CollectionBase<T> c) + { + list_.InsertRange(index, c.list_); + } + + public virtual void InsertRange(int index, T[] c) + { + list_.InsertRange(index, c); + } + + public virtual void RemoveRange(int index, int count) + { + list_.RemoveRange(index, count); + } + + public virtual void SetRange(int index, CollectionBase<T> c) + { + if(c == null) + { + throw new ArgumentNullException("c", "Collection must not be null for SetRange()"); + } + if(index < 0 || index + c.Count > list_.Count) + { + throw new ArgumentOutOfRangeException("index", "Index out of range"); + } + for(int i = index; i < list_.Count; ++i) + { + list_[i] = c[i - index]; + } + } + + public virtual void SetRange(int index, T[] c) + { + if(c == null) + { + throw new ArgumentNullException("c", "Collection must not be null for SetRange()"); + } + if(index < 0 || index + c.Length > list_.Count) + { + throw new ArgumentOutOfRangeException("index", "Index out of range"); + } + for(int i = index; i < list_.Count; ++i) + { + list_[i] = c[i - index]; + } + } + + public virtual int LastIndexOf(T value) + { + return list_.LastIndexOf(value); + } + + public virtual int LastIndexOf(T value, int startIndex) + { + return list_.LastIndexOf(value, startIndex); + } + + public virtual int LastIndexOf(T value, int startIndex, int count) + { + return list_.LastIndexOf(value, startIndex, count); + } + + public void AddRange(CollectionBase<T> s__) + { + list_.AddRange(s__.list_); + } + + public void AddRange(T[] a__) + { + list_.AddRange(a__); + } + + public int Capacity + { + get + { + return list_.Capacity; + } + + set + { + list_.Capacity = value; + } + } + + public int Count + { + get + { + return list_.Count; + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return list_.GetEnumerator(); + } + + public IEnumerator<T> GetEnumerator() + { + return list_.GetEnumerator(); + } + + public void RemoveAt(int index) + { + list_.RemoveAt(index); + } + + public int Add(T value) + { + list_.Add(value); + return list_.Count - 1; + } + + public void Clear() + { + list_.Clear(); + } + + public bool IsFixedSize + { + get + { + return false; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public int IndexOf(T value) + { + return list_.IndexOf(value); + } + + public void Insert(int index, T value) + { + list_.Insert(index, value); + } + + public void Remove(T value) + { + list_.Remove(value); + } + + public bool Contains(T value) + { + return list_.Contains(value); + } + + public bool IsSynchronized + { + get + { + return false; + } + } + + public object SyncRoot + { + get + { + return this; + } + } + + public T this[int index] + { + get + { + return list_[index]; + } + set + { + list_[index] = value; + } + } + + public override int GetHashCode() + { + int h = 5381; + for(int i = 0; i < Count; ++i) + { + T v__ = list_[i]; + IceInternal.HashUtil.hashAdd(ref h, v__); + } + return h; + } + + public override bool Equals(object other) + { + if(object.ReferenceEquals(this, other)) + { + return true; + } + try + { + CollectionBase<T> c = (CollectionBase<T>)other; + if(list_.Count != c.list_.Count) + { + return false; + } + if(list_.Count == 0) + { + return true; + } + for(int i = 0; i < list_.Count; ++i) + { + if(!Equals(list_[i], c.list_[i])) + { + return false; + } + } + } + catch(System.Exception) + { + return false; + } + + return true; + } + + public static bool operator==(CollectionBase<T> lhs__, CollectionBase<T> rhs__) + { + return Equals(lhs__, rhs__); + } + + public static bool operator!=(CollectionBase<T> lhs__, CollectionBase<T> rhs__) + { + return !Equals(lhs__, rhs__); + } + + private class Comparer : IComparer<T> + { + private System.Collections.IComparer _c; + + public Comparer(System.Collections.IComparer c) + { + _c = c; + } + + public virtual int Compare(T l, T r) + { + return _c.Compare(l, r); + } + } + + public int Add(object o) + { + checkType(o); + return Add((T)o); + } + + public bool Contains(object o) + { + checkType(o); + return Contains((T)o); + } + + public int IndexOf(object o) + { + checkType(o); + return IndexOf((T)o); + } + + public void Insert(int i, object o) + { + checkType(o); + Insert(i, (T)o); + } + + public void Remove(object o) + { + checkType(o); + Remove((T)o); + } + + object System.Collections.IList.this[int index] + { + get + { + return this[index]; + } + set + { + checkType(value); + this[index] = (T)value; + } + } + + public void CopyTo(Array a, int index) + { + Type t = a.GetType().GetElementType(); + if(!t.IsAssignableFrom(typeof(T))) + { + throw new ArgumentException("a__", "Cannot assign " + typeof(T).ToString() + " to array of " + + t.ToString()); + } + CopyTo((T[])a, index); + } + + private void checkType(object o) + { + + if(o != null && !(o is T)) + { + throw new ArgumentException("Cannot use an object of type " + o.GetType().ToString() + + " with a collection of " + typeof(T).ToString()); + } + } + } + +} + +namespace Ice +{ + [Obsolete("This class is deprecated.")] + public abstract class CollectionBase<T> : IceInternal.CollectionBase<T> + { + } +} diff --git a/csharp/src/Ice/Collections.cs b/csharp/src/Ice/Collections.cs new file mode 100644 index 00000000000..65db2114791 --- /dev/null +++ b/csharp/src/Ice/Collections.cs @@ -0,0 +1,239 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace IceUtilInternal +{ + public sealed class Collections + { + public static bool SequenceEquals(ICollection seq1, ICollection seq2) + { + if(object.ReferenceEquals(seq1, seq2)) + { + return true; + } + + if((seq1 == null && seq2 != null) || (seq1 != null && seq2 == null)) + { + return false; + } + + if(seq1.Count == seq2.Count) + { + IEnumerator e1 = seq1.GetEnumerator(); + IEnumerator e2 = seq2.GetEnumerator(); + while(e1.MoveNext()) + { + e2.MoveNext(); + if(e1.Current == null) + { + if(e2.Current != null) + { + return false; + } + } + else if(!e1.Current.Equals(e2.Current)) + { + return false; + } + } + + return true; + } + + return false; + } + + public static bool SequenceEquals(IEnumerable seq1, IEnumerable seq2) + { + if(object.ReferenceEquals(seq1, seq2)) + { + return true; + } + + if((seq1 == null && seq2 != null) || (seq1 != null && seq2 == null)) + { + return false; + } + + IEnumerator e1 = seq1.GetEnumerator(); + IEnumerator e2 = seq2.GetEnumerator(); + while(e1.MoveNext()) + { + if(!e2.MoveNext()) + { + return false; + } + if(e1.Current == null) + { + if(e2.Current != null) + { + return false; + } + } + else if(!e1.Current.Equals(e2.Current)) + { + return false; + } + } + + if(e2.MoveNext()) + { + return false; + } + + return true; + } + + public static int SequenceGetHashCode(IEnumerable seq) + { + int h = 5381; + IEnumerator e = seq.GetEnumerator(); + while(e.MoveNext()) + { + IceInternal.HashUtil.hashAdd(ref h, e.Current); + } + return h; + } + + public static bool DictionaryEquals(IDictionary d1, IDictionary d2) + { + if(object.ReferenceEquals(d1, d2)) + { + return true; + } + + if((d1 == null && d2 != null) || (d1 != null && d2 == null)) + { + return false; + } + + if(d1.Count == d2.Count) + { + IDictionaryEnumerator e1 = d1.GetEnumerator(); + IDictionaryEnumerator e2 = d2.GetEnumerator(); + while(e1.MoveNext()) + { + e2.MoveNext(); + if(!e1.Key.Equals(e2.Key)) + { + return false; + } + if(e1.Value == null) + { + if(e2.Value != null) + { + return false; + } + } + else if(!e1.Value.Equals(e2.Value)) + { + return false; + } + } + + return true; + } + + return false; + } + + public static int DictionaryGetHashCode(IDictionary d) + { + int h = 5381; + IDictionaryEnumerator e = d.GetEnumerator(); + while(e.MoveNext()) + { + IceInternal.HashUtil.hashAdd(ref h, e.Key); + IceInternal.HashUtil.hashAdd(ref h, e.Value); + } + return h; + } + + public static void Shuffle<T>(ref List<T> l) + { + lock(rand_) + { + for(int j = 0; j < l.Count - 1; ++j) + { + int r = rand_.Next(l.Count - j) + j; + Debug.Assert(r >= j && r < l.Count); + if(r != j) + { + T tmp = l[j]; + l[j] = l[r]; + l[r] = tmp; + } + } + } + } + + public static void Sort<T>(ref List<T> array, IComparer<T> comparator) + { + // + // This Sort method implements the merge sort algorithm + // which is a stable sort (unlike the Sort method of the + // System.Collections.ArrayList which is unstable). + // + Sort1(ref array, 0, array.Count, comparator); + } + + private static void Sort1<T>(ref List<T> array, int begin, int end, IComparer<T> comparator) + { + int mid; + if(end - begin <= 1) + { + return; + } + + mid = (begin + end) / 2; + Sort1(ref array, begin, mid, comparator); + Sort1(ref array, mid, end, comparator); + Merge(ref array, begin, mid, end, comparator); + } + + private static void Merge<T>(ref List<T> array, int begin, int mid, int end, IComparer<T> comparator) + { + int i = begin; + int j = mid; + int k = 0; + + T[] tmp = new T[end - begin]; + while(i < mid && j < end) + { + if(comparator.Compare(array[i], array[j]) <= 0) + { + tmp[k++] = array[i++]; + } + else + { + tmp[k++] = array[j++]; + } + } + + while(i < mid) + { + tmp[k++] = array[i++]; + } + while(j < end) + { + tmp[k++] = array[j++]; + } + for(i = 0; i < (end - begin); ++i) + { + array[begin + i] = tmp[i]; + } + } + + private static System.Random rand_ = new System.Random(unchecked((int)System.DateTime.Now.Ticks)); + } +} diff --git a/csharp/src/Ice/CollocatedRequestHandler.cs b/csharp/src/Ice/CollocatedRequestHandler.cs new file mode 100644 index 00000000000..4fcd670003c --- /dev/null +++ b/csharp/src/Ice/CollocatedRequestHandler.cs @@ -0,0 +1,346 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using Ice.Instrumentation; + +namespace IceInternal +{ + public class CollocatedRequestHandler : RequestHandler, ResponseHandler + { + private void + fillInValue(BasicStream os, int pos, int value) + { + os.rewriteInt(value, pos); + } + + public + CollocatedRequestHandler(Reference @ref, Ice.ObjectAdapter adapter) + { + _reference = @ref; + _dispatcher = _reference.getInstance().initializationData().dispatcher != null; + _response = _reference.getMode() == Reference.Mode.ModeTwoway; + _adapter = (Ice.ObjectAdapterI)adapter; + + _logger = _reference.getInstance().initializationData().logger; // Cached for better performance. + _traceLevels = _reference.getInstance().traceLevels(); // Cached for better performance. + _requestId = 0; + } + + public RequestHandler update(RequestHandler previousHandler, RequestHandler newHandler) + { + return previousHandler == this ? newHandler : this; + } + + public bool sendAsyncRequest(ProxyOutgoingAsyncBase outAsync, out Ice.AsyncCallback sentCallback) + { + return outAsync.invokeCollocated(this, out sentCallback); + } + + public void asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex) + { + lock(this) + { + int requestId; + if(_sendAsyncRequests.TryGetValue(outAsync, out requestId)) + { + if(requestId > 0) + { + _asyncRequests.Remove(requestId); + } + _sendAsyncRequests.Remove(outAsync); + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + return; + } + if(outAsync is OutgoingAsync) + { + OutgoingAsync o = (OutgoingAsync)outAsync; + Debug.Assert(o != null); + foreach(KeyValuePair<int, OutgoingAsyncBase> e in _asyncRequests) + { + if(e.Value == o) + { + _asyncRequests.Remove(e.Key); + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + return; + } + } + } + } + } + + public void sendResponse(int requestId, BasicStream os, byte status, bool amd) + { + Ice.AsyncCallback cb = null; + OutgoingAsyncBase outAsync; + lock(this) + { + Debug.Assert(_response); + + os.pos(Protocol.replyHdr.Length + 4); + + if(_traceLevels.protocol >= 1) + { + fillInValue(os, 10, os.size()); + TraceUtil.traceRecv(os, _logger, _traceLevels); + } + + if(_asyncRequests.TryGetValue(requestId, out outAsync)) + { + _asyncRequests.Remove(requestId); + outAsync.getIs().swap(os); + cb = outAsync.completed(); + } + } + + if(cb != null) + { + if(amd) + { + outAsync.invokeCompletedAsync(cb); + } + else + { + outAsync.invokeCompleted(cb); + } + } + _adapter.decDirectCount(); + } + + public void + sendNoResponse() + { + _adapter.decDirectCount(); + } + + public bool + systemException(int requestId, Ice.SystemException ex, bool amd) + { + handleException(requestId, ex, amd); + _adapter.decDirectCount(); + return true; + } + + public void + invokeException(int requestId, Ice.LocalException ex, int invokeNum, bool amd) + { + handleException(requestId, ex, amd); + _adapter.decDirectCount(); + } + + public Reference + getReference() + { + return _reference; + } + + public Ice.ConnectionI + getConnection() + { + return null; + } + + public bool invokeAsyncRequest(OutgoingAsyncBase outAsync, int batchRequestNum, bool synchronous, + out Ice.AsyncCallback sentCallback) + { + int requestId = 0; + { + lock(this) + { + outAsync.cancelable(this); // This will throw if the request is canceled + + if(_response) + { + requestId = ++_requestId; + _asyncRequests.Add(requestId, outAsync); + } + + _sendAsyncRequests.Add(outAsync, requestId); + } + } + + outAsync.attachCollocatedObserver(_adapter, requestId); + + if(synchronous) + { + // + // Treat this collocated call as if it is a synchronous invocation. + // + if(_reference.getInvocationTimeout() > 0 || !_response) + { + // Don't invoke from the user thread, invocation timeouts wouldn't work otherwise. + _adapter.getThreadPool().dispatch(() => + { + if(sentAsync(outAsync)) + { + invokeAll(outAsync.getOs(), requestId, batchRequestNum); + } + }, null); + } + else if(_dispatcher) + { + _adapter.getThreadPool().dispatchFromThisThread(() => + { + if(sentAsync(outAsync)) + { + invokeAll(outAsync.getOs(), requestId, batchRequestNum); + } + }, null); + } + else // Optimization: directly call invokeAll if there's no dispatcher. + { + if(sentAsync(outAsync)) + { + invokeAll(outAsync.getOs(), requestId, batchRequestNum); + } + } + sentCallback = null; + } + else + { + _adapter.getThreadPool().dispatch(() => + { + if(sentAsync(outAsync)) + { + invokeAll(outAsync.getOs(), requestId, batchRequestNum); + } + }, null); + sentCallback = null; + } + return false; + } + + private bool sentAsync(OutgoingAsyncBase outAsync) + { + lock(this) + { + if(!_sendAsyncRequests.Remove(outAsync)) + { + return false; // The request timed-out. + } + } + + Ice.AsyncCallback cb = outAsync.sent(); + if(cb != null) + { + outAsync.invokeSent(cb); + } + return true; + } + + private void invokeAll(BasicStream os, int requestId, int batchRequestNum) + { + if(batchRequestNum > 0) + { + os.pos(Protocol.requestBatchHdr.Length); + } + else + { + os.pos(Protocol.requestHdr.Length); + } + + if(_traceLevels.protocol >= 1) + { + fillInValue(os, 10, os.size()); + if(requestId > 0) + { + fillInValue(os, Protocol.headerSize, requestId); + } + else if(batchRequestNum > 0) + { + fillInValue(os, Protocol.headerSize, batchRequestNum); + } + TraceUtil.traceSend(os, _logger, _traceLevels); + } + + int invokeNum = batchRequestNum > 0 ? batchRequestNum : 1; + ServantManager servantManager = _adapter.getServantManager(); + try + { + while(invokeNum > 0) + { + try + { + _adapter.incDirectCount(); + } + catch(Ice.ObjectAdapterDeactivatedException ex) + { + handleException(requestId, ex, false); + return; + } + + Incoming @in = new Incoming(_reference.getInstance(), this, null, _adapter, _response, (byte)0, + requestId); + @in.invoke(servantManager, os); + --invokeNum; + } + } + catch(Ice.LocalException ex) + { + invokeException(requestId, ex, invokeNum, false); // Fatal invocation exception + } + } + + void + handleException(int requestId, Ice.Exception ex, bool amd) + { + if(requestId == 0) + { + return; // Ignore exception for oneway messages. + } + + OutgoingAsyncBase outAsync; + Ice.AsyncCallback cb = null; + lock(this) + { + if(_asyncRequests.TryGetValue(requestId, out outAsync)) + { + _asyncRequests.Remove(requestId); + cb = outAsync.completed(ex); + } + } + + if(cb != null) + { + if(amd) + { + outAsync.invokeCompletedAsync(cb); + } + else + { + outAsync.invokeCompleted(cb); + } + } + } + + private readonly Reference _reference; + private readonly bool _dispatcher; + private readonly bool _response; + private readonly Ice.ObjectAdapterI _adapter; + private readonly Ice.Logger _logger; + private readonly TraceLevels _traceLevels; + + private int _requestId; + + private Dictionary<OutgoingAsyncBase, int> _sendAsyncRequests = new Dictionary<OutgoingAsyncBase, int>(); + private Dictionary<int, OutgoingAsyncBase> _asyncRequests = new Dictionary<int, OutgoingAsyncBase>(); + } +} diff --git a/csharp/src/Ice/CommunicatorI.cs b/csharp/src/Ice/CommunicatorI.cs new file mode 100644 index 00000000000..915709fb641 --- /dev/null +++ b/csharp/src/Ice/CommunicatorI.cs @@ -0,0 +1,294 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections.Generic; + +namespace Ice +{ + + sealed class CommunicatorI : Communicator + { + public void destroy() + { + instance_.destroy(); + } + + public void shutdown() + { + instance_.objectAdapterFactory().shutdown(); + } + + public void waitForShutdown() + { + instance_.objectAdapterFactory().waitForShutdown(); + } + + public bool isShutdown() + { + return instance_.objectAdapterFactory().isShutdown(); + } + + public Ice.ObjectPrx stringToProxy(string s) + { + return instance_.proxyFactory().stringToProxy(s); + } + + public string proxyToString(Ice.ObjectPrx proxy) + { + return instance_.proxyFactory().proxyToString(proxy); + } + + public Ice.ObjectPrx propertyToProxy(string s) + { + return instance_.proxyFactory().propertyToProxy(s); + } + + public Dictionary<string, string> proxyToProperty(Ice.ObjectPrx proxy, string prefix) + { + return instance_.proxyFactory().proxyToProperty(proxy, prefix); + } + + public Ice.Identity stringToIdentity(string s) + { + return instance_.stringToIdentity(s); + } + + public string identityToString(Ice.Identity ident) + { + return instance_.identityToString(ident); + } + + public ObjectAdapter createObjectAdapter(string name) + { + return instance_.objectAdapterFactory().createObjectAdapter(name, null); + } + + public ObjectAdapter createObjectAdapterWithEndpoints(string name, string endpoints) + { + if(name.Length == 0) + { + name = System.Guid.NewGuid().ToString(); + } + + getProperties().setProperty(name + ".Endpoints", endpoints); + return instance_.objectAdapterFactory().createObjectAdapter(name, null); + } + + public ObjectAdapter createObjectAdapterWithRouter(string name, RouterPrx router) + { + if(name.Length == 0) + { + name = System.Guid.NewGuid().ToString(); + } + + // + // We set the proxy properties here, although we still use the proxy supplied. + // + Dictionary<string, string> properties = proxyToProperty(router, name + ".Router"); + foreach(KeyValuePair<string, string> entry in properties) + { + getProperties().setProperty(entry.Key, entry.Value); + } + + return instance_.objectAdapterFactory().createObjectAdapter(name, router); + } + + public void addObjectFactory(ObjectFactory factory, string id) + { + instance_.servantFactoryManager().add(factory, id); + } + + public ObjectFactory findObjectFactory(string id) + { + return instance_.servantFactoryManager().find(id); + } + + public Properties getProperties() + { + return instance_.initializationData().properties; + } + + public Logger getLogger() + { + return instance_.initializationData().logger; + } + + public Ice.Instrumentation.CommunicatorObserver getObserver() + { + return instance_.initializationData().observer; + } + + public RouterPrx getDefaultRouter() + { + return instance_.referenceFactory().getDefaultRouter(); + } + + public void setDefaultRouter(RouterPrx router) + { + instance_.setDefaultRouter(router); + } + + public LocatorPrx getDefaultLocator() + { + return instance_.referenceFactory().getDefaultLocator(); + } + + public void setDefaultLocator(LocatorPrx locator) + { + instance_.setDefaultLocator(locator); + } + + public ImplicitContext getImplicitContext() + { + return instance_.getImplicitContext(); + } + + public PluginManager getPluginManager() + { + return instance_.pluginManager(); + } + + public void flushBatchRequests() + { + AsyncResult r = begin_flushBatchRequests(); + end_flushBatchRequests(r); + } + + public AsyncResult begin_flushBatchRequests() + { + return begin_flushBatchRequests(null, null); + } + + private const string __flushBatchRequests_name = "flushBatchRequests"; + + public AsyncResult begin_flushBatchRequests(AsyncCallback cb, object cookie) + { + IceInternal.OutgoingConnectionFactory connectionFactory = instance_.outgoingConnectionFactory(); + IceInternal.ObjectAdapterFactory adapterFactory = instance_.objectAdapterFactory(); + + // + // This callback object receives the results of all invocations + // of Connection.begin_flushBatchRequests. + // + IceInternal.CommunicatorFlushBatch result = + new IceInternal.CommunicatorFlushBatch(this, instance_, __flushBatchRequests_name, cookie); + + if(cb != null) + { + result.whenCompletedWithAsyncCallback(cb); + } + + connectionFactory.flushAsyncBatchRequests(result); + adapterFactory.flushAsyncBatchRequests(result); + + // + // Inform the callback that we have finished initiating all of the + // flush requests. If all of the requests have already completed, + // the callback is invoked now. + // + result.ready(); + + return result; + } + + public void end_flushBatchRequests(AsyncResult result) + { + IceInternal.CommunicatorFlushBatch outAsync = + IceInternal.CommunicatorFlushBatch.check(result, this, __flushBatchRequests_name); + outAsync.wait(); + } + + public Ice.ObjectPrx createAdmin(ObjectAdapter adminAdapter, Identity adminIdentity) + { + return instance_.createAdmin(adminAdapter, adminIdentity); + } + + public Ice.ObjectPrx getAdmin() + { + return instance_.getAdmin(); + } + + public void addAdminFacet(Ice.Object servant, string facet) + { + instance_.addAdminFacet(servant, facet); + } + + public Ice.Object removeAdminFacet(string facet) + { + return instance_.removeAdminFacet(facet); + } + + public Ice.Object findAdminFacet(string facet) + { + return instance_.findAdminFacet(facet); + } + + public Dictionary<string, Ice.Object> findAllAdminFacets() + { + return instance_.findAllAdminFacets(); + } + + public void Dispose() + { + destroy(); + } + + internal CommunicatorI(InitializationData initData) + { + instance_ = new IceInternal.Instance(this, initData); + } + + /* + ~CommunicatorI() + { + if(!destroyed_) + { + if(!System.Environment.HasShutdownStarted) + { + instance_.initializationData().logger.warning( + "Ice::Communicator::destroy() has not been called"); + } + else + { + System.Console.Error.WriteLine("Ice::Communicator::destroy() has not been called"); + } + } + } + */ + + // + // Certain initialization tasks need to be completed after the + // constructor. + // + internal void finishSetup(ref string[] args) + { + try + { + instance_.finishSetup(ref args, this); + } + catch(System.Exception) + { + instance_.destroy(); + throw; + } + } + + // + // For use by Util.getInstance() + // + internal IceInternal.Instance getInstance() + { + return instance_; + } + + private IceInternal.Instance instance_; + } + +} diff --git a/csharp/src/Ice/Compare.cs b/csharp/src/Ice/Compare.cs new file mode 100644 index 00000000000..750ad9d940e --- /dev/null +++ b/csharp/src/Ice/Compare.cs @@ -0,0 +1,185 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; + +namespace Ice +{ + public class CollectionComparer + { + // + // Try to compare two collections efficiently, by doing a reference + // and count comparison. If equality or inequality can be determined + // this way, 'result' contains the outcome of the comparison and the + // return value is true. Otherwise, if equality or inequality cannot + // be determined this way, 'result' and return value are both false. + // + private static bool cheapComparison(System.Collections.ICollection c1, + System.Collections.ICollection c2, + out bool result) + { + if(object.ReferenceEquals(c1, c2)) + { + result = true; + return true; // Equal references means the collections are equal. + } + if(c1 == null || c2 == null) + { + result = false; + return true; // The references are not equal and one of them is null, so c1 and c2 are not equal. + } + if(c1.Count != c2.Count) + { + result = false; + return true; // Different number of elements, so c1 and c2 are not equal. + } + if(c1.Count == 0) + { + result = true; + return true; // Same number of elements, both zero, so c1 and c2 are equal. + } + + result = false; // Couldn't get a result cheaply. + return false; // c1 and c2 may still be equal, but we have to compare elements to find out. + } + + // + // Compare two dictionaries for value equality (as implemented by the Equals() method of its elements). + // + public static bool Equals(System.Collections.IDictionary d1, System.Collections.IDictionary d2) + { + try + { + bool result; + if(cheapComparison(d1, d2, out result)) + { + return result; + } + + System.Collections.ICollection keys1 = d1.Keys; + foreach(object k in keys1) + { + if(d2.Contains(k)) + { + object v1 = d1[k]; + object v2 = d2[k]; + if(v1 == null) + { + if(v2 != null) + { + return false; + } + } + else + { + if(!v1.Equals(v2)) + { + return false; + } + } + } + else + { + return false; + } + } + } + catch(System.Exception) + { + return false; + } + + return true; + } + + // + // Compare two collections for equality (as implemented by the Equals() method of its elements). + // Order is significant. + // + public static bool Equals(System.Collections.ICollection c1, System.Collections.ICollection c2) + { + try + { + bool result; + if(cheapComparison(c1, c2, out result)) + { + return result; + } + + System.Collections.IEnumerator e = c2.GetEnumerator(); + foreach(object o in c1) + { + e.MoveNext(); + if(!Equals(o, e.Current)) + { + return false; + } + } + return true; + } + catch(System.Exception) + { + return false; + } + } + + // + // Compare two collections for equality (as implemented by the Equals() method of its elements). + // Order is significant. + // + public static bool Equals(System.Collections.IEnumerable c1, System.Collections.IEnumerable c2) + { + try + { + if(object.ReferenceEquals(c1, c2)) + { + return true; // Equal references means the collections are equal. + } + if(c1 == null || c2 == null) + { + return false; // The references are not equal and one of them is null, so c1 and c2 are not equal. + } + + System.Collections.IEnumerator e1 = c1.GetEnumerator(); + System.Collections.IEnumerator e2 = c2.GetEnumerator(); + while(e1.MoveNext()) + { + if(!e2.MoveNext()) + { + return false; // c2 has fewer elements than c1. + } + if(e1.Current == null) + { + if(e2.Current != null) + { + return false; + } + } + else + { + if(!e1.Current.Equals(e2.Current)) + { + return false; + } + } + } + if(e2.MoveNext()) + { + return false; // c2 has more elements than c1. + } + return true; + } + catch(System.Exception) + { + return false; + } + } + } +} diff --git a/csharp/src/Ice/ConnectRequestHandler.cs b/csharp/src/Ice/ConnectRequestHandler.cs new file mode 100644 index 00000000000..67ff6629605 --- /dev/null +++ b/csharp/src/Ice/ConnectRequestHandler.cs @@ -0,0 +1,343 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using Ice.Instrumentation; + +namespace IceInternal +{ + public class ConnectRequestHandler : RequestHandler, Reference.GetConnectionCallback, RouterInfo.AddProxyCallback + { + public RequestHandler connect(Ice.ObjectPrxHelperBase proxy) + { + lock(this) + { + try + { + if(!initialized()) + { + _proxies.Add(proxy); + } + } + catch(Ice.LocalException ex) + { + // + // Only throw if the connection didn't get established. If + // it died after being established, we allow the caller to + // retry the connection establishment by not throwing here. + // + if(_connection == null) + { + throw ex; + } + } + + return proxy.setRequestHandler__(_requestHandler); + } + } + + public RequestHandler update(RequestHandler previousHandler, RequestHandler newHandler) + { + return previousHandler == this ? newHandler : this; + } + + public bool sendAsyncRequest(ProxyOutgoingAsyncBase outAsync, out Ice.AsyncCallback sentCallback) + { + lock(this) + { + if(!_initialized) + { + outAsync.cancelable(this); // This will throw if the request is canceled + } + + try + { + if(!initialized()) + { + _requests.AddLast(outAsync); + sentCallback = null; + return false; + } + } + catch(Ice.LocalException ex) + { + throw new RetryException(ex); + } + } + return outAsync.invokeRemote(_connection, _compress, _response, out sentCallback); + } + + public void asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex) + { + lock(this) + { + if(_exception != null) + { + return; // The request has been notified of a failure already. + } + + if(!initialized()) + { + LinkedListNode<ProxyOutgoingAsyncBase> p = _requests.First; + while(p != null) + { + if(p.Value == outAsync) + { + _requests.Remove(p); + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + return; + } + p = p.Next; + } + Debug.Assert(false); // The request has to be queued if it timed out and we're not initialized yet. + } + } + _connection.asyncRequestCanceled(outAsync, ex); + } + + public Reference getReference() + { + return _reference; + } + + public Ice.ConnectionI getConnection() + { + lock(this) + { + if(_exception != null) + { + throw _exception; + } + else + { + return _connection; + } + } + } + + // + // Implementation of Reference.GetConnectionCallback + // + + public void setConnection(Ice.ConnectionI connection, bool compress) + { + lock(this) + { + Debug.Assert(_exception == null && _connection == null); + _connection = connection; + _compress = compress; + } + + // + // If this proxy is for a non-local object, and we are using a router, then + // add this proxy to the router info object. + // + RouterInfo ri = _reference.getRouterInfo(); + if(ri != null && !ri.addProxy(_proxy, this)) + { + return; // The request handler will be initialized once addProxy returns. + } + + // + // We can now send the queued requests. + // + flushRequests(); + } + + public void setException(Ice.LocalException ex) + { + lock(this) + { + Debug.Assert(!_initialized && _exception == null); + _exception = ex; + _proxies.Clear(); + _proxy = null; // Break cyclic reference count. + + // + // NOTE: remove the request handler *before* notifying the + // requests that the connection failed. It's important to ensure + // that future invocations will obtain a new connect request + // handler once invocations are notified. + // + try + { + _reference.getInstance().requestHandlerFactory().removeRequestHandler(_reference, this); + } + catch(Ice.CommunicatorDestroyedException) + { + // Ignore + } + + foreach(ProxyOutgoingAsyncBase outAsync in _requests) + { + Ice.AsyncCallback cb = outAsync.completed(_exception); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + } + _requests.Clear(); + System.Threading.Monitor.PulseAll(this); + } + } + + // + // Implementation of RouterInfo.AddProxyCallback + // + public void addedProxy() + { + // + // The proxy was added to the router info, we're now ready to send the + // queued requests. + // + flushRequests(); + } + + public ConnectRequestHandler(Reference @ref, Ice.ObjectPrx proxy) + { + _reference = @ref; + _response = _reference.getMode() == Reference.Mode.ModeTwoway; + _proxy = (Ice.ObjectPrxHelperBase)proxy; + _initialized = false; + _flushing = false; + _requestHandler = this; + } + + private bool initialized() + { + if(_initialized) + { + Debug.Assert(_connection != null); + return true; + } + else + { + while(_flushing && _exception == null) + { + System.Threading.Monitor.Wait(this); + } + + if(_exception != null) + { + throw _exception; + } + else + { + return _initialized; + } + } + } + + private void flushRequests() + { + lock(this) + { + Debug.Assert(_connection != null && !_initialized); + + // + // We set the _flushing flag to true to prevent any additional queuing. Callers + // might block for a little while as the queued requests are being sent but this + // shouldn't be an issue as the request sends are non-blocking. + // + _flushing = true; + } + + Ice.LocalException exception = null; + foreach(ProxyOutgoingAsyncBase outAsync in _requests) + { + try + { + Ice.AsyncCallback sentCallback = null; + if(outAsync.invokeRemote(_connection, _compress, _response, out sentCallback)) + { + if(sentCallback != null) + { + outAsync.invokeSentAsync(sentCallback); + } + } + } + catch(RetryException ex) + { + exception = ex.get(); + + // Remove the request handler before retrying. + _reference.getInstance().requestHandlerFactory().removeRequestHandler(_reference, this); + + outAsync.retryException(ex.get()); + } + catch(Ice.LocalException ex) + { + exception = ex; + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + } + } + _requests.Clear(); + + // + // If we aren't caching the connection, don't bother creating a + // connection request handler. Otherwise, update the proxies + // request handler to use the more efficient connection request + // handler. + // + if(_reference.getCacheConnection() && exception == null) + { + _requestHandler = new ConnectionRequestHandler(_reference, _connection, _compress); + foreach(Ice.ObjectPrxHelperBase prx in _proxies) + { + prx.updateRequestHandler__(this, _requestHandler); + } + } + + lock(this) + { + Debug.Assert(!_initialized); + _exception = exception; + _initialized = _exception == null; + _flushing = false; + + // + // Only remove once all the requests are flushed to + // guarantee serialization. + // + _reference.getInstance().requestHandlerFactory().removeRequestHandler(_reference, this); + + _proxies.Clear(); + _proxy = null; // Break cyclic reference count. + System.Threading.Monitor.PulseAll(this); + } + } + + private Reference _reference; + private bool _response; + + private Ice.ObjectPrxHelperBase _proxy; + private HashSet<Ice.ObjectPrxHelperBase> _proxies = new HashSet<Ice.ObjectPrxHelperBase>(); + + private Ice.ConnectionI _connection; + private bool _compress; + private Ice.LocalException _exception; + private bool _initialized; + private bool _flushing; + + private LinkedList<ProxyOutgoingAsyncBase> _requests = new LinkedList<ProxyOutgoingAsyncBase>(); + private RequestHandler _requestHandler; + } +} diff --git a/csharp/src/Ice/ConnectionFactory.cs b/csharp/src/Ice/ConnectionFactory.cs new file mode 100644 index 00000000000..215b64322b1 --- /dev/null +++ b/csharp/src/Ice/ConnectionFactory.cs @@ -0,0 +1,1758 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Net.Sockets; + using System.Threading; + using System.Text; + using IceUtilInternal; + + public class MultiDictionary<K, V> : Dictionary<K, ICollection<V>> + { + public void + Add(K key, V value) + { + ICollection<V> list = null; + if(!this.TryGetValue(key, out list)) + { + list = new List<V>(); + this.Add(key, list); + } + list.Add(value); + } + + public void + Remove(K key, V value) + { + ICollection<V> list = this[key]; + list.Remove(value); + if(list.Count == 0) + { + this.Remove(key); + } + } + } + + public sealed class OutgoingConnectionFactory + { + public interface CreateConnectionCallback + { + void setConnection(Ice.ConnectionI connection, bool compress); + void setException(Ice.LocalException ex); + } + + public void destroy() + { + lock(this) + { + if(_destroyed) + { + return; + } + + foreach(ICollection<Ice.ConnectionI> connections in _connections.Values) + { + foreach(Ice.ConnectionI c in connections) + { + c.destroy(Ice.ConnectionI.CommunicatorDestroyed); + } + } + + _destroyed = true; + _communicator = null; + System.Threading.Monitor.PulseAll(this); + } + } + + public void updateConnectionObservers() + { + lock(this) + { + foreach(ICollection<Ice.ConnectionI> connections in _connections.Values) + { + foreach(Ice.ConnectionI c in connections) + { + c.updateObserver(); + } + } + } + } + + public void waitUntilFinished() + { + Dictionary<Connector, ICollection<Ice.ConnectionI>> connections = null; + lock(this) + { + // + // First we wait until the factory is destroyed. We also + // wait until there are no pending connections + // anymore. Only then we can be sure the _connections + // contains all connections. + // + while(!_destroyed || _pending.Count > 0 || _pendingConnectCount > 0) + { + System.Threading.Monitor.Wait(this); + } + + // + // We want to wait until all connections are finished outside the + // thread synchronization. + // + connections = new Dictionary<Connector, ICollection<Ice.ConnectionI>>(_connections); + } + + // + // Now we wait until the destruction of each connection is finished. + // + foreach(ICollection<Ice.ConnectionI> cl in connections.Values) + { + foreach(Ice.ConnectionI c in cl) + { + c.waitUntilFinished(); + } + } + + lock(this) + { + // Ensure all the connections are finished and reapable at this point. + ICollection<Ice.ConnectionI> cons = _monitor.swapReapedConnections(); + if(cons != null) + { + int size = 0; + foreach(ICollection<Ice.ConnectionI> cl in _connections.Values) + { + size += cl.Count; + } + Debug.Assert(cons.Count == size); + _connections.Clear(); + _connectionsByEndpoint.Clear(); + } + else + { + Debug.Assert(_connections.Count == 0); + Debug.Assert(_connectionsByEndpoint.Count == 0); + } + _monitor.destroy(); + } + } + + + public void create(EndpointI[] endpts, bool hasMore, Ice.EndpointSelectionType selType, + CreateConnectionCallback callback) + { + Debug.Assert(endpts.Length > 0); + + // + // Apply the overrides. + // + List<EndpointI> endpoints = applyOverrides(endpts); + + // + // Try to find a connection to one of the given endpoints. + // + try + { + bool compress; + Ice.ConnectionI connection = findConnection(endpoints, out compress); + if(connection != null) + { + callback.setConnection(connection, compress); + return; + } + } + catch(Ice.LocalException ex) + { + callback.setException(ex); + return; + } + + ConnectCallback cb = new ConnectCallback(this, endpoints, hasMore, callback, selType); + cb.getConnectors(); + } + + public void setRouterInfo(IceInternal.RouterInfo routerInfo) + { + lock(this) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(routerInfo != null); + + // + // Search for connections to the router's client proxy + // endpoints, and update the object adapter for such + // connections, so that callbacks from the router can be + // received over such connections. + // + Ice.ObjectAdapter adapter = routerInfo.getAdapter(); + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + EndpointI[] endpoints = routerInfo.getClientEndpoints(); + for(int i = 0; i < endpoints.Length; i++) + { + EndpointI endpoint = endpoints[i]; + + // + // Modify endpoints with overrides. + // + if(defaultsAndOverrides.overrideTimeout) + { + endpoint = endpoint.timeout(defaultsAndOverrides.overrideTimeoutValue); + } + + // + // The Ice.ConnectionI object does not take the compression flag of + // endpoints into account, but instead gets the information + // about whether messages should be compressed or not from + // other sources. In order to allow connection sharing for + // endpoints that differ in the value of the compression flag + // only, we always set the compression flag to false here in + // this connection factory. + // + endpoint = endpoint.compress(false); + + foreach(ICollection<Ice.ConnectionI> connections in _connections.Values) + { + foreach(Ice.ConnectionI connection in connections) + { + if(connection.endpoint().Equals(endpoint)) + { + connection.setAdapter(adapter); + } + } + } + } + } + } + + public void removeAdapter(Ice.ObjectAdapter adapter) + { + lock(this) + { + if(_destroyed) + { + return; + } + + foreach(ICollection<Ice.ConnectionI> connectionList in _connections.Values) + { + foreach(Ice.ConnectionI connection in connectionList) + { + if(connection.getAdapter() == adapter) + { + connection.setAdapter(null); + } + } + } + } + } + + public void flushAsyncBatchRequests(CommunicatorFlushBatch outAsync) + { + ICollection<Ice.ConnectionI> c = new List<Ice.ConnectionI>(); + + lock(this) + { + if(!_destroyed) + { + foreach(ICollection<Ice.ConnectionI> connectionList in _connections.Values) + { + foreach(Ice.ConnectionI conn in connectionList) + { + if(conn.isActiveOrHolding()) + { + c.Add(conn); + } + } + } + } + } + + foreach(Ice.ConnectionI conn in c) + { + try + { + outAsync.flushConnection(conn); + } + catch(Ice.LocalException) + { + // Ignore. + } + } + } + + // + // Only for use by Instance. + // + internal OutgoingConnectionFactory(Ice.Communicator communicator, Instance instance) + { + _communicator = communicator; + _instance = instance; + _destroyed = false; + _monitor = new FactoryACMMonitor(instance, instance.clientACM()); + _pendingConnectCount = 0; + } + + private List<EndpointI> applyOverrides(EndpointI[] endpts) + { + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + List<EndpointI> endpoints = new List<EndpointI>(); + for(int i = 0; i < endpts.Length; i++) + { + // + // Modify endpoints with overrides. + // + if(defaultsAndOverrides.overrideTimeout) + { + endpoints.Add(endpts[i].timeout(defaultsAndOverrides.overrideTimeoutValue)); + } + else + { + endpoints.Add(endpts[i]); + } + } + + return endpoints; + } + + private Ice.ConnectionI findConnection(List<EndpointI> endpoints, out bool compress) + { + lock(this) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + Debug.Assert(endpoints.Count > 0); + + foreach(EndpointI endpoint in endpoints) + { + ICollection<Ice.ConnectionI> connectionList = null; + if(!_connectionsByEndpoint.TryGetValue(endpoint, out connectionList)) + { + continue; + } + + foreach(Ice.ConnectionI connection in connectionList) + { + if(connection.isActiveOrHolding()) // Don't return destroyed or unvalidated connections + { + if(defaultsAndOverrides.overrideCompress) + { + compress = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress = endpoint.compress(); + } + return connection; + } + } + } + + compress = false; // Satisfy the compiler + return null; + } + } + + // + // Must be called while synchronized. + // + private Ice.ConnectionI findConnection(List<ConnectorInfo> connectors, out bool compress) + { + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + foreach(ConnectorInfo ci in connectors) + { + if(_pending.ContainsKey(ci.connector)) + { + continue; + } + + ICollection<Ice.ConnectionI> connectionList = null; + if(!_connections.TryGetValue(ci.connector, out connectionList)) + { + continue; + } + + foreach(Ice.ConnectionI connection in connectionList) + { + if(connection.isActiveOrHolding()) // Don't return destroyed or un-validated connections + { + if(defaultsAndOverrides.overrideCompress) + { + compress = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress = ci.endpoint.compress(); + } + return connection; + } + } + } + + compress = false; // Satisfy the compiler + return null; + } + + internal void incPendingConnectCount() + { + // + // Keep track of the number of pending connects. The outgoing connection factory + // waitUntilFinished() method waits for all the pending connects to terminate before + // to return. This ensures that the communicator client thread pool isn't destroyed + // too soon and will still be available to execute the ice_exception() callbacks for + // the asynchronous requests waiting on a connection to be established. + // + + lock(this) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + ++_pendingConnectCount; + } + } + + internal void decPendingConnectCount() + { + lock(this) + { + --_pendingConnectCount; + Debug.Assert(_pendingConnectCount >= 0); + if(_destroyed && _pendingConnectCount == 0) + { + System.Threading.Monitor.PulseAll(this); + } + } + } + + private Ice.ConnectionI getConnection(List<ConnectorInfo> connectors, ConnectCallback cb, out bool compress) + { + lock(this) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + // + // Reap closed connections + // + ICollection<Ice.ConnectionI> cons = _monitor.swapReapedConnections(); + if(cons != null) + { + foreach(Ice.ConnectionI c in cons) + { + _connections.Remove(c.connector(), c); + _connectionsByEndpoint.Remove(c.endpoint(), c); + _connectionsByEndpoint.Remove(c.endpoint().compress(true), c); + } + } + + // + // Try to get the connection. We may need to wait for other threads to + // finish if one of them is currently establishing a connection to one + // of our connectors. + // + while(true) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + // + // Search for a matching connection. If we find one, we're done. + // + Ice.ConnectionI connection = findConnection(connectors, out compress); + if(connection != null) + { + return connection; + } + + if(addToPending(cb, connectors)) + { + // + // If a callback is not specified we wait until another thread notifies us about a + // change to the pending list. Otherwise, if a callback is provided we're done: + // when the pending list changes the callback will be notified and will try to + // get the connection again. + // + if(cb == null) + { + System.Threading.Monitor.Wait(this); + } + else + { + return null; + } + } + else + { + // + // If no thread is currently establishing a connection to one of our connectors, + // we get out of this loop and start the connection establishment to one of the + // given connectors. + // + break; + } + } + } + + // + // At this point, we're responsible for establishing the connection to one of + // the given connectors. If it's a non-blocking connect, calling nextConnector + // will start the connection establishment. Otherwise, we return null to get + // the caller to establish the connection. + // + if(cb != null) + { + cb.nextConnector(); + } + + compress = false; // Satisfy the compiler + return null; + } + + private Ice.ConnectionI createConnection(Transceiver transceiver, ConnectorInfo ci) + { + lock(this) + { + Debug.Assert(_pending.ContainsKey(ci.connector) && transceiver != null); + + // + // Create and add the connection to the connection map. Adding the connection to the map + // is necessary to support the interruption of the connection initialization and validation + // in case the communicator is destroyed. + // + Ice.ConnectionI connection; + try + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + connection = new Ice.ConnectionI(_communicator, _instance, _monitor, transceiver, ci.connector, + ci.endpoint.compress(false), null); + } + catch(Ice.LocalException) + { + try + { + transceiver.close(); + } + catch(Ice.LocalException) + { + // Ignore + } + throw; + } + + _connections.Add(ci.connector, connection); + _connectionsByEndpoint.Add(connection.endpoint(), connection); + _connectionsByEndpoint.Add(connection.endpoint().compress(true), connection); + return connection; + } + } + + private void finishGetConnection(List<ConnectorInfo> connectors, + ConnectorInfo ci, + Ice.ConnectionI connection, + ConnectCallback cb) + { + HashSet<ConnectCallback> connectionCallbacks = new HashSet<ConnectCallback>(); + if(cb != null) + { + connectionCallbacks.Add(cb); + } + + HashSet<ConnectCallback> callbacks = new HashSet<ConnectCallback>(); + lock(this) + { + foreach(ConnectorInfo c in connectors) + { + HashSet<ConnectCallback> s = null; + if(_pending.TryGetValue(c.connector, out s)) + { + foreach(ConnectCallback cc in s) + { + if(cc.hasConnector(ci)) + { + connectionCallbacks.Add(cc); + } + else + { + callbacks.Add(cc); + } + } + _pending.Remove(c.connector); + } + } + + foreach(ConnectCallback cc in connectionCallbacks) + { + cc.removeFromPending(); + callbacks.Remove(cc); + } + foreach(ConnectCallback cc in callbacks) + { + cc.removeFromPending(); + } + System.Threading.Monitor.PulseAll(this); + } + + bool compress; + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideCompress) + { + compress = defaultsAndOverrides.overrideCompressValue; + } + else + { + compress = ci.endpoint.compress(); + } + + foreach(ConnectCallback cc in callbacks) + { + cc.getConnection(); + } + foreach(ConnectCallback cc in connectionCallbacks) + { + cc.setConnection(connection, compress); + } + } + + private void finishGetConnection(List<ConnectorInfo> connectors, Ice.LocalException ex, ConnectCallback cb) + { + HashSet<ConnectCallback> failedCallbacks = new HashSet<ConnectCallback>(); + if(cb != null) + { + failedCallbacks.Add(cb); + } + + HashSet<ConnectCallback> callbacks = new HashSet<ConnectCallback>(); + lock(this) + { + foreach(ConnectorInfo c in connectors) + { + HashSet<ConnectCallback> s = null; + if(_pending.TryGetValue(c.connector, out s)) + { + foreach(ConnectCallback cc in s) + { + if(cc.removeConnectors(connectors)) + { + failedCallbacks.Add(cc); + } + else + { + callbacks.Add(cc); + } + } + _pending.Remove(c.connector); + } + } + + foreach(ConnectCallback cc in callbacks) + { + Debug.Assert(!failedCallbacks.Contains(cc)); + cc.removeFromPending(); + } + System.Threading.Monitor.PulseAll(this); + } + + foreach(ConnectCallback cc in callbacks) + { + cc.getConnection(); + } + foreach(ConnectCallback cc in failedCallbacks) + { + cc.setException(ex); + } + } + + private void handleConnectionException(Ice.LocalException ex, bool hasMore) + { + TraceLevels traceLevels = _instance.traceLevels(); + if(traceLevels.retry >= 2) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("connection to endpoint failed"); + if(ex is Ice.CommunicatorDestroyedException) + { + s.Append("\n"); + } + else + { + if(hasMore) + { + s.Append(", trying next endpoint\n"); + } + else + { + s.Append(" and no more endpoints to try\n"); + } + } + s.Append(ex); + _instance.initializationData().logger.trace(traceLevels.retryCat, s.ToString()); + } + } + + private bool + addToPending(ConnectCallback cb, List<ConnectorInfo> connectors) + { + // + // Add the callback to each connector pending list. + // + bool found = false; + foreach(ConnectorInfo ci in connectors) + { + HashSet<ConnectCallback> cbs = null; + if(_pending.TryGetValue(ci.connector, out cbs)) + { + found = true; + if(cb != null) + { + cbs.Add(cb); // Add the callback to each pending connector. + } + } + } + + if(found) + { + return true; + } + + // + // If there's no pending connection for the given connectors, we're + // responsible for its establishment. We add empty pending lists, + // other callbacks to the same connectors will be queued. + // + foreach(ConnectorInfo ci in connectors) + { + if(!_pending.ContainsKey(ci.connector)) + { + _pending.Add(ci.connector, new HashSet<ConnectCallback>()); + } + } + return false; + } + + private void + removeFromPending(ConnectCallback cb, List<ConnectorInfo> connectors) + { + foreach(ConnectorInfo ci in connectors) + { + HashSet<ConnectCallback> cbs = null; + if(_pending.TryGetValue(ci.connector, out cbs)) + { + cbs.Remove(cb); + } + } + } + + internal void handleException(Ice.LocalException ex, bool hasMore) + { + TraceLevels traceLevels = _instance.traceLevels(); + if(traceLevels.retry >= 2) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't resolve endpoint host"); + if(ex is Ice.CommunicatorDestroyedException) + { + s.Append("\n"); + } + else + { + if(hasMore) + { + s.Append(", trying next endpoint\n"); + } + else + { + s.Append(" and no more endpoints to try\n"); + } + } + s.Append(ex); + _instance.initializationData().logger.trace(traceLevels.retryCat, s.ToString()); + } + } + + private class ConnectorInfo + { + internal ConnectorInfo(Connector c, EndpointI e) + { + connector = c; + endpoint = e; + } + + public override bool Equals(object obj) + { + ConnectorInfo r = (ConnectorInfo)obj; + return connector.Equals(r.connector); + } + + public override int GetHashCode() + { + return connector.GetHashCode(); + } + + public Connector connector; + public EndpointI endpoint; + } + + private class ConnectCallback : Ice.ConnectionI.StartCallback, EndpointI_connectors + { + internal ConnectCallback(OutgoingConnectionFactory f, List<EndpointI> endpoints, bool more, + CreateConnectionCallback cb, Ice.EndpointSelectionType selType) + { + _factory = f; + _endpoints = endpoints; + _hasMore = more; + _callback = cb; + _selType = selType; + _endpointsIter = 0; + } + + // + // Methods from ConnectionI.StartCallback + // + public void connectionStartCompleted(Ice.ConnectionI connection) + { + if(_observer != null) + { + _observer.detach(); + } + connection.activate(); + _factory.finishGetConnection(_connectors, _current, connection, this); + } + + public void connectionStartFailed(Ice.ConnectionI connection, Ice.LocalException ex) + { + if(_observer != null) + { + _observer.failed(ex.ice_name()); + _observer.detach(); + } + _factory.handleConnectionException(ex, _hasMore || _iter < _connectors.Count); + if(ex is Ice.CommunicatorDestroyedException) // No need to continue. + { + _factory.finishGetConnection(_connectors, ex, this); + } + else if(_iter < _connectors.Count) // Try the next connector. + { + nextConnector(); + } + else + { + _factory.finishGetConnection(_connectors, ex, this); + } + } + + // + // Methods from EndpointI_connectors + // + public void connectors(List<Connector> cons) + { + foreach(Connector connector in cons) + { + _connectors.Add(new ConnectorInfo(connector, _currentEndpoint)); + } + + if(_endpointsIter < _endpoints.Count) + { + nextEndpoint(); + } + else + { + Debug.Assert(_connectors.Count > 0); + + // + // We now have all the connectors for the given endpoints. We can try to obtain the + // connection. + // + _iter = 0; + getConnection(); + } + } + + public void exception(Ice.LocalException ex) + { + _factory.handleException(ex, _hasMore || _endpointsIter < _endpoints.Count); + if(_endpointsIter < _endpoints.Count) + { + nextEndpoint(); + } + else if(_connectors.Count > 0) + { + // + // We now have all the connectors for the given endpoints. We can try to obtain the + // connection. + // + _iter = 0; + getConnection(); + } + else + { + _callback.setException(ex); + _factory.decPendingConnectCount(); // Must be called last. + } + } + + public void setConnection(Ice.ConnectionI connection, bool compress) + { + // + // Callback from the factory: the connection to one of the callback + // connectors has been established. + // + _callback.setConnection(connection, compress); + _factory.decPendingConnectCount(); // Must be called last. + } + + public void setException(Ice.LocalException ex) + { + // + // Callback from the factory: connection establishment failed. + // + _callback.setException(ex); + _factory.decPendingConnectCount(); // Must be called last. + } + + public bool hasConnector(ConnectorInfo ci) + { + return _connectors.Contains(ci); + } + + public bool removeConnectors(List<ConnectorInfo> connectors) + { + foreach(ConnectorInfo ci in connectors) + { + while(_connectors.Remove(ci)); // Remove all of them. + } + return _connectors.Count == 0; + } + + public void removeFromPending() + { + _factory.removeFromPending(this, _connectors); + } + + public void getConnectors() + { + try + { + // + // Notify the factory that there's an async connect pending. This is necessary + // to prevent the outgoing connection factory to be destroyed before all the + // pending asynchronous connects are finished. + // + _factory.incPendingConnectCount(); + } + catch(Ice.LocalException ex) + { + _callback.setException(ex); + return; + } + + nextEndpoint(); + } + + void nextEndpoint() + { + try + { + Debug.Assert(_endpointsIter < _endpoints.Count); + _currentEndpoint = _endpoints[_endpointsIter++]; + _currentEndpoint.connectors_async(_selType, this); + } + catch(Ice.LocalException ex) + { + exception(ex); + } + } + + internal void getConnection() + { + try + { + // + // If all the connectors have been created, we ask the factory to get a + // connection. + // + bool compress; + Ice.ConnectionI connection = _factory.getConnection(_connectors, this, out compress); + if(connection == null) + { + // + // A null return value from getConnection indicates that the connection + // is being established and that everthing has been done to ensure that + // the callback will be notified when the connection establishment is + // done. + // + return; + } + + _callback.setConnection(connection, compress); + _factory.decPendingConnectCount(); // Must be called last. + } + catch(Ice.LocalException ex) + { + _callback.setException(ex); + _factory.decPendingConnectCount(); // Must be called last. + } + } + + internal void nextConnector() + { + Ice.ConnectionI connection = null; + try + { + Debug.Assert(_iter < _connectors.Count); + _current = _connectors[_iter++]; + + Ice.Instrumentation.CommunicatorObserver obsv = _factory._instance.initializationData().observer; + if(obsv != null) + { + _observer = obsv.getConnectionEstablishmentObserver(_current.endpoint, + _current.connector.ToString()); + if(_observer != null) + { + _observer.attach(); + } + } + + if(_factory._instance.traceLevels().network >= 2) + { + StringBuilder s = new StringBuilder("trying to establish "); + s.Append(_current.endpoint.protocol()); + s.Append(" connection to "); + s.Append(_current.connector.ToString()); + _factory._instance.initializationData().logger.trace( + _factory._instance.traceLevels().networkCat, s.ToString()); + } + + connection = _factory.createConnection(_current.connector.connect(), _current); + connection.start(this); + } + catch(Ice.LocalException ex) + { + if(_factory._instance.traceLevels().network >= 2) + { + StringBuilder s = new StringBuilder("failed to establish "); + s.Append(_current.endpoint.protocol()); + s.Append(" connection to "); + s.Append(_current.connector.ToString()); + s.Append(ex); + _factory._instance.initializationData().logger.trace( + _factory._instance.traceLevels().networkCat, s.ToString()); + } + + connectionStartFailed(connection, ex); + } + } + + private OutgoingConnectionFactory _factory; + private bool _hasMore; + private CreateConnectionCallback _callback; + private List<EndpointI> _endpoints; + private Ice.EndpointSelectionType _selType; + private int _endpointsIter; + private EndpointI _currentEndpoint; + private List<ConnectorInfo> _connectors = new List<ConnectorInfo>(); + private int _iter; + private ConnectorInfo _current; + private Ice.Instrumentation.Observer _observer; + } + + private Ice.Communicator _communicator; + private readonly Instance _instance; + private FactoryACMMonitor _monitor; + private bool _destroyed; + + private MultiDictionary<Connector, Ice.ConnectionI> _connections = + new MultiDictionary<Connector, Ice.ConnectionI>(); + private MultiDictionary<EndpointI, Ice.ConnectionI> _connectionsByEndpoint = + new MultiDictionary<EndpointI, Ice.ConnectionI>(); + private Dictionary<Connector, HashSet<ConnectCallback>> _pending = + new Dictionary<Connector, HashSet<ConnectCallback>>(); + private int _pendingConnectCount; + } + + public sealed class IncomingConnectionFactory : EventHandler, Ice.ConnectionI.StartCallback + { + public void activate() + { + lock(this) + { + setState(StateActive); + } + } + + public void hold() + { + lock(this) + { + setState(StateHolding); + } + } + + public void destroy() + { + lock(this) + { + setState(StateClosed); + } + } + + public void updateConnectionObservers() + { + lock(this) + { + foreach(Ice.ConnectionI connection in _connections) + { + connection.updateObserver(); + } + } + } + + public void waitUntilHolding() + { + ICollection<Ice.ConnectionI> connections; + + lock(this) + { + // + // First we wait until the connection factory itself is in + // holding state. + // + while(_state < StateHolding) + { + System.Threading.Monitor.Wait(this); + } + + // + // We want to wait until all connections are in holding state + // outside the thread synchronization. + // + connections = new List<Ice.ConnectionI>(_connections); + } + + // + // Now we wait until each connection is in holding state. + // + foreach(Ice.ConnectionI connection in connections) + { + connection.waitUntilHolding(); + } + } + + public void waitUntilFinished() + { + ICollection<Ice.ConnectionI> connections = null; + + lock(this) + { + // + // First we wait until the factory is destroyed. If we are using + // an acceptor, we also wait for it to be closed. + // + while(_state != StateFinished) + { + System.Threading.Monitor.Wait(this); + } + + // + // Clear the OA. See bug 1673 for the details of why this is necessary. + // + _adapter = null; + + // + // We want to wait until all connections are finished outside the + // thread synchronization. + // + connections = new List<Ice.ConnectionI>(_connections); + } + + foreach(Ice.ConnectionI connection in connections) + { + connection.waitUntilFinished(); + } + + lock(this) + { + if(_transceiver != null) + { + Debug.Assert(_connections.Count <= 1); // The connection isn't monitored or reaped. + } + else + { + // Ensure all the connections are finished and reapable at this point. + ICollection<Ice.ConnectionI> cons = _monitor.swapReapedConnections(); + Debug.Assert((cons == null ? 0 : cons.Count) == _connections.Count); + if(cons != null) + { + cons.Clear(); + } + } + _connections.Clear(); + _monitor.destroy(); + } + } + + public EndpointI endpoint() + { + // No mutex protection necessary, _endpoint is immutable. + return _endpoint; + } + + public ICollection<Ice.ConnectionI> connections() + { + lock(this) + { + ICollection<Ice.ConnectionI> connections = new List<Ice.ConnectionI>(); + + // + // Only copy connections which have not been destroyed. + // + foreach(Ice.ConnectionI connection in _connections) + { + if(connection.isActiveOrHolding()) + { + connections.Add(connection); + } + } + + return connections; + } + } + + public void flushAsyncBatchRequests(CommunicatorFlushBatch outAsync) + { + // + // connections() is synchronized, no need to synchronize here. + // + foreach(Ice.ConnectionI connection in connections()) + { + try + { + outAsync.flushConnection(connection); + } + catch(Ice.LocalException) + { + // Ignore. + } + } + } + + // + // Operations from EventHandler. + // + public override bool startAsync(int operation, AsyncCallback callback, ref bool completedSynchronously) + { + if(_state >= StateClosed) + { + return false; + } + + Debug.Assert(_acceptor != null); + try + { + completedSynchronously = _acceptor.startAccept(callback, this); + } + catch(Ice.LocalException ex) + { + string s = "can't accept connections:\n" + ex + '\n' + _acceptor.ToString(); + try + { + _instance.initializationData().logger.error(s); + } + finally + { +#if !COMPACT && !SILVERLIGHT + System.Environment.FailFast(s); +#endif + } + return false; + } + return true; + } + + public override bool finishAsync(int unused) + { + try + { + _acceptor.finishAccept(); + } + catch(Ice.LocalException ex) + { + if(Network.noMoreFds(ex.InnerException)) + { + string s = "can't accept more connections:\n" + ex + '\n' + _acceptor.ToString(); + try + { + _instance.initializationData().logger.error(s); + } + finally + { +#if !COMPACT && !SILVERLIGHT + System.Environment.FailFast(s); +#endif + } + return false; + } + else + { + string s = "couldn't accept connection:\n" + ex + '\n' + _acceptor.ToString(); + _instance.initializationData().logger.error(s); + return false; + } + } + return _state < StateClosed; + } + + public override void message(ref ThreadPoolCurrent current) + { + Ice.ConnectionI connection = null; + + ThreadPoolMessage msg = new ThreadPoolMessage(this); + + lock(this) + { + if(!msg.startIOScope(ref current)) + { + return; + } + + try + { + if(_state >= StateClosed) + { + return; + } + else if(_state == StateHolding) + { + return; + } + + // + // Reap closed connections + // + ICollection<Ice.ConnectionI> cons = _monitor.swapReapedConnections(); + if(cons != null) + { + foreach(Ice.ConnectionI c in cons) + { + _connections.Remove(c); + } + } + + // + // Now accept a new connection. + // + Transceiver transceiver = null; + try + { + transceiver = _acceptor.accept(); + + if(_instance.traceLevels().network >= 2) + { + StringBuilder s = new StringBuilder("trying to accept "); + s.Append(_endpoint.protocol()); + s.Append(" connection\n"); + s.Append(transceiver.ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + } + catch(Ice.SocketException ex) + { + if(Network.noMoreFds(ex.InnerException)) + { + string s = "can't accept more connections:\n" + ex + '\n' + _acceptor.ToString(); + try + { + _instance.initializationData().logger.error(s); + } + finally + { +#if !COMPACT && !SILVERLIGHT + System.Environment.FailFast(s); +#endif + } + } + + // Ignore socket exceptions. + return; + } + catch(Ice.LocalException ex) + { + // Warn about other Ice local exceptions. + if(_warn) + { + warning(ex); + } + return; + } + + Debug.Assert(transceiver != null); + + try + { + connection = new Ice.ConnectionI(_adapter.getCommunicator(), _instance, _monitor, transceiver, + null, _endpoint, _adapter); + } + catch(Ice.LocalException ex) + { + try + { + transceiver.close(); + } + catch(Ice.LocalException) + { + // Ignore + } + + if(_warn) + { + warning(ex); + } + return; + } + + _connections.Add(connection); + } + finally + { + msg.finishIOScope(ref current); + } + } + + Debug.Assert(connection != null); + connection.start(this); + } + + public override void finished(ref ThreadPoolCurrent current) + { + lock(this) + { + Debug.Assert(_state == StateClosed); + setState(StateFinished); + } + } + + public override string ToString() + { + if(_transceiver != null) + { + return _transceiver.ToString(); + } + return _acceptor.ToString(); + } + + // + // Operations from ConnectionI.StartCallback + // + public void connectionStartCompleted(Ice.ConnectionI connection) + { + lock(this) + { + // + // Initially, connections are in the holding state. If the factory is active + // we activate the connection. + // + if(_state == StateActive) + { + connection.activate(); + } + } + } + + public void connectionStartFailed(Ice.ConnectionI connection, Ice.LocalException ex) + { + lock(this) + { + if(_state >= StateClosed) + { + return; + } + + // + // Do not warn about connection exceptions here. The connection is not yet validated. + // + } + } + + public IncomingConnectionFactory(Instance instance, EndpointI endpoint, Ice.ObjectAdapterI adapter) + { + _instance = instance; + _endpoint = endpoint; + _adapter = adapter; + _warn = _instance.initializationData().properties.getPropertyAsInt("Ice.Warn.Connections") > 0; + _connections = new HashSet<Ice.ConnectionI>(); + _state = StateHolding; + _monitor = new FactoryACMMonitor(instance, ((Ice.ObjectAdapterI)adapter).getACM()); + + DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideTimeout) + { + _endpoint = _endpoint.timeout(defaultsAndOverrides.overrideTimeoutValue); + } + + if(defaultsAndOverrides.overrideCompress) + { + _endpoint = _endpoint.compress(defaultsAndOverrides.overrideCompressValue); + } + + try + { + _transceiver = _endpoint.transceiver(); + if(_transceiver != null) + { + if(_instance.traceLevels().network >= 2) + { + StringBuilder s = new StringBuilder("attempting to bind to "); + s.Append(_endpoint.protocol()); + s.Append(" socket\n"); + s.Append(_transceiver.ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + _endpoint = _transceiver.bind(); + + Ice.ConnectionI connection = new Ice.ConnectionI(_adapter.getCommunicator(), _instance, null, + _transceiver, null, _endpoint, _adapter); + connection.startAndWait(); + _connections.Add(connection); + } + else + { + createAcceptor(); + } + } + catch(System.Exception ex) + { + // + // Clean up. + // + if(_transceiver != null) + { + try + { + _transceiver.close(); + } + catch(Ice.LocalException) + { + // Ignore + } + } + + _state = StateFinished; + _monitor.destroy(); + _connections.Clear(); + + if(ex is Ice.LocalException) + { + throw; + } + else + { + throw new Ice.SyscallException(ex); + } + } + } + + private const int StateActive = 0; + private const int StateHolding = 1; + private const int StateClosed = 2; + private const int StateFinished = 3; + + private void setState(int state) + { + if(_state == state) // Don't switch twice. + { + return; + } + + switch (state) + { + case StateActive: + { + if(_state != StateHolding) // Can only switch from holding to active. + { + return; + } + if(_acceptor != null) + { + if(_instance.traceLevels().network >= 1) + { + StringBuilder s = new StringBuilder("accepting "); + s.Append(_endpoint.protocol()); + s.Append(" connections at "); + s.Append(_acceptor.ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, + s.ToString()); + } + _adapter.getThreadPool().register(this, SocketOperation.Read); + } + + foreach(Ice.ConnectionI connection in _connections) + { + connection.activate(); + } + break; + } + + case StateHolding: + { + if(_state != StateActive) // Can only switch from active to holding. + { + return; + } + if(_acceptor != null) + { + if(_instance.traceLevels().network >= 1) + { + StringBuilder s = new StringBuilder("holding "); + s.Append(_endpoint.protocol()); + s.Append(" connections at "); + s.Append(_acceptor.ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, + s.ToString()); + } + _adapter.getThreadPool().unregister(this, SocketOperation.Read); + } + + foreach(Ice.ConnectionI connection in _connections) + { + connection.hold(); + } + break; + } + + case StateClosed: + { + if(_acceptor != null) + { + _adapter.getThreadPool().finish(this); + closeAcceptor(); + } + else + { + state = StateFinished; + } + + foreach(Ice.ConnectionI connection in _connections) + { + connection.destroy(Ice.ConnectionI.ObjectAdapterDeactivated); + } + break; + } + + case StateFinished: + { + Debug.Assert(_state == StateClosed); + break; + } + } + + _state = state; + System.Threading.Monitor.PulseAll(this); + } + + private void createAcceptor() + { + try + { + _acceptor = _endpoint.acceptor(_adapter.getName()); + Debug.Assert(_acceptor != null); + + if(_instance.traceLevels().network >= 2) + { + StringBuilder s = new StringBuilder("attempting to bind to "); + s.Append(_endpoint.protocol()); + s.Append(" socket "); + s.Append(_acceptor.ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + _endpoint = _acceptor.listen(); + + if(_instance.traceLevels().network >= 1) + { + StringBuilder s = new StringBuilder("listening for "); + s.Append(_endpoint.protocol()); + s.Append(" connections\n"); + s.Append(_acceptor.toDetailedString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + + _adapter.getThreadPool().initialize(this); + + if(_state == StateActive) + { + _adapter.getThreadPool().unregister(this, SocketOperation.Read); + } + } + catch(SystemException ex) + { + if(_acceptor != null) + { + _acceptor.close(); + } + throw ex; + } + } + + private void closeAcceptor() + { + if(_instance.traceLevels().network >= 1) + { + StringBuilder s = new StringBuilder("stopping to accept "); + s.Append(_endpoint.protocol()); + s.Append(" connections at "); + s.Append(_acceptor.ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + + _acceptor.close(); + } + + private void warning(Ice.LocalException ex) + { + _instance.initializationData().logger.warning("connection exception:\n" + ex + '\n' + + _acceptor.ToString()); + } + + private Instance _instance; + private FactoryACMMonitor _monitor; + + private Acceptor _acceptor; + private readonly Transceiver _transceiver; + private EndpointI _endpoint; + + private Ice.ObjectAdapterI _adapter; + + private readonly bool _warn; + + private HashSet<Ice.ConnectionI> _connections; + + private int _state; + } + +} diff --git a/csharp/src/Ice/ConnectionI.cs b/csharp/src/Ice/ConnectionI.cs new file mode 100644 index 00000000000..581536eade7 --- /dev/null +++ b/csharp/src/Ice/ConnectionI.cs @@ -0,0 +1,2990 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Text; + using System.Threading; + using Ice.Instrumentation; + + public sealed class ConnectionI : + IceInternal.EventHandler, IceInternal.ResponseHandler, IceInternal.CancellationHandler, Connection + { + public interface StartCallback + { + void connectionStartCompleted(ConnectionI connection); + void connectionStartFailed(ConnectionI connection, LocalException ex); + } + + private class TimeoutCallback : IceInternal.TimerTask + { + public TimeoutCallback(ConnectionI connection) + { + _connection = connection; + } + + public void runTimerTask() + { + _connection.timedOut(); + } + + private Ice.ConnectionI _connection; + } + + public void start(StartCallback callback) + { + try + { + lock(this) + { + // + // The connection might already be closed if the communicator was destroyed. + // + if(_state >= StateClosed) + { + Debug.Assert(_exception != null); + throw _exception; + } + + if(!initialize(IceInternal.SocketOperation.None) || !validate(IceInternal.SocketOperation.None)) + { + _startCallback = callback; + return; + } + + // + // We start out in holding state. + // + setState(StateHolding); + } + } + catch(LocalException ex) + { + exception(ex); + callback.connectionStartFailed(this, _exception); + return; + } + + callback.connectionStartCompleted(this); + } + + public void startAndWait() + { + try + { + lock(this) + { + // + // The connection might already be closed if the communicator was destroyed. + // + if(_state >= StateClosed) + { + Debug.Assert(_exception != null); + throw _exception; + } + + if(!initialize(IceInternal.SocketOperation.None) || !validate(IceInternal.SocketOperation.None)) + { + // + // Wait for the connection to be validated. + // + while(_state <= StateNotValidated) + { + System.Threading.Monitor.Wait(this); + } + + if(_state >= StateClosing) + { + Debug.Assert(_exception != null); + throw _exception; + } + } + + // + // We start out in holding state. + // + setState(StateHolding); + } + } + catch(LocalException ex) + { + exception(ex); + waitUntilFinished(); + return; + } + } + + public void activate() + { + lock(this) + { + if(_state <= StateNotValidated) + { + return; + } + + if(_acmLastActivity > -1) + { + _acmLastActivity = IceInternal.Time.currentMonotonicTimeMillis(); + } + setState(StateActive); + } + } + + public void hold() + { + lock(this) + { + if(_state <= StateNotValidated) + { + return; + } + + setState(StateHolding); + } + } + + // DestructionReason. + public const int ObjectAdapterDeactivated = 0; + public const int CommunicatorDestroyed = 1; + + public void destroy(int reason) + { + lock(this) + { + switch(reason) + { + case ObjectAdapterDeactivated: + { + setState(StateClosing, new ObjectAdapterDeactivatedException()); + break; + } + + case CommunicatorDestroyed: + { + setState(StateClosing, new CommunicatorDestroyedException()); + break; + } + } + } + } + + public void close(bool force) + { + lock(this) + { + if(force) + { + setState(StateClosed, new ForcedCloseConnectionException()); + } + else + { + // + // If we do a graceful shutdown, then we wait until all + // outstanding requests have been completed. Otherwise, + // the CloseConnectionException will cause all outstanding + // requests to be retried, regardless of whether the + // server has processed them or not. + // + while(_asyncRequests.Count != 0) + { + System.Threading.Monitor.Wait(this); + } + + setState(StateClosing, new CloseConnectionException()); + } + } + } + + public bool isActiveOrHolding() + { + lock(this) + { + return _state > StateNotValidated && _state < StateClosing; + } + } + + public bool isFinished() + { + // + // We can use TryLock here, because as long as there are still + // threads operating in this connection object, connection + // destruction is considered as not yet finished. + // + if(!System.Threading.Monitor.TryEnter(this)) + { + return false; + } + + try + { + if(_state != StateFinished || _dispatchCount != 0) + { + return false; + } + + Debug.Assert(_state == StateFinished); + return true; + } + finally + { + System.Threading.Monitor.Exit(this); + } + } + + public void throwException() + { + lock(this) + { + if(_exception != null) + { + Debug.Assert(_state >= StateClosing); + throw _exception; + } + } + } + + public void waitUntilHolding() + { + lock(this) + { + while(_state < StateHolding || _dispatchCount > 0) + { + System.Threading.Monitor.Wait(this); + } + } + } + + public void waitUntilFinished() + { + lock(this) + { + // + // We wait indefinitely until the connection is finished and all + // outstanding requests are completed. Otherwise we couldn't + // guarantee that there are no outstanding calls when deactivate() + // is called on the servant locators. + // + while(_state < StateFinished || _dispatchCount > 0) + { + System.Threading.Monitor.Wait(this); + } + + Debug.Assert(_state == StateFinished); + + // + // Clear the OA. See bug 1673 for the details of why this is necessary. + // + _adapter = null; + } + } + + public void updateObserver() + { + lock(this) + { + if(_state < StateNotValidated || _state > StateClosed) + { + return; + } + + Debug.Assert(_instance.initializationData().observer != null); + _observer = _instance.initializationData().observer.getConnectionObserver(initConnectionInfo(), + _endpoint, + toConnectionState(_state), + _observer); + if(_observer != null) + { + _observer.attach(); + } + else + { + _writeStreamPos = -1; + _readStreamPos = -1; + } + } + } + + public void monitor(long now, IceInternal.ACMConfig acm) + { + lock(this) + { + if(_state != StateActive) + { + return; + } + + // + // We send a heartbeat if there was no activity in the last + // (timeout / 4) period. Sending a heartbeat sooner than + // really needed is safer to ensure that the receiver will + // receive in time the heartbeat. Sending the heartbeat if + // there was no activity in the last (timeout / 2) period + // isn't enough since monitor() is called only every (timeout + // / 2) period. + // + // Note that this doesn't imply that we are sending 4 heartbeats + // per timeout period because the monitor() method is sill only + // called every (timeout / 2) period. + // + if(acm.heartbeat == ACMHeartbeat.HeartbeatAlways || + (acm.heartbeat != ACMHeartbeat.HeartbeatOff && _writeStream.isEmpty() && + now >= (_acmLastActivity + acm.timeout / 4))) + { + if(acm.heartbeat != ACMHeartbeat.HeartbeatOnInvocation || _dispatchCount > 0) + { + heartbeat(); + } + } + + if(_readStream.size() > IceInternal.Protocol.headerSize || !_writeStream.isEmpty()) + { + // + // If writing or reading, nothing to do, the connection + // timeout will kick-in if writes or reads don't progress. + // This check is necessary because the actitivy timer is + // only set when a message is fully read/written. + // + return; + } + + if(acm.close != ACMClose.CloseOff && now >= (_acmLastActivity + acm.timeout)) + { + if(acm.close == ACMClose.CloseOnIdleForceful || + (acm.close != ACMClose.CloseOnIdle && (_asyncRequests.Count > 0))) + { + // + // Close the connection if we didn't receive a heartbeat in + // the last period. + // + setState(StateClosed, new ConnectionTimeoutException()); + } + else if(acm.close != ACMClose.CloseOnInvocation && + _dispatchCount == 0 && _batchRequestQueue.isEmpty() && + _asyncRequests.Count == 0) + { + // + // The connection is idle, close it. + // + setState(StateClosing, new ConnectionTimeoutException()); + } + } + } + } + + public bool sendAsyncRequest(IceInternal.OutgoingAsyncBase og, bool compress, bool response, + int batchRequestNum, out Ice.AsyncCallback sentCallback) + { + IceInternal.BasicStream os = og.getOs(); + + lock(this) + { + if(_exception != null) + { + // + // If the connection is closed before we even have a chance + // to send our request, we always try to send the request + // again. + // + throw new IceInternal.RetryException(_exception); + } + + Debug.Assert(_state > StateNotValidated); + Debug.Assert(_state < StateClosing); + + // + // Ensure the message isn't bigger than what we can send with the + // transport. + // + _transceiver.checkSendSize(os.getBuffer()); + + // + // Notify the request that it's cancelable with this connection. + // This will throw if the request is canceled. + // + og.cancelable(this); + + int requestId = 0; + if(response) + { + // + // Create a new unique request ID. + // + requestId = _nextRequestId++; + if(requestId <= 0) + { + _nextRequestId = 1; + requestId = _nextRequestId++; + } + + // + // Fill in the request ID. + // + os.pos(IceInternal.Protocol.headerSize); + os.writeInt(requestId); + } + else if(batchRequestNum > 0) + { + os.pos(IceInternal.Protocol.headerSize); + os.writeInt(batchRequestNum); + } + + og.attachRemoteObserver(initConnectionInfo(), _endpoint, requestId); + + bool sent; + try + { + OutgoingMessage msg = new OutgoingMessage(og, os, compress, requestId); + sent = sendMessage(msg); + sentCallback = msg.sentCallback; + } + catch(LocalException ex) + { + setState(StateClosed, ex); + Debug.Assert(_exception != null); + throw _exception; + } + + if(response) + { + // + // Add to the async requests map. + // + _asyncRequests[requestId] = og; + } + return sent; + } + } + + public IceInternal.BatchRequestQueue getBatchRequestQueue() + { + return _batchRequestQueue; + } + + public void flushBatchRequests() + { + end_flushBatchRequests(begin_flushBatchRequests()); + } + + public AsyncResult begin_flushBatchRequests() + { + return begin_flushBatchRequestsInternal(null, null); + } + + public AsyncResult begin_flushBatchRequests(AsyncCallback cb, object cookie) + { + return begin_flushBatchRequestsInternal(cb, cookie); + } + + public void end_flushBatchRequests(AsyncResult r) + { + IceInternal.ConnectionFlushBatch outAsync = + IceInternal.ConnectionFlushBatch.check(r, this, __flushBatchRequests_name); + outAsync.wait(); + } + + private const string __flushBatchRequests_name = "flushBatchRequests"; + + private AsyncResult begin_flushBatchRequestsInternal(AsyncCallback cb, object cookie) + { + IceInternal.ConnectionFlushBatch result = + new IceInternal.ConnectionFlushBatch(this, _communicator, _instance, __flushBatchRequests_name, cookie); + if(cb != null) + { + result.whenCompletedWithAsyncCallback(cb); + } + result.invoke(); + return result; + } + + public void setCallback(ConnectionCallback callback) + { + lock(this) + { + if(_state >= StateClosed) + { + if(callback != null) + { + _threadPool.dispatch(() => + { + try + { + callback.closed(this); + } + catch(System.Exception ex) + { + _logger.error("connection callback exception:\n" + ex + '\n' + _desc); + } + } , this); + } + } + else + { + _callback = callback; + } + } + } + + public void setACM(Optional<int> timeout, Optional<ACMClose> close, Optional<ACMHeartbeat> heartbeat) + { + lock(this) + { + if(_monitor == null || _state >= StateClosed) + { + return; + } + + if(_state == StateActive) + { + _monitor.remove(this); + } + _monitor = _monitor.acm(timeout, close, heartbeat); + + if(_monitor.getACM().timeout <= 0) + { + _acmLastActivity = -1; // Disable the recording of last activity. + } + else if(_state == StateActive && _acmLastActivity == -1) + { + _acmLastActivity = IceInternal.Time.currentMonotonicTimeMillis(); + } + + if(_state == StateActive) + { + _monitor.add(this); + } + } + } + + public ACM getACM() + { + lock(this) + { + return _monitor != null ? _monitor.getACM() : new ACM(0, ACMClose.CloseOff, ACMHeartbeat.HeartbeatOff); + } + } + + public void asyncRequestCanceled(IceInternal.OutgoingAsyncBase outAsync, Ice.LocalException ex) + { + // + // NOTE: This isn't called from a thread pool thread. + // + + lock(this) + { + if(_state >= StateClosed) + { + return; // The request has already been or will be shortly notified of the failure. + } + + LinkedListNode<OutgoingMessage> p; + for(p = _sendStreams.First; p != null; p = p.Next) + { + OutgoingMessage o = p.Value; + if(o.outAsync == outAsync) + { + if(o.requestId > 0) + { + _asyncRequests.Remove(o.requestId); + } + + if(ex is Ice.ConnectionTimeoutException) + { + setState(StateClosed, ex); + } + else + { + // + // If the request is being sent, don't remove it from the send streams, + // it will be removed once the sending is finished. + // + o.canceled(); + if(o != _sendStreams.First.Value) + { + _sendStreams.Remove(p); + } + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + } + return; + } + } + + if(outAsync is IceInternal.OutgoingAsync) + { + foreach(KeyValuePair<int, IceInternal.OutgoingAsyncBase> kvp in _asyncRequests) + { + if(kvp.Value == outAsync) + { + if(ex is Ice.ConnectionTimeoutException) + { + setState(StateClosed, ex); + } + else + { + _asyncRequests.Remove(kvp.Key); + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompletedAsync(cb); + } + } + return; + } + } + } + } + } + + public void sendResponse(int requestId, IceInternal.BasicStream os, byte compressFlag, bool amd) + { + lock(this) + { + Debug.Assert(_state > StateNotValidated); + + try + { + if(--_dispatchCount == 0) + { + if(_state == StateFinished) + { + reap(); + } + System.Threading.Monitor.PulseAll(this); + } + + if(_state >= StateClosed) + { + Debug.Assert(_exception != null); + throw _exception; + } + + sendMessage(new OutgoingMessage(os, compressFlag != 0, true)); + + if(_state == StateClosing && _dispatchCount == 0) + { + initiateShutdown(); + } + } + catch(LocalException ex) + { + setState(StateClosed, ex); + } + } + } + + public void sendNoResponse() + { + lock(this) + { + Debug.Assert(_state > StateNotValidated); + + try + { + if(--_dispatchCount == 0) + { + if(_state == StateFinished) + { + reap(); + } + System.Threading.Monitor.PulseAll(this); + } + + if(_state >= StateClosed) + { + Debug.Assert(_exception != null); + throw _exception; + } + + if(_state == StateClosing && _dispatchCount == 0) + { + initiateShutdown(); + } + } + catch(LocalException ex) + { + setState(StateClosed, ex); + } + } + } + + public bool systemException(int requestId, Ice.SystemException ex, bool amd) + { + return false; // System exceptions aren't marshalled. + } + + public void invokeException(int requestId, LocalException ex, int invokeNum, bool amd) + { + // + // Fatal exception while invoking a request. Since sendResponse/sendNoResponse isn't + // called in case of a fatal exception we decrement _dispatchCount here. + // + + lock(this) + { + setState(StateClosed, ex); + + if(invokeNum > 0) + { + Debug.Assert(_dispatchCount >= invokeNum); + _dispatchCount -= invokeNum; + if(_dispatchCount == 0) + { + if(_state == StateFinished) + { + reap(); + } + System.Threading.Monitor.PulseAll(this); + } + } + } + } + + public IceInternal.EndpointI endpoint() + { + return _endpoint; // No mutex protection necessary, _endpoint is immutable. + } + + public IceInternal.Connector connector() + { + return _connector; // No mutex protection necessary, _endpoint is immutable. + } + + public void setAdapter(ObjectAdapter adapter) + { + lock(this) + { + if(_state <= StateNotValidated || _state >= StateClosing) + { + return; + } + + _adapter = adapter; + + if(_adapter != null) + { + _servantManager = ((ObjectAdapterI) _adapter).getServantManager(); + if(_servantManager == null) + { + _adapter = null; + } + } + else + { + _servantManager = null; + } + + // + // We never change the thread pool with which we were initially + // registered, even if we add or remove an object adapter. + // + } + } + + public ObjectAdapter getAdapter() + { + lock(this) + { + return _adapter; + } + } + + public Endpoint getEndpoint() + { + return _endpoint; // No mutex protection necessary, _endpoint is immutable. + } + + public ObjectPrx createProxy(Identity ident) + { + // + // Create a reference and return a reverse proxy for this + // reference. + // + return _instance.proxyFactory().referenceToProxy(_instance.referenceFactory().create(ident, this)); + } + + // + // Operations from EventHandler + // + public override bool startAsync(int operation, IceInternal.AsyncCallback cb, ref bool completedSynchronously) + { + if(_state >= StateClosed) + { + return false; + } + + try + { + if((operation & IceInternal.SocketOperation.Write) != 0) + { + if(_observer != null) + { + observerStartWrite(_writeStream.getBuffer()); + } + + bool completed; + completedSynchronously = _transceiver.startWrite(_writeStream.getBuffer(), cb, this, out completed); + if(completed && _sendStreams.Count > 0) + { + // The whole message is written, assume it's sent now for at-most-once semantics. + _sendStreams.First.Value.isSent = true; + } + } + else if((operation & IceInternal.SocketOperation.Read) != 0) + { + if(_observer != null && !_readHeader) + { + observerStartRead(_readStream.getBuffer()); + } + + completedSynchronously = _transceiver.startRead(_readStream.getBuffer(), cb, this); + } + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + return false; + } + return true; + } + + public override bool finishAsync(int operation) + { + try + { + if((operation & IceInternal.SocketOperation.Write) != 0) + { + IceInternal.Buffer buf = _writeStream.getBuffer(); + int start = buf.b.position(); + _transceiver.finishWrite(buf); + if(_instance.traceLevels().network >= 3 && buf.b.position() != start) + { + StringBuilder s = new StringBuilder("sent "); + s.Append(buf.b.position() - start); + if(!_endpoint.datagram()) + { + s.Append(" of "); + s.Append(buf.b.limit() - start); + } + s.Append(" bytes via "); + s.Append(_endpoint.protocol()); + s.Append("\n"); + s.Append(ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + + if(_observer != null) + { + observerFinishWrite(_writeStream.getBuffer()); + } + } + else if((operation & IceInternal.SocketOperation.Read) != 0) + { + IceInternal.Buffer buf = _readStream.getBuffer(); + int start = buf.b.position(); + _transceiver.finishRead(buf); + if(_instance.traceLevels().network >= 3 && buf.b.position() != start) + { + StringBuilder s = new StringBuilder("received "); + if(_endpoint.datagram()) + { + s.Append(buf.b.limit()); + } + else + { + s.Append(buf.b.position() - start); + s.Append(" of "); + s.Append(buf.b.limit() - start); + } + s.Append(" bytes via "); + s.Append(_endpoint.protocol()); + s.Append("\n"); + s.Append(ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + + if(_observer != null && !_readHeader) + { + observerFinishRead(_readStream.getBuffer()); + } + } + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + } + return _state < StateClosed; + } + + public override void message(ref IceInternal.ThreadPoolCurrent current) + { + StartCallback startCB = null; + Queue<OutgoingMessage> sentCBs = null; + MessageInfo info = new MessageInfo(); + int dispatchCount = 0; + + IceInternal.ThreadPoolMessage msg = new IceInternal.ThreadPoolMessage(this); + try + { + lock(this) + { + if(!msg.startIOScope(ref current)) + { + return; + } + + if(_state >= StateClosed) + { + return; + } + + int readyOp = current.operation; + try + { + unscheduleTimeout(current.operation); + + int writeOp = IceInternal.SocketOperation.None; + int readOp = IceInternal.SocketOperation.None; + if((readyOp & IceInternal.SocketOperation.Write) != 0) + { + if(_observer != null) + { + observerStartWrite(_writeStream.getBuffer()); + } + writeOp = write(_writeStream.getBuffer()); + if(_observer != null && (writeOp & IceInternal.SocketOperation.Write) == 0) + { + observerFinishWrite(_writeStream.getBuffer()); + } + } + + while((readyOp & IceInternal.SocketOperation.Read) != 0) + { + IceInternal.Buffer buf = _readStream.getBuffer(); + + if(_observer != null && !_readHeader) + { + observerStartRead(buf); + } + + readOp = read(buf); + if((readOp & IceInternal.SocketOperation.Read) != 0) + { + break; + } + if(_observer != null && !_readHeader) + { + Debug.Assert(!buf.b.hasRemaining()); + observerFinishRead(buf); + } + + if(_readHeader) // Read header if necessary. + { + _readHeader = false; + + if(_observer != null) + { + _observer.receivedBytes(IceInternal.Protocol.headerSize); + } + + int pos = _readStream.pos(); + if(pos < IceInternal.Protocol.headerSize) + { + // + // This situation is possible for small UDP packets. + // + throw new Ice.IllegalMessageSizeException(); + } + + _readStream.pos(0); + byte[] m = new byte[4]; + m[0] = _readStream.readByte(); + m[1] = _readStream.readByte(); + m[2] = _readStream.readByte(); + m[3] = _readStream.readByte(); + if(m[0] != IceInternal.Protocol.magic[0] || m[1] != IceInternal.Protocol.magic[1] || + m[2] != IceInternal.Protocol.magic[2] || m[3] != IceInternal.Protocol.magic[3]) + { + Ice.BadMagicException ex = new Ice.BadMagicException(); + ex.badMagic = m; + throw ex; + } + + ProtocolVersion pv = new ProtocolVersion(); + pv.read__(_readStream); + IceInternal.Protocol.checkSupportedProtocol(pv); + EncodingVersion ev = new EncodingVersion(); + ev.read__(_readStream); + IceInternal.Protocol.checkSupportedProtocolEncoding(ev); + + _readStream.readByte(); // messageType + _readStream.readByte(); // compress + int size = _readStream.readInt(); + if(size < IceInternal.Protocol.headerSize) + { + throw new Ice.IllegalMessageSizeException(); + } + if(size > _messageSizeMax) + { + IceInternal.Ex.throwMemoryLimitException(size, _messageSizeMax); + } + if(size > _readStream.size()) + { + _readStream.resize(size, true); + } + _readStream.pos(pos); + } + + if(buf.b.hasRemaining()) + { + if(_endpoint.datagram()) + { + throw new Ice.DatagramLimitException(); // The message was truncated. + } + continue; + } + break; + } + + int newOp = readOp | writeOp; + readyOp &= ~newOp; + Debug.Assert(readyOp != 0 || newOp != 0); + + if(_state <= StateNotValidated) + { + if(newOp != 0) + { + // + // Wait for all the transceiver conditions to be + // satisfied before continuing. + // + scheduleTimeout(newOp); + _threadPool.update(this, current.operation, newOp); + return; + } + + if(_state == StateNotInitialized && !initialize(current.operation)) + { + return; + } + + if(_state <= StateNotValidated && !validate(current.operation)) + { + return; + } + + _threadPool.unregister(this, current.operation); + + // + // We start out in holding state. + // + setState(StateHolding); + if(_startCallback != null) + { + startCB = _startCallback; + _startCallback = null; + if(startCB != null) + { + ++dispatchCount; + } + } + } + else + { + Debug.Assert(_state <= StateClosingPending); + + // + // We parse messages first, if we receive a close + // connection message we won't send more messages. + // + if((readyOp & IceInternal.SocketOperation.Read) != 0) + { + newOp |= parseMessage(ref info); + dispatchCount += info.messageDispatchCount; + } + + if((readyOp & IceInternal.SocketOperation.Write) != 0) + { + newOp |= sendNextMessage(out sentCBs); + if(sentCBs != null) + { + ++dispatchCount; + } + } + + if(_state < StateClosed) + { + scheduleTimeout(newOp); + _threadPool.update(this, current.operation, newOp); + } + } + + if(_acmLastActivity > -1) + { + _acmLastActivity = IceInternal.Time.currentMonotonicTimeMillis(); + } + + if(dispatchCount == 0) + { + return; // Nothing to dispatch we're done! + } + + _dispatchCount += dispatchCount; + + msg.completed(ref current); + } + catch(DatagramLimitException) // Expected. + { + if(_warnUdp) + { + _logger.warning("maximum datagram size of " + _readStream.pos() + " exceeded"); + } + _readStream.resize(IceInternal.Protocol.headerSize, true); + _readStream.pos(0); + _readHeader = true; + return; + } + catch(SocketException ex) + { + setState(StateClosed, ex); + return; + } + catch(LocalException ex) + { + if(_endpoint.datagram()) + { + if(_warn) + { + String s = "datagram connection exception:\n" + ex + '\n' + _desc; + _logger.warning(s); + } + _readStream.resize(IceInternal.Protocol.headerSize, true); + _readStream.pos(0); + _readHeader = true; + } + else + { + setState(StateClosed, ex); + } + return; + } + + IceInternal.ThreadPoolCurrent c = current; + _threadPool.dispatch(() => + { + dispatch(startCB, sentCBs, info); + msg.destroy(ref c); + }, this); + } + } + finally + { + msg.finishIOScope(ref current); + } + + } + + private void dispatch(StartCallback startCB, Queue<OutgoingMessage> sentCBs, MessageInfo info) + { + int dispatchedCount = 0; + + // + // Notify the factory that the connection establishment and + // validation has completed. + // + if(startCB != null) + { + startCB.connectionStartCompleted(this); + ++dispatchedCount; + } + + // + // Notify AMI calls that the message was sent. + // + if(sentCBs != null) + { + foreach(OutgoingMessage m in sentCBs) + { + if(m.sentCallback != null) + { + m.outAsync.invokeSent(m.sentCallback); + } + if(m.receivedReply) + { + IceInternal.OutgoingAsync outAsync = (IceInternal.OutgoingAsync)m.outAsync; + Ice.AsyncCallback cb = outAsync.completed(); + if(cb != null) + { + outAsync.invokeCompleted(cb); + } + } + } + ++dispatchedCount; + } + + // + // Asynchronous replies must be handled outside the thread + // synchronization, so that nested calls are possible. + // + if(info.outAsync != null) + { + info.outAsync.invokeCompleted(info.completedCallback); + ++dispatchedCount; + } + + if(info.heartbeatCallback != null) + { + try + { + info.heartbeatCallback.heartbeat(this); + } + catch(System.Exception ex) + { + _logger.error("connection callback exception:\n" + ex + '\n' + _desc); + } + ++dispatchedCount; + } + + // + // Method invocation (or multiple invocations for batch messages) + // must be done outside the thread synchronization, so that nested + // calls are possible. + // + if(info.invokeNum > 0) + { + invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, + info.adapter); + + // + // Don't increase dispatchedCount, the dispatch count is + // decreased when the incoming reply is sent. + // + } + + // + // Decrease dispatch count. + // + if(dispatchedCount > 0) + { + lock(this) + { + _dispatchCount -= dispatchedCount; + if(_dispatchCount == 0) + { + // + // Only initiate shutdown if not already done. It + // might have already been done if the sent callback + // or AMI callback was dispatched when the connection + // was already in the closing state. + // + if(_state == StateClosing) + { + try + { + initiateShutdown(); + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + } + } + else if(_state == StateFinished) + { + reap(); + } + System.Threading.Monitor.PulseAll(this); + } + } + } + } + + public override void finished(ref IceInternal.ThreadPoolCurrent current) + { + lock(this) + { + Debug.Assert(_state == StateClosed); + unscheduleTimeout(IceInternal.SocketOperation.Read | IceInternal.SocketOperation.Write); + } + + // + // If there are no callbacks to call, we don't call ioCompleted() since we're not going + // to call code that will potentially block (this avoids promoting a new leader and + // unecessary thread creation, especially if this is called on shutdown). + // + if(_startCallback == null && _sendStreams.Count == 0 && _asyncRequests.Count == 0 && _callback == null) + { + finish(); + return; + } + + // + // Unlike C++/Java, this method is called from an IO thread of the .NET thread + // pool of from the communicator async IO thread. While it's fine to handle the + // non-blocking activity of the connection from these threads, the dispatching + // of the message must be taken care of by the Ice thread pool. + // + _threadPool.dispatch(finish, this); + } + + private void finish() + { + if(!_initialized) + { + if(_instance.traceLevels().network >= 2) + { + StringBuilder s = new StringBuilder("failed to "); + s.Append(_connector != null ? "establish" : "accept"); + s.Append(" "); + s.Append(_endpoint.protocol()); + s.Append(" connection\n"); + s.Append(ToString()); + s.Append("\n"); + s.Append(_exception); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + } + else + { + if(_instance.traceLevels().network >= 1) + { + StringBuilder s = new StringBuilder("closed "); + s.Append(_endpoint.protocol()); + s.Append(" connection\n"); + s.Append(ToString()); + + // + // Trace the cause of unexpected connection closures + // + if(!(_exception is CloseConnectionException || + _exception is ForcedCloseConnectionException || + _exception is ConnectionTimeoutException || + _exception is CommunicatorDestroyedException || + _exception is ObjectAdapterDeactivatedException)) + { + s.Append("\n"); + s.Append(_exception); + } + + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + } + + if(_startCallback != null) + { + _startCallback.connectionStartFailed(this, _exception); + _startCallback = null; + } + + if(_sendStreams.Count > 0) + { + if(!_writeStream.isEmpty()) + { + // + // Return the stream to the outgoing call. This is important for + // retriable AMI calls which are not marshalled again. + // + OutgoingMessage message = _sendStreams.First.Value; + _writeStream.swap(message.stream); + + // + // The current message might be sent but not yet removed from _sendStreams. If + // the response has been received in the meantime, we remove the message from + // _sendStreams to not call finished on a message which is already done. + // + if(message.isSent || message.receivedReply) + { + if(message.sent() && message.sentCallback != null) + { + message.outAsync.invokeSent(message.sentCallback); + } + if(message.receivedReply) + { + IceInternal.OutgoingAsync outAsync = (IceInternal.OutgoingAsync)message.outAsync; + Ice.AsyncCallback cb = outAsync.completed(); + if(cb != null) + { + outAsync.invokeCompleted(cb); + } + } + _sendStreams.RemoveFirst(); + } + } + + foreach(OutgoingMessage m in _sendStreams) + { + m.completed(_exception); + if(m.requestId > 0) // Make sure finished isn't called twice. + { + _asyncRequests.Remove(m.requestId); + } + } + _sendStreams.Clear(); + } + + foreach(IceInternal.OutgoingAsyncBase o in _asyncRequests.Values) + { + Ice.AsyncCallback cb = o.completed(_exception); + if(cb != null) + { + o.invokeCompleted(cb); + } + } + _asyncRequests.Clear(); + + if(_callback != null) + { + try + { + _callback.closed(this); + } + catch(System.Exception ex) + { + _logger.error("connection callback exception:\n" + ex + '\n' + _desc); + } + _callback = null; + } + + // + // This must be done last as this will cause waitUntilFinished() to return (and communicator + // objects such as the timer might be destroyed too). + // + lock(this) + { + setState(StateFinished); + + if(_dispatchCount == 0) + { + reap(); + } + } + } + + public override string ToString() + { + return _desc; // No mutex lock, _desc is immutable. + } + + public void timedOut() + { + lock(this) + { + if(_state <= StateNotValidated) + { + setState(StateClosed, new ConnectTimeoutException()); + } + else if(_state < StateClosing) + { + setState(StateClosed, new TimeoutException()); + } + else if(_state < StateClosed) + { + setState(StateClosed, new CloseTimeoutException()); + } + } + } + + public string type() + { + return _type; // No mutex lock, _type is immutable. + } + + public int timeout() + { + return _endpoint.timeout(); // No mutex protection necessary, _endpoint is immutable. + } + + public ConnectionInfo getInfo() + { + lock(this) + { + if(_state >= StateClosed) + { + throw _exception; + } + return initConnectionInfo(); + } + } + + public void setBufferSize(int rcvSize, int sndSize) + { + lock(this) + { + if(_state >= StateClosed) + { + throw _exception; + } + _transceiver.setBufferSize(rcvSize, sndSize); + _info = null; // Invalidate the cached connection info + } + } + + public string ice_toString_() + { + return ToString(); + } + + public void exception(LocalException ex) + { + lock(this) + { + setState(StateClosed, ex); + } + } + + static ConnectionI() + { + _compressionSupported = IceInternal.BasicStream.compressible(); + } + + internal ConnectionI(Communicator communicator, IceInternal.Instance instance, + IceInternal.ACMMonitor monitor, IceInternal.Transceiver transceiver, + IceInternal.Connector connector, IceInternal.EndpointI endpoint, ObjectAdapterI adapter) + { + _communicator = communicator; + _instance = instance; + _monitor = monitor; + _transceiver = transceiver; + _desc = transceiver.ToString(); + _type = transceiver.protocol(); + _connector = connector; + _endpoint = endpoint; + _adapter = adapter; + InitializationData initData = instance.initializationData(); + _logger = initData.logger; // Cached for better performance. + _traceLevels = instance.traceLevels(); // Cached for better performance. + _timer = instance.timer(); + _writeTimeout = new TimeoutCallback(this); + _writeTimeoutScheduled = false; + _readTimeout = new TimeoutCallback(this); + _readTimeoutScheduled = false; + _warn = initData.properties.getPropertyAsInt("Ice.Warn.Connections") > 0; + _warnUdp = initData.properties.getPropertyAsInt("Ice.Warn.Datagrams") > 0; + _cacheBuffers = instance.cacheMessageBuffers() > 0; + if(_monitor != null && _monitor.getACM().timeout > 0) + { + _acmLastActivity = IceInternal.Time.currentMonotonicTimeMillis(); + } + else + { + _acmLastActivity = -1; + } + _nextRequestId = 1; + _messageSizeMax = adapter != null ? adapter.messageSizeMax() : instance.messageSizeMax(); + _batchRequestQueue = new IceInternal.BatchRequestQueue(instance, _endpoint.datagram()); + _readStream = new IceInternal.BasicStream(instance, Util.currentProtocolEncoding); + _readHeader = false; + _readStreamPos = -1; + _writeStream = new IceInternal.BasicStream(instance, Util.currentProtocolEncoding); + _writeStreamPos = -1; + _dispatchCount = 0; + _state = StateNotInitialized; + + _compressionLevel = initData.properties.getPropertyAsIntWithDefault("Ice.Compression.Level", 1); + if(_compressionLevel < 1) + { + _compressionLevel = 1; + } + else if(_compressionLevel > 9) + { + _compressionLevel = 9; + } + + if(adapter != null) + { + _servantManager = adapter.getServantManager(); + } + + try + { + if(adapter != null) + { + _threadPool = adapter.getThreadPool(); + } + else + { + _threadPool = instance.clientThreadPool(); + } + _threadPool.initialize(this); + } + catch(LocalException) + { + throw; + } + catch(System.Exception ex) + { + throw new SyscallException(ex); + } + } + + private const int StateNotInitialized = 0; + private const int StateNotValidated = 1; + private const int StateActive = 2; + private const int StateHolding = 3; + private const int StateClosing = 4; + private const int StateClosingPending = 5; + private const int StateClosed = 6; + private const int StateFinished = 7; + + private void setState(int state, LocalException ex) + { + // + // If setState() is called with an exception, then only closed + // and closing states are permissible. + // + Debug.Assert(state >= StateClosing); + + if(_state == state) // Don't switch twice. + { + return; + } + + if(_exception == null) + { + // + // If we are in closed state, an exception must be set. + // + Debug.Assert(_state != StateClosed); + + _exception = ex; + + // + // We don't warn if we are not validated. + // + if(_warn && _validated) + { + // + // Don't warn about certain expected exceptions. + // + if(!(_exception is CloseConnectionException || + _exception is ForcedCloseConnectionException || + _exception is ConnectionTimeoutException || + _exception is CommunicatorDestroyedException || + _exception is ObjectAdapterDeactivatedException || + (_exception is ConnectionLostException && _state >= StateClosing))) + { + warning("connection exception", _exception); + } + } + } + + // + // We must set the new state before we notify requests of any + // exceptions. Otherwise new requests may retry on a + // connection that is not yet marked as closed or closing. + // + setState(state); + } + + private void setState(int state) + { + // + // We don't want to send close connection messages if the endpoint + // only supports oneway transmission from client to server. + // + if(_endpoint.datagram() && state == StateClosing) + { + state = StateClosed; + } + + // + // Skip graceful shutdown if we are destroyed before validation. + // + if(_state <= StateNotValidated && state == StateClosing) + { + state = StateClosed; + } + + if(_state == state) // Don't switch twice. + { + return; + } + + try + { + switch(state) + { + case StateNotInitialized: + { + Debug.Assert(false); + break; + } + + case StateNotValidated: + { + if(_state != StateNotInitialized) + { + Debug.Assert(_state == StateClosed); + return; + } + break; + } + + case StateActive: + { + // + // Can only switch from holding or not validated to + // active. + // + if(_state != StateHolding && _state != StateNotValidated) + { + return; + } + _threadPool.register(this, IceInternal.SocketOperation.Read); + break; + } + + case StateHolding: + { + // + // Can only switch from active or not validated to + // holding. + // + if(_state != StateActive && _state != StateNotValidated) + { + return; + } + if(_state == StateActive) + { + _threadPool.unregister(this, IceInternal.SocketOperation.Read); + } + break; + } + + case StateClosing: + case StateClosingPending: + { + // + // Can't change back from closing pending. + // + if(_state >= StateClosingPending) + { + return; + } + break; + } + + case StateClosed: + { + if(_state == StateFinished) + { + return; + } + + _batchRequestQueue.destroy(_exception); + _threadPool.finish(this); + _transceiver.close(); + break; + } + + case StateFinished: + { + Debug.Assert(_state == StateClosed); + _transceiver.destroy(); + _communicator = null; + break; + } + } + } + catch(Ice.LocalException ex) + { + _logger.error("unexpected connection exception:\n" + ex + "\n" + _transceiver.ToString()); + } + + // + // We only register with the connection monitor if our new state + // is StateActive. Otherwise we unregister with the connection + // monitor, but only if we were registered before, i.e., if our + // old state was StateActive. + // + if(_monitor != null) + { + if(state == StateActive) + { + if(_acmLastActivity > -1) + { + _acmLastActivity = IceInternal.Time.currentMonotonicTimeMillis(); + } + _monitor.add(this); + } + else if(_state == StateActive) + { + _monitor.remove(this); + } + } + + if(_instance.initializationData().observer != null) + { + ConnectionState oldState = toConnectionState(_state); + ConnectionState newState = toConnectionState(state); + if(oldState != newState) + { + _observer = _instance.initializationData().observer.getConnectionObserver(initConnectionInfo(), + _endpoint, + newState, + _observer); + if(_observer != null) + { + _observer.attach(); + } + else + { + _writeStreamPos = -1; + _readStreamPos = -1; + } + } + if(_observer != null && state == StateClosed && _exception != null) + { + if(!(_exception is CloseConnectionException || + _exception is ForcedCloseConnectionException || + _exception is ConnectionTimeoutException || + _exception is CommunicatorDestroyedException || + _exception is ObjectAdapterDeactivatedException || + (_exception is ConnectionLostException && _state >= StateClosing))) + { + _observer.failed(_exception.ice_name()); + } + } + } + _state = state; + + System.Threading.Monitor.PulseAll(this); + + if(_state == StateClosing && _dispatchCount == 0) + { + try + { + initiateShutdown(); + } + catch(LocalException ex) + { + setState(StateClosed, ex); + } + } + } + + private void initiateShutdown() + { + Debug.Assert(_state == StateClosing); + Debug.Assert(_dispatchCount == 0); + + if(_shutdownInitiated) + { + return; + } + _shutdownInitiated = true; + + if(!_endpoint.datagram()) + { + // + // Before we shut down, we send a close connection message. + // + IceInternal.BasicStream os = new IceInternal.BasicStream(_instance, Util.currentProtocolEncoding); + os.writeBlob(IceInternal.Protocol.magic); + Ice.Util.currentProtocol.write__(os); + Ice.Util.currentProtocolEncoding.write__(os); + os.writeByte(IceInternal.Protocol.closeConnectionMsg); + os.writeByte(_compressionSupported ? (byte)1 : (byte)0); + os.writeInt(IceInternal.Protocol.headerSize); // Message size. + + if(sendMessage(new OutgoingMessage(os, false, false))) + { + setState(StateClosingPending); + + // + // Notify the the transceiver of the graceful connection closure. + // + int op = _transceiver.closing(true, _exception); + if(op != 0) + { + scheduleTimeout(op); + _threadPool.register(this, op); + } + } + } + } + + private void heartbeat() + { + Debug.Assert(_state == StateActive); + + if(!_endpoint.datagram()) + { + IceInternal.BasicStream os = new IceInternal.BasicStream(_instance, Util.currentProtocolEncoding); + os.writeBlob(IceInternal.Protocol.magic); + Ice.Util.currentProtocol.write__(os); + Ice.Util.currentProtocolEncoding.write__(os); + os.writeByte(IceInternal.Protocol.validateConnectionMsg); + os.writeByte((byte)0); + os.writeInt(IceInternal.Protocol.headerSize); // Message size. + try + { + OutgoingMessage message = new OutgoingMessage(os, false, false); + sendMessage(message); + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + Debug.Assert(_exception != null); + } + } + } + + private bool initialize(int operation) + { + int s = _transceiver.initialize(_readStream.getBuffer(), _writeStream.getBuffer(), ref _hasMoreData); + if(s != IceInternal.SocketOperation.None) + { + scheduleTimeout(s); + _threadPool.update(this, operation, s); + return false; + } + + // + // Update the connection description once the transceiver is initialized. + // + _desc = _transceiver.ToString(); + _initialized = true; + setState(StateNotValidated); + + return true; + } + + private bool validate(int operation) + { + if(!_endpoint.datagram()) // Datagram connections are always implicitly validated. + { + if(_adapter != null) // The server side has the active role for connection validation. + { + if(_writeStream.size() == 0) + { + _writeStream.writeBlob(IceInternal.Protocol.magic); + Ice.Util.currentProtocol.write__(_writeStream); + Ice.Util.currentProtocolEncoding.write__(_writeStream); + _writeStream.writeByte(IceInternal.Protocol.validateConnectionMsg); + _writeStream.writeByte((byte)0); // Compression status (always zero for validate connection). + _writeStream.writeInt(IceInternal.Protocol.headerSize); // Message size. + IceInternal.TraceUtil.traceSend(_writeStream, _logger, _traceLevels); + _writeStream.prepareWrite(); + } + + if(_observer != null) + { + observerStartWrite(_writeStream.getBuffer()); + } + + if(_writeStream.pos() != _writeStream.size()) + { + int op = write(_writeStream.getBuffer()); + if(op != 0) + { + scheduleTimeout(op); + _threadPool.update(this, operation, op); + return false; + } + } + + if(_observer != null) + { + observerFinishWrite(_writeStream.getBuffer()); + } + } + else // The client side has the passive role for connection validation. + { + if(_readStream.size() == 0) + { + _readStream.resize(IceInternal.Protocol.headerSize, true); + _readStream.pos(0); + } + + if(_observer != null) + { + observerStartRead(_readStream.getBuffer()); + } + + if(_readStream.pos() != _readStream.size()) + { + int op = read(_readStream.getBuffer()); + if(op != 0) + { + scheduleTimeout(op); + _threadPool.update(this, operation, op); + return false; + } + } + + if(_observer != null) + { + observerFinishRead(_readStream.getBuffer()); + } + + Debug.Assert(_readStream.pos() == IceInternal.Protocol.headerSize); + _readStream.pos(0); + byte[] m = _readStream.readBlob(4); + if(m[0] != IceInternal.Protocol.magic[0] || m[1] != IceInternal.Protocol.magic[1] || + m[2] != IceInternal.Protocol.magic[2] || m[3] != IceInternal.Protocol.magic[3]) + { + BadMagicException ex = new BadMagicException(); + ex.badMagic = m; + throw ex; + } + + ProtocolVersion pv = new ProtocolVersion(); + pv.read__(_readStream); + IceInternal.Protocol.checkSupportedProtocol(pv); + + EncodingVersion ev = new EncodingVersion(); + ev.read__(_readStream); + IceInternal.Protocol.checkSupportedProtocolEncoding(ev); + + byte messageType = _readStream.readByte(); + if(messageType != IceInternal.Protocol.validateConnectionMsg) + { + throw new ConnectionNotValidatedException(); + } + _readStream.readByte(); // Ignore compression status for validate connection. + int size = _readStream.readInt(); + if(size != IceInternal.Protocol.headerSize) + { + throw new IllegalMessageSizeException(); + } + IceInternal.TraceUtil.traceRecv(_readStream, _logger, _traceLevels); + + _validated = true; + } + } + + _writeStream.resize(0, false); + _writeStream.pos(0); + + _readStream.resize(IceInternal.Protocol.headerSize, true); + _readStream.pos(0); + _readHeader = true; + + if(_instance.traceLevels().network >= 1) + { + StringBuilder s = new StringBuilder(); + if(_endpoint.datagram()) + { + s.Append("starting to "); + s.Append(_connector != null ? "send" : "receive"); + s.Append(" "); + s.Append(_endpoint.protocol()); + s.Append(" messages\n"); + s.Append(_transceiver.toDetailedString()); + } + else + { + s.Append(_connector != null ? "established" : "accepted"); + s.Append(" "); + s.Append(_endpoint.protocol()); + s.Append(" connection\n"); + s.Append(ToString()); + } + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + + return true; + } + + private int sendNextMessage(out Queue<OutgoingMessage> callbacks) + { + callbacks = null; + + if(_sendStreams.Count == 0) + { + return IceInternal.SocketOperation.None; + } + else if(_state == StateClosingPending && _writeStream.pos() == 0) + { + // Message wasn't sent, empty the _writeStream, we're not going to send more data. + OutgoingMessage message = _sendStreams.First.Value; + _writeStream.swap(message.stream); + return IceInternal.SocketOperation.None; + } + + Debug.Assert(!_writeStream.isEmpty() && _writeStream.pos() == _writeStream.size()); + try + { + while(true) + { + // + // Notify the message that it was sent. + // + OutgoingMessage message = _sendStreams.First.Value; + _writeStream.swap(message.stream); + if(message.sent()) + { + if(callbacks == null) + { + callbacks = new Queue<OutgoingMessage>(); + } + callbacks.Enqueue(message); + } + _sendStreams.RemoveFirst(); + + // + // If there's nothing left to send, we're done. + // + if(_sendStreams.Count == 0) + { + break; + } + + // + // If we are in the closed state or if the close is + // pending, don't continue sending. + // + // This can occur if parseMessage (called before + // sendNextMessage by message()) closes the connection. + // + if(_state >= StateClosingPending) + { + return IceInternal.SocketOperation.None; + } + + // + // Otherwise, prepare the next message stream for writing. + // + message = _sendStreams.First.Value; + Debug.Assert(!message.prepared); + IceInternal.BasicStream stream = message.stream; + + message.stream = doCompress(message.stream, message.compress); + message.stream.prepareWrite(); + message.prepared = true; + + if(message.outAsync != null) + { + IceInternal.TraceUtil.trace("sending asynchronous request", stream, _logger, _traceLevels); + } + else + { + IceInternal.TraceUtil.traceSend(stream, _logger, _traceLevels); + } + _writeStream.swap(message.stream); + + // + // Send the message. + // + if(_observer != null) + { + observerStartWrite(_writeStream.getBuffer()); + } + if(_writeStream.pos() != _writeStream.size()) + { + int op = write(_writeStream.getBuffer()); + if(op != 0) + { + return op; + } + } + if(_observer != null) + { + observerFinishWrite(_writeStream.getBuffer()); + } + } + + // + // If all the messages were sent and we are in the closing state, we schedule + // the close timeout to wait for the peer to close the connection. + // + if(_state == StateClosing && _shutdownInitiated) + { + setState(StateClosingPending); + int op = _transceiver.closing(true, _exception); + if(op != 0) + { + return op; + } + } + } + catch(Ice.LocalException ex) + { + setState(StateClosed, ex); + } + return IceInternal.SocketOperation.None; + } + + private bool sendMessage(OutgoingMessage message) + { + Debug.Assert(_state < StateClosed); + + if(_sendStreams.Count > 0) + { + message.adopt(); + _sendStreams.AddLast(message); + return false; + } + + // + // Attempt to send the message without blocking. If the send blocks, we use + // asynchronous I/O or we request the caller to call finishSendMessage() outside + // the synchronization. + // + + Debug.Assert(!message.prepared); + + IceInternal.BasicStream stream = message.stream; + + message.stream = doCompress(stream, message.compress); + message.stream.prepareWrite(); + message.prepared = true; + + if(message.outAsync != null) + { + IceInternal.TraceUtil.trace("sending asynchronous request", stream, _logger, _traceLevels); + } + else + { + IceInternal.TraceUtil.traceSend(stream, _logger, _traceLevels); + } + + // + // Send the message without blocking. + // + if(_observer != null) + { + observerStartWrite(message.stream.getBuffer()); + } + int op = write(message.stream.getBuffer()); + if(op == 0) + { + if(_observer != null) + { + observerFinishWrite(message.stream.getBuffer()); + } + + message.sent(); + + if(_acmLastActivity > -1) + { + _acmLastActivity = IceInternal.Time.currentMonotonicTimeMillis(); + } + return true; + } + + message.adopt(); + + _writeStream.swap(message.stream); + _sendStreams.AddLast(message); + scheduleTimeout(op); + _threadPool.register(this, op); + return false; + } + + private IceInternal.BasicStream doCompress(IceInternal.BasicStream uncompressed, bool compress) + { + if(_compressionSupported) + { + if(compress && uncompressed.size() >= 100) + { + // + // Do compression. + // + IceInternal.BasicStream cstream = null; + if(uncompressed.compress(ref cstream, IceInternal.Protocol.headerSize, _compressionLevel)) + { + // + // Set compression status. + // + cstream.pos(9); + cstream.writeByte((byte)2); + + // + // Write the size of the compressed stream into the header. + // + cstream.pos(10); + cstream.writeInt(cstream.size()); + + // + // Write the compression status and size of the compressed stream into the header of the + // uncompressed stream -- we need this to trace requests correctly. + // + uncompressed.pos(9); + uncompressed.writeByte((byte)2); + uncompressed.writeInt(cstream.size()); + + return cstream; + } + } + } + + uncompressed.pos(9); + uncompressed.writeByte((byte)((_compressionSupported && compress) ? 1 : 0)); + + // + // Not compressed, fill in the message size. + // + uncompressed.pos(10); + uncompressed.writeInt(uncompressed.size()); + + return uncompressed; + } + + private struct MessageInfo + { + public IceInternal.BasicStream stream; + public int invokeNum; + public int requestId; + public byte compress; + public IceInternal.ServantManager servantManager; + public ObjectAdapter adapter; + public IceInternal.OutgoingAsyncBase outAsync; + public Ice.AsyncCallback completedCallback; + public ConnectionCallback heartbeatCallback; + public int messageDispatchCount; + } + + private int parseMessage(ref MessageInfo info) + { + Debug.Assert(_state > StateNotValidated && _state < StateClosed); + + info.stream = new IceInternal.BasicStream(_instance, Util.currentProtocolEncoding); + _readStream.swap(info.stream); + _readStream.resize(IceInternal.Protocol.headerSize, true); + _readStream.pos(0); + _readHeader = true; + + Debug.Assert(info.stream.pos() == info.stream.size()); + + // + // Connection is validated on first message. This is only used by + // setState() to check wether or not we can print a connection + // warning (a client might close the connection forcefully if the + // connection isn't validated). + // + _validated = true; + + try + { + // + // The magic and version fields have already been checked. + // + info.stream.pos(8); + byte messageType = info.stream.readByte(); + info.compress = info.stream.readByte(); + if(info.compress == (byte)2) + { + if(_compressionSupported) + { + info.stream = info.stream.uncompress(IceInternal.Protocol.headerSize, _messageSizeMax); + } + else + { + FeatureNotSupportedException ex = new FeatureNotSupportedException(); + ex.unsupportedFeature = "Cannot uncompress compressed message: bzip2 DLL not found"; + throw ex; + } + } + info.stream.pos(IceInternal.Protocol.headerSize); + + switch(messageType) + { + case IceInternal.Protocol.closeConnectionMsg: + { + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); + if(_endpoint.datagram()) + { + if(_warn) + { + _logger.warning("ignoring close connection message for datagram connection:\n" + _desc); + } + } + else + { + setState(StateClosingPending, new CloseConnectionException()); + + // + // Notify the the transceiver of the graceful connection closure. + // + int op = _transceiver.closing(false, _exception); + if(op != 0) + { + return op; + } + setState(StateClosed); + } + break; + } + + case IceInternal.Protocol.requestMsg: + { + if(_state >= StateClosing) + { + IceInternal.TraceUtil.trace("received request during closing\n" + + "(ignored by server, client will retry)", info.stream, _logger, + _traceLevels); + } + else + { + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); + info.requestId = info.stream.readInt(); + info.invokeNum = 1; + info.servantManager = _servantManager; + info.adapter = _adapter; + ++info.messageDispatchCount; + } + break; + } + + case IceInternal.Protocol.requestBatchMsg: + { + if(_state >= StateClosing) + { + IceInternal.TraceUtil.trace("received batch request during closing\n" + + "(ignored by server, client will retry)", info.stream, _logger, + _traceLevels); + } + else + { + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); + info.invokeNum = info.stream.readInt(); + if(info.invokeNum < 0) + { + info.invokeNum = 0; + throw new UnmarshalOutOfBoundsException(); + } + info.servantManager = _servantManager; + info.adapter = _adapter; + info.messageDispatchCount += info.invokeNum; + } + break; + } + + case IceInternal.Protocol.replyMsg: + { + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); + info.requestId = info.stream.readInt(); + IceInternal.OutgoingAsyncBase outAsync = null; + if(_asyncRequests.TryGetValue(info.requestId, out outAsync)) + { + _asyncRequests.Remove(info.requestId); + + outAsync.getIs().swap(info.stream); + + // + // If we just received the reply for a request which isn't acknowledge as + // sent yet, we queue the reply instead of processing it right away. It + // will be processed once the write callback is invoked for the message. + // + OutgoingMessage message = _sendStreams.Count > 0 ? _sendStreams.First.Value : null; + if(message != null && message.outAsync == outAsync) + { + message.receivedReply = true; + } + else + { + info.completedCallback = outAsync.completed(); + if(info.completedCallback != null) + { + info.outAsync = outAsync; + ++info.messageDispatchCount; + } + } + System.Threading.Monitor.PulseAll(this); // Notify threads blocked in close(false) + } + break; + } + + case IceInternal.Protocol.validateConnectionMsg: + { + IceInternal.TraceUtil.traceRecv(info.stream, _logger, _traceLevels); + if(_callback != null) + { + info.heartbeatCallback = _callback; + ++info.messageDispatchCount; + } + break; + } + + default: + { + IceInternal.TraceUtil.trace("received unknown message\n(invalid, closing connection)", + info.stream, _logger, _traceLevels); + throw new UnknownMessageException(); + } + } + } + catch(LocalException ex) + { + if(_endpoint.datagram()) + { + if(_warn) + { + _logger.warning("datagram connection exception:\n" + ex.ToString() + "\n" + _desc); + } + } + else + { + setState(StateClosed, ex); + } + } + + return _state == StateHolding ? IceInternal.SocketOperation.None : IceInternal.SocketOperation.Read; + } + + private void invokeAll(IceInternal.BasicStream stream, int invokeNum, int requestId, byte compress, + IceInternal.ServantManager servantManager, ObjectAdapter adapter) + { + // + // Note: In contrast to other private or protected methods, this + // operation must be called *without* the mutex locked. + // + + IceInternal.Incoming inc = null; + try + { + while(invokeNum > 0) + { + // + // Prepare the invocation. + // + bool response = !_endpoint.datagram() && requestId != 0; + Debug.Assert(!response || invokeNum == 1); + + inc = getIncoming(adapter, response, compress, requestId); + + // + // Dispatch the invocation. + // + inc.invoke(servantManager, stream); + + --invokeNum; + + reclaimIncoming(inc); + inc = null; + } + + stream.clear(); + } + catch(LocalException ex) + { + invokeException(requestId, ex, invokeNum, false); + } + finally + { + if(inc != null) + { + reclaimIncoming(inc); + } + } + } + + private void scheduleTimeout(int status) + { + int timeout; + if(_state < StateActive) + { + IceInternal.DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideConnectTimeout) + { + timeout = defaultsAndOverrides.overrideConnectTimeoutValue; + } + else + { + timeout = _endpoint.timeout(); + } + } + else if(_state < StateClosingPending) + { + if(_readHeader) // No timeout for reading the header. + { + status &= ~IceInternal.SocketOperation.Read; + } + timeout = _endpoint.timeout(); + } + else + { + IceInternal.DefaultsAndOverrides defaultsAndOverrides = _instance.defaultsAndOverrides(); + if(defaultsAndOverrides.overrideCloseTimeout) + { + timeout = defaultsAndOverrides.overrideCloseTimeoutValue; + } + else + { + timeout = _endpoint.timeout(); + } + } + + if(timeout < 0) + { + return; + } + + if((status & IceInternal.SocketOperation.Read) != 0) + { + if(_readTimeoutScheduled) + { + _timer.cancel(_readTimeout); + } + _timer.schedule(_readTimeout, timeout); + _readTimeoutScheduled = true; + } + if((status & (IceInternal.SocketOperation.Write | IceInternal.SocketOperation.Connect)) != 0) + { + if(_writeTimeoutScheduled) + { + _timer.cancel(_writeTimeout); + } + _timer.schedule(_writeTimeout, timeout); + _writeTimeoutScheduled = true; + } + } + + private void unscheduleTimeout(int status) + { + if((status & IceInternal.SocketOperation.Read) != 0 && _readTimeoutScheduled) + { + _timer.cancel(_readTimeout); + _readTimeoutScheduled = false; + } + if((status & (IceInternal.SocketOperation.Write | IceInternal.SocketOperation.Connect)) != 0 && + _writeTimeoutScheduled) + { + _timer.cancel(_writeTimeout); + _writeTimeoutScheduled = false; + } + } + + private ConnectionInfo initConnectionInfo() + { + if(_info != null) + { + return _info; + } + + ConnectionInfo info = _transceiver.getInfo(); + info.connectionId = _endpoint.connectionId(); + info.adapterName = _adapter != null ? _adapter.getName() : ""; + info.incoming = _connector == null; + if(_state > StateNotInitialized) + { + _info = info; // Cache the connection information only if initialized. + } + return info; + } + + private void reap() + { + if(_monitor != null) + { + _monitor.reap(this); + } + if(_observer != null) + { + _observer.detach(); + } + } + + ConnectionState toConnectionState(int state) + { + return connectionStateMap[state]; + } + + private void warning(string msg, System.Exception ex) + { + _logger.warning(msg + ":\n" + ex + "\n" + _transceiver.ToString()); + } + + private void observerStartRead(IceInternal.Buffer buf) + { + if(_readStreamPos >= 0) + { + Debug.Assert(!buf.empty()); + _observer.receivedBytes(buf.b.position() - _readStreamPos); + } + _readStreamPos = buf.empty() ? -1 : buf.b.position(); + } + + private void observerFinishRead(IceInternal.Buffer buf) + { + if(_readStreamPos == -1) + { + return; + } + Debug.Assert(buf.b.position() >= _readStreamPos); + _observer.receivedBytes(buf.b.position() - _readStreamPos); + _readStreamPos = -1; + } + + private void observerStartWrite(IceInternal.Buffer buf) + { + if(_writeStreamPos >= 0) + { + Debug.Assert(!buf.empty()); + _observer.sentBytes(buf.b.position() - _writeStreamPos); + } + _writeStreamPos = buf.empty() ? -1 : buf.b.position(); + } + + private void observerFinishWrite(IceInternal.Buffer buf) + { + if(_writeStreamPos == -1) + { + return; + } + if(buf.b.position() > _writeStreamPos) + { + _observer.sentBytes(buf.b.position() - _writeStreamPos); + } + _writeStreamPos = -1; + } + + private IceInternal.Incoming getIncoming(ObjectAdapter adapter, bool response, byte compress, int requestId) + { + IceInternal.Incoming inc = null; + + if(_cacheBuffers) + { + lock(_incomingCacheMutex) + { + if(_incomingCache == null) + { + inc = new IceInternal.Incoming(_instance, this, this, adapter, response, compress, requestId); + } + else + { + inc = _incomingCache; + _incomingCache = _incomingCache.next; + inc.reset(_instance, this, this, adapter, response, compress, requestId); + inc.next = null; + } + } + } + else + { + inc = new IceInternal.Incoming(_instance, this, this, adapter, response, compress, requestId); + } + + return inc; + } + + private void reclaimIncoming(IceInternal.Incoming inc) + { + if(_cacheBuffers) + { + lock(_incomingCacheMutex) + { + inc.next = _incomingCache; + _incomingCache = inc; + // + // Clear references to Ice objects as soon as possible. + // + _incomingCache.reclaim(); + } + } + } + + private int read(IceInternal.Buffer buf) + { + int start = buf.b.position(); + int op = _transceiver.read(buf, ref _hasMoreData); + if(_instance.traceLevels().network >= 3 && buf.b.position() != start) + { + StringBuilder s = new StringBuilder("received "); + if(_endpoint.datagram()) + { + s.Append(buf.b.limit()); + } + else + { + s.Append(buf.b.position() - start); + s.Append(" of "); + s.Append(buf.b.limit() - start); + } + s.Append(" bytes via "); + s.Append(_endpoint.protocol()); + s.Append("\n"); + s.Append(ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + return op; + } + + private int write(IceInternal.Buffer buf) + { + int start = buf.b.position(); + int op = _transceiver.write(buf); + if(_instance.traceLevels().network >= 3 && buf.b.position() != start) + { + StringBuilder s = new StringBuilder("sent "); + s.Append(buf.b.position() - start); + if(!_endpoint.datagram()) + { + s.Append(" of "); + s.Append(buf.b.limit() - start); + } + s.Append(" bytes via "); + s.Append(_endpoint.protocol()); + s.Append("\n"); + s.Append(ToString()); + _instance.initializationData().logger.trace(_instance.traceLevels().networkCat, s.ToString()); + } + return op; + } + + private class OutgoingMessage + { + internal OutgoingMessage(IceInternal.BasicStream stream, bool compress, bool adopt) + { + this.stream = stream; + this.compress = compress; + this._adopt = adopt; + this.isSent = false; + this.requestId = 0; + } + + internal OutgoingMessage(IceInternal.OutgoingAsyncBase outAsync, IceInternal.BasicStream stream, + bool compress, int requestId) + { + this.stream = stream; + this.compress = compress; + this.outAsync = outAsync; + this.requestId = requestId; + this.isSent = false; + } + + internal void canceled() + { + Debug.Assert(outAsync != null); + outAsync = null; + } + + internal void adopt() + { + if(_adopt) + { + IceInternal.BasicStream stream = new IceInternal.BasicStream(this.stream.instance(), + Util.currentProtocolEncoding); + stream.swap(this.stream); + this.stream = stream; + _adopt = false; + } + } + + internal bool sent() + { + if(outAsync != null) + { + sentCallback = outAsync.sent(); + } + return sentCallback != null || receivedReply; + } + + internal void completed(LocalException ex) + { + if(outAsync != null) + { + Ice.AsyncCallback cb = outAsync.completed(ex); + if(cb != null) + { + outAsync.invokeCompleted(cb); + } + } + } + + internal IceInternal.BasicStream stream; + internal IceInternal.OutgoingAsyncBase outAsync; + internal bool receivedReply; + internal bool compress; + internal int requestId; + internal bool _adopt; + internal bool prepared; + internal bool isSent; + internal Ice.AsyncCallback sentCallback = null; + } + + private Communicator _communicator; + private IceInternal.Instance _instance; + private IceInternal.ACMMonitor _monitor; + private IceInternal.Transceiver _transceiver; + private string _desc; + private string _type; + private IceInternal.Connector _connector; + private IceInternal.EndpointI _endpoint; + + private ObjectAdapter _adapter; + private IceInternal.ServantManager _servantManager; + + private Logger _logger; + private IceInternal.TraceLevels _traceLevels; + private IceInternal.ThreadPool _threadPool; + + private IceInternal.Timer _timer; + private IceInternal.TimerTask _writeTimeout; + private bool _writeTimeoutScheduled; + private IceInternal.TimerTask _readTimeout; + private bool _readTimeoutScheduled; + + private StartCallback _startCallback = null; + + private bool _warn; + private bool _warnUdp; + + private long _acmLastActivity; + + private int _compressionLevel; + + private int _nextRequestId; + + private Dictionary<int, IceInternal.OutgoingAsyncBase> _asyncRequests = + new Dictionary<int, IceInternal.OutgoingAsyncBase>(); + + private LocalException _exception; + + private readonly int _messageSizeMax; + private IceInternal.BatchRequestQueue _batchRequestQueue; + + private LinkedList<OutgoingMessage> _sendStreams = new LinkedList<OutgoingMessage>(); + + private IceInternal.BasicStream _readStream; + private bool _readHeader; + private IceInternal.BasicStream _writeStream; + + private ConnectionObserver _observer; + private int _readStreamPos; + private int _writeStreamPos; + + private int _dispatchCount; + + private int _state; // The current state. + private bool _shutdownInitiated = false; + private bool _initialized = false; + private bool _validated = false; + + private IceInternal.Incoming _incomingCache; + private object _incomingCacheMutex = new object(); + + private static bool _compressionSupported; + + private bool _cacheBuffers; + + private Ice.ConnectionInfo _info; + + private Ice.ConnectionCallback _callback; + + private static ConnectionState[] connectionStateMap = new ConnectionState[] { + ConnectionState.ConnectionStateValidating, // StateNotInitialized + ConnectionState.ConnectionStateValidating, // StateNotValidated + ConnectionState.ConnectionStateActive, // StateActive + ConnectionState.ConnectionStateHolding, // StateHolding + ConnectionState.ConnectionStateClosing, // StateClosing + ConnectionState.ConnectionStateClosing, // StateClosingPending + ConnectionState.ConnectionStateClosed, // StateClosed + ConnectionState.ConnectionStateClosed, // StateFinished + }; + } +} diff --git a/csharp/src/Ice/ConnectionRequestHandler.cs b/csharp/src/Ice/ConnectionRequestHandler.cs new file mode 100644 index 00000000000..8ddcca74e02 --- /dev/null +++ b/csharp/src/Ice/ConnectionRequestHandler.cs @@ -0,0 +1,77 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using Ice.Instrumentation; + +namespace IceInternal +{ + public class ConnectionRequestHandler : RequestHandler + { + public RequestHandler update(RequestHandler previousHandler, RequestHandler newHandler) + { + try + { + if(previousHandler == this) + { + return newHandler; + } + else if(previousHandler.getConnection() == _connection) + { + // + // If both request handlers point to the same connection, we also + // update the request handler. See bug ICE-5489 for reasons why + // this can be useful. + // + return newHandler; + } + } + catch(Ice.Exception) + { + // Ignore + } + return this; + } + + public bool sendAsyncRequest(ProxyOutgoingAsyncBase outAsync, out Ice.AsyncCallback sentCallback) + { + return outAsync.invokeRemote(_connection, _compress, _response, out sentCallback); + } + + public void asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex) + { + _connection.asyncRequestCanceled(outAsync, ex); + } + + public Reference getReference() + { + return _reference; + } + + public Ice.ConnectionI getConnection() + { + return _connection; + } + + public ConnectionRequestHandler(Reference @ref, Ice.ConnectionI connection, bool compress) + { + _reference = @ref; + _response = _reference.getMode() == Reference.Mode.ModeTwoway; + _connection = connection; + _compress = compress; + } + + private Reference _reference; + private bool _response; + private Ice.ConnectionI _connection; + private bool _compress; + } +} diff --git a/csharp/src/Ice/Connector.cs b/csharp/src/Ice/Connector.cs new file mode 100644 index 00000000000..84046ad4dcd --- /dev/null +++ b/csharp/src/Ice/Connector.cs @@ -0,0 +1,27 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Net.Sockets; + + public interface Connector + { + // + // Create a transceiver without blocking. The transceiver may not be fully connected + // until its initialize method is called. + // + Transceiver connect(); + + short type(); + } + +} diff --git a/csharp/src/Ice/DefaultsAndOverrides.cs b/csharp/src/Ice/DefaultsAndOverrides.cs new file mode 100644 index 00000000000..cfe64b77076 --- /dev/null +++ b/csharp/src/Ice/DefaultsAndOverrides.cs @@ -0,0 +1,229 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Net; +using System.Text; + +namespace IceInternal +{ + + public sealed class DefaultsAndOverrides + { + internal DefaultsAndOverrides(Ice.Properties properties, Ice.Logger logger) + { + string val; + + defaultProtocol = properties.getPropertyWithDefault("Ice.Default.Protocol", "tcp"); + + val = properties.getProperty("Ice.Default.Host"); + if(val.Length != 0) + { + defaultHost = val; + } + else + { + defaultHost = null; + } + + val = properties.getProperty("Ice.Default.SourceAddress"); + if(val.Length > 0) + { + defaultSourceAddress = Network.getNumericAddress(val); + if(defaultSourceAddress == null) + { + throw new Ice.InitializationException("invalid IP address set for Ice.Default.SourceAddress: `" + + val + "'"); + } + } + else + { + defaultSourceAddress = null; + } + + val = properties.getProperty("Ice.Override.Timeout"); + if(val.Length > 0) + { + overrideTimeout = true; + overrideTimeoutValue = properties.getPropertyAsInt("Ice.Override.Timeout"); + if(overrideTimeoutValue < 1 && overrideTimeoutValue != -1) + { + overrideTimeoutValue = -1; + StringBuilder msg = new StringBuilder("invalid value for Ice.Override.Timeout `"); + msg.Append(properties.getProperty("Ice.Override.Timeout")); + msg.Append("': defaulting to -1"); + logger.warning(msg.ToString()); + } + } + else + { + overrideTimeout = false; + overrideTimeoutValue = -1; + } + + val = properties.getProperty("Ice.Override.ConnectTimeout"); + if(val.Length > 0) + { + overrideConnectTimeout = true; + overrideConnectTimeoutValue = properties.getPropertyAsInt("Ice.Override.ConnectTimeout"); + if(overrideConnectTimeoutValue < 1 && overrideConnectTimeoutValue != -1) + { + overrideConnectTimeoutValue = -1; + StringBuilder msg = new StringBuilder("invalid value for Ice.Override.ConnectTimeout `"); + msg.Append(properties.getProperty("Ice.Override.ConnectTimeout")); + msg.Append("': defaulting to -1"); + logger.warning(msg.ToString()); + } + } + else + { + overrideConnectTimeout = false; + overrideConnectTimeoutValue = -1; + } + + val = properties.getProperty("Ice.Override.CloseTimeout"); + if(val.Length > 0) + { + overrideCloseTimeout = true; + overrideCloseTimeoutValue = properties.getPropertyAsInt("Ice.Override.CloseTimeout"); + if(overrideCloseTimeoutValue < 1 && overrideCloseTimeoutValue != -1) + { + overrideCloseTimeoutValue = -1; + StringBuilder msg = new StringBuilder("invalid value for Ice.Override.CloseTimeout `"); + msg.Append(properties.getProperty("Ice.Override.CloseTimeout")); + msg.Append("': defaulting to -1"); + logger.warning(msg.ToString()); + } + } + else + { + overrideCloseTimeout = false; + overrideCloseTimeoutValue = -1; + } + +#if COMPACT + overrideCompress = false; + overrideCompressValue = false; +#else + val = properties.getProperty("Ice.Override.Compress"); + if(val.Length > 0) + { + overrideCompress = true; + overrideCompressValue = properties.getPropertyAsInt("Ice.Override.Compress") > 0; + if(!BasicStream.compressible() && overrideCompressValue) + { + string lib = AssemblyUtil.runtime_ == AssemblyUtil.Runtime.Mono ? "bzip2 library" : "bzip2.dll"; + Console.Error.WriteLine("warning: " + lib + " not found, Ice.Override.Compress ignored."); + overrideCompressValue = false; + } + } + else + { + overrideCompress = !BasicStream.compressible(); + overrideCompressValue = false; + } +#endif + + val = properties.getProperty("Ice.Override.Secure"); + if(val.Length > 0) + { + overrideSecure = true; + overrideSecureValue = properties.getPropertyAsInt("Ice.Override.Secure") > 0; + } + else + { + overrideSecure = false; + overrideSecureValue = false; + } + + defaultCollocationOptimization = + properties.getPropertyAsIntWithDefault("Ice.Default.CollocationOptimized", 1) > 0; + + val = properties.getPropertyWithDefault("Ice.Default.EndpointSelection", "Random"); + if(val.Equals("Random")) + { + defaultEndpointSelection = Ice.EndpointSelectionType.Random; + } + else if(val.Equals("Ordered")) + { + defaultEndpointSelection = Ice.EndpointSelectionType.Ordered; + } + else + { + Ice.EndpointSelectionTypeParseException ex = new Ice.EndpointSelectionTypeParseException(); + ex.str = "illegal value `" + val + "'; expected `Random' or `Ordered'"; + throw ex; + } + + defaultTimeout = properties.getPropertyAsIntWithDefault("Ice.Default.Timeout", 60000); + if(defaultTimeout < 1 && defaultTimeout != -1) + { + defaultTimeout = 60000; + StringBuilder msg = new StringBuilder("invalid value for Ice.Default.Timeout `"); + msg.Append(properties.getProperty("Ice.Default.Timeout")); + msg.Append("': defaulting to 60000"); + logger.warning(msg.ToString()); + } + + defaultLocatorCacheTimeout = properties.getPropertyAsIntWithDefault("Ice.Default.LocatorCacheTimeout", -1); + if(defaultLocatorCacheTimeout < -1) + { + defaultLocatorCacheTimeout = -1; + StringBuilder msg = new StringBuilder("invalid value for Ice.Default.LocatorCacheTimeout `"); + msg.Append(properties.getProperty("Ice.Default.LocatorCacheTimeout")); + msg.Append("': defaulting to -1"); + logger.warning(msg.ToString()); + } + + defaultInvocationTimeout = properties.getPropertyAsIntWithDefault("Ice.Default.InvocationTimeout", -1); + if(defaultInvocationTimeout < 1 && defaultInvocationTimeout != -1 && defaultInvocationTimeout != -2) + { + defaultInvocationTimeout = -1; + StringBuilder msg = new StringBuilder("invalid value for Ice.Default.InvocationTimeout `"); + msg.Append(properties.getProperty("Ice.Default.InvocationTimeout")); + msg.Append("': defaulting to -1"); + logger.warning(msg.ToString()); + } + + defaultPreferSecure = properties.getPropertyAsIntWithDefault("Ice.Default.PreferSecure", 0) > 0; + + val = properties.getPropertyWithDefault("Ice.Default.EncodingVersion", + Ice.Util.encodingVersionToString(Ice.Util.currentEncoding)); + defaultEncoding = Ice.Util.stringToEncodingVersion(val); + Protocol.checkSupportedEncoding(defaultEncoding); + + bool slicedFormat = properties.getPropertyAsIntWithDefault("Ice.Default.SlicedFormat", 0) > 0; + defaultFormat = slicedFormat ? Ice.FormatType.SlicedFormat : Ice.FormatType.CompactFormat; + } + + public string defaultHost; + public EndPoint defaultSourceAddress; + public string defaultProtocol; + public bool defaultCollocationOptimization; + public Ice.EndpointSelectionType defaultEndpointSelection; + public int defaultTimeout; + public int defaultLocatorCacheTimeout; + public int defaultInvocationTimeout; + public bool defaultPreferSecure; + public Ice.EncodingVersion defaultEncoding; + public Ice.FormatType defaultFormat; + + public bool overrideTimeout; + public int overrideTimeoutValue; + public bool overrideConnectTimeout; + public int overrideConnectTimeoutValue; + public bool overrideCloseTimeout; + public int overrideCloseTimeoutValue; + public bool overrideCompress; + public bool overrideCompressValue; + public bool overrideSecure; + public bool overrideSecureValue; + } + +} diff --git a/csharp/src/Ice/DictionaryBase.cs b/csharp/src/Ice/DictionaryBase.cs new file mode 100644 index 00000000000..ed3b8f28d1a --- /dev/null +++ b/csharp/src/Ice/DictionaryBase.cs @@ -0,0 +1,355 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections.Generic; + +namespace IceInternal +{ +#if !SILVERLIGHT + [Serializable] +#endif + public abstract class DictionaryBase<KT, VT> : System.Collections.IDictionary + { + protected Dictionary<KT, VT> dict_; + + public DictionaryBase() + { + dict_ = new Dictionary<KT, VT>(); + } + + public int Count + { + get + { + return dict_.Count; + } + } + + public void Clear() + { + dict_.Clear(); + } + + public void CopyTo(System.Array a__, int index) + { + if(a__ == null) + { + throw new ArgumentNullException("a__", "Cannot copy to null array"); + } + if(index < 0) + { + throw new ArgumentException("Array index cannot be less than zero", "index"); + } + if(index >= a__.Length) + { + throw new ArgumentException("Array index must less than array length"); + } + if(dict_.Count > a__.Length - index) + { + throw new ArgumentException("Insufficient room in target array beyond index"); + } + if(a__.Rank > 1) + { + throw new ArgumentException("Cannot copy to multidimensional array", "a__"); + } + Type t = a__.GetType().GetElementType(); + if(!t.IsAssignableFrom(typeof(System.Collections.DictionaryEntry))) + { + throw new ArgumentException("Cannot assign DictionaryEntry to target array", "a__"); + } + + IEnumerator<KeyValuePair<KT, VT>> e = dict_.GetEnumerator(); + while(e.MoveNext()) + { + a__.SetValue(new System.Collections.DictionaryEntry(e.Current.Key, e.Current.Value), index++); + } + } + + public override bool Equals(object other) + { + if(object.ReferenceEquals(this, other)) + { + return true; + } + if(other == null) + { + return false; + } + + try + { + DictionaryBase<KT, VT> d2 = (DictionaryBase<KT, VT>)other; + + if(dict_.Count != d2.dict_.Count) + { + return false; + } + if(Count == 0) + { + return true; + } + + // + // Compare both sets of keys. Keys are unique and non-null. + // + Dictionary<KT, VT>.KeyCollection keys1 = dict_.Keys; + Dictionary<KT, VT>.KeyCollection keys2 = d2.dict_.Keys; + KT[] ka1 = new KT[dict_.Count]; + KT[] ka2 = new KT[d2.dict_.Count]; + keys1.CopyTo(ka1, 0); + keys2.CopyTo(ka2, 0); + Array.Sort(ka1); + Array.Sort(ka2); + + for(int i = 0; i < ka1.Length; ++i) + { + if(!Equals(ka1[i], ka2[i])) + { + return false; + } + if(!Equals(dict_[ka1[i]], d2.dict_[ka1[i]])) + { + return false; + } + } + + return true; + } + catch(System.Exception) + { + return false; + } + } + + public static bool operator==(DictionaryBase<KT, VT> lhs__, DictionaryBase<KT, VT> rhs__) + { + return Equals(lhs__, rhs__); + } + + public static bool operator!=(DictionaryBase<KT, VT> lhs__, DictionaryBase<KT, VT> rhs__) + { + return !Equals(lhs__, rhs__); + } + + public override int GetHashCode() + { + int h = 5381; + foreach(KeyValuePair<KT, VT> kvp in dict_) + { + IceInternal.HashUtil.hashAdd(ref h, kvp.Key); + IceInternal.HashUtil.hashAdd(ref h, kvp.Value); + } + return h; + } + + public class CEnumerator : System.Collections.IDictionaryEnumerator + { + public CEnumerator(IEnumerator<KeyValuePair<KT, VT>> e) + { + _e = e; + } + + public bool MoveNext() + { + return _e.MoveNext(); + } + + public object Current + { + get + { + return new System.Collections.DictionaryEntry(_e.Current.Key, _e.Current.Value); + } + } + + public System.Collections.DictionaryEntry Entry + { + get + { + return new System.Collections.DictionaryEntry(_e.Current.Key, _e.Current.Value); + } + } + + public object Key + { + get + { + return _e.Current.Key; + } + } + + public object Value + { + get + { + return _e.Current.Value; + } + } + + public void Reset() + { + _e.Reset(); + } + + private IEnumerator<KeyValuePair<KT, VT>> _e; + } + + public System.Collections.IEnumerator GetEnumerator() + { + return new CEnumerator(dict_.GetEnumerator()); + } + + public bool IsFixedSize + { + get + { + return false; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + public bool IsSynchronized + { + get + { + return false; + } + } + + public object SyncRoot + { + get + { + return this; + } + } + + public VT this[KT key] + { + get + { + return dict_[key]; + } + set + { + dict_[key] = value; + } + } + + public System.Collections.ICollection Keys + { + get + { + return dict_.Keys; + } + } + + public System.Collections.ICollection Values + { + get + { + return dict_.Values; + } + } + + public void Add(KT key, VT value) + { + try + { + dict_.Add(key, value); + } + catch(ArgumentException) + { + // Ignore. + } + } + + public void Remove(KT key) + { + dict_.Remove(key); + } + + public bool Contains(KT key) + { + return dict_.ContainsKey(key); + } + + public void Add(object key, object value) + { + checkKeyType(key); + checkValueType(value); + Add((KT)key, (VT)value); + } + + public void Remove(object key) + { + checkKeyType(key); + Remove((KT)key); + } + + public bool Contains(object key) + { + return dict_.ContainsKey((KT)key); + } + + System.Collections.IDictionaryEnumerator System.Collections.IDictionary.GetEnumerator() + { + return new CEnumerator(dict_.GetEnumerator()); + } + + public object this[object key] + { + get + { + checkKeyType(key); + return dict_[(KT)key]; + } + set + { + checkKeyType(key); + checkValueType(value); + dict_[(KT)key] = (VT)value; + } + } + + private void checkKeyType(object o) + { + if(o != null && !(o is KT)) + { + throw new ArgumentException("Cannot use a key of type " + o.GetType().ToString() + + " for a dictionary with key type " + typeof(KT).ToString()); + } + } + + private void checkValueType(object o) + { + if(o != null && !(o is KT)) + { + throw new ArgumentException("Cannot use a value of type " + o.GetType().ToString() + + " for a dictionary with value type " + typeof(VT).ToString()); + } + } + } +} + +namespace Ice +{ + [Obsolete("This class is deprecated.")] + public abstract class DictionaryBase<KT, VT> : IceInternal.DictionaryBase<KT, VT> + { + } +} diff --git a/csharp/src/Ice/DispatchInterceptor.cs b/csharp/src/Ice/DispatchInterceptor.cs new file mode 100644 index 00000000000..d5a1cc9260f --- /dev/null +++ b/csharp/src/Ice/DispatchInterceptor.cs @@ -0,0 +1,68 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Diagnostics; + +namespace Ice +{ + /// <summary> + /// Base class that allows a server intercept incoming requests. + /// The application must derive a concrete class from DispatchInterceptor + /// that implements the DispatchInterceptor.dispatch operation. An instance of this derived + /// class can be registered with an object adapter like any other servant. + /// A dispatch interceptor is useful particularly to automatically retry requests + /// that have failed due to a recoverable error condition. + /// </summary> + public abstract class DispatchInterceptor : Ice.ObjectImpl + { + /// <summary> + /// Called by the Ice run time to dispatch an incoming request. The implementation + /// of <code>dispatch</code> must dispatch the request to the actual servant. + /// </summary> + /// <param name="request">The details of the incoming request.</param> + /// <returns>For synchronous dispatch, the return value must be whatever is + /// returned ice_dispatch. For asynchronous dispatch, the return + /// value must be DispatchAsync.</returns> + public abstract DispatchStatus + dispatch(Request request); + + public override DispatchStatus + dispatch__(IceInternal.Incoming inc, Current current) + { + try + { + DispatchStatus status = dispatch(inc); + if(status != DispatchStatus.DispatchAsync) + { + // + // Make sure 'inc' owns the connection etc. + // + inc.killAsync(); + } + return status; + } + catch(ResponseSentException) + { + return DispatchStatus.DispatchAsync; + } + catch(System.Exception) + { + try + { + inc.killAsync(); + throw; + } + catch(ResponseSentException) + { + return DispatchStatus.DispatchAsync; + } + } + } + } +} diff --git a/csharp/src/Ice/EndpointFactory.cs b/csharp/src/Ice/EndpointFactory.cs new file mode 100644 index 00000000000..d1fd14312fa --- /dev/null +++ b/csharp/src/Ice/EndpointFactory.cs @@ -0,0 +1,25 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Collections.Generic; + + public interface EndpointFactory + { + short type(); + string protocol(); + EndpointI create(List<string> args, bool oaEndpoint); + EndpointI read(BasicStream s); + void destroy(); + + EndpointFactory clone(ProtocolInstance instance); + } + +} diff --git a/csharp/src/Ice/EndpointFactoryManager.cs b/csharp/src/Ice/EndpointFactoryManager.cs new file mode 100644 index 00000000000..1f52ffeb605 --- /dev/null +++ b/csharp/src/Ice/EndpointFactoryManager.cs @@ -0,0 +1,203 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections.Generic; + using System.Diagnostics; + using System.Text.RegularExpressions; + + public sealed class EndpointFactoryManager + { + internal EndpointFactoryManager(Instance instance) + { + instance_ = instance; + _factories = new List<EndpointFactory>(); + } + + public void add(EndpointFactory factory) + { + lock(this) + { + for(int i = 0; i < _factories.Count; i++) + { + EndpointFactory f = (EndpointFactory)_factories[i]; + if(f.type() == factory.type()) + { + Debug.Assert(false); + } + } + _factories.Add(factory); + } + } + + public EndpointFactory get(short type) + { + lock(this) + { + for(int i = 0; i < _factories.Count; i++) + { + EndpointFactory f = (EndpointFactory)_factories[i]; + if(f.type() == type) + { + return f; + } + } + return null; + } + } + + public EndpointI create(string str, bool oaEndpoint) + { + string[] arr = IceUtilInternal.StringUtil.splitString(str, " \t\r\n"); + if(arr == null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "mismatched quote"; + throw e; + } + + if(arr.Length == 0) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "value has no non-whitespace characters"; + throw e; + } + + List<string> v = new List<string>(arr); + string protocol = v[0]; + v.RemoveAt(0); + + if(protocol.Equals("default")) + { + protocol = instance_.defaultsAndOverrides().defaultProtocol; + } + + EndpointFactory factory = null; + + lock(this) + { + for(int i = 0; i < _factories.Count; i++) + { + EndpointFactory f = _factories[i]; + if(f.protocol().Equals(protocol)) + { + factory = f; + } + } + } + + if(factory != null) + { + EndpointI e = factory.create(v, oaEndpoint); + if(v.Count > 0) + { + Ice.EndpointParseException ex = new Ice.EndpointParseException(); + ex.str = "unrecognized argument `" + v[0] + "' in endpoint `" + str + "'"; + throw ex; + } + return e; + + // Code below left in place for debugging. + + /* + EndpointI e = f.create(s.Substring(m.Index + m.Length), oaEndpoint); + BasicStream bs = new BasicStream(instance_, true); + e.streamWrite(bs); + Buffer buf = bs.getBuffer(); + buf.b.position(0); + short type = bs.readShort(); + EndpointI ue = new IceInternal.OpaqueEndpointI(type, bs); + System.Console.Error.WriteLine("Normal: " + e); + System.Console.Error.WriteLine("Opaque: " + ue); + return e; + */ + } + + // + // If the stringified endpoint is opaque, create an unknown endpoint, + // then see whether the type matches one of the known endpoints. + // + if(protocol.Equals("opaque")) + { + EndpointI ue = new OpaqueEndpointI(v); + if(v.Count > 0) + { + Ice.EndpointParseException ex = new Ice.EndpointParseException(); + ex.str = "unrecognized argument `" + v[0] + "' in endpoint `" + str + "'"; + throw ex; + } + factory = get(ue.type()); + if(factory != null) + { + // + // Make a temporary stream, write the opaque endpoint data into the stream, + // and ask the factory to read the endpoint data from that stream to create + // the actual endpoint. + // + BasicStream bs = new BasicStream(instance_, Ice.Util.currentProtocolEncoding); + bs.writeShort(ue.type()); + ue.streamWrite(bs); + Buffer buf = bs.getBuffer(); + buf.b.position(0); + buf.b.limit(buf.size()); + bs.readShort(); // type + bs.startReadEncaps(); + EndpointI e = factory.read(bs); + bs.endReadEncaps(); + return e; + } + return ue; // Endpoint is opaque, but we don't have a factory for its type. + } + + return null; + } + + public EndpointI read(BasicStream s) + { + lock(this) + { + short type = s.readShort(); + + EndpointFactory factory = get(type); + EndpointI e = null; + + s.startReadEncaps(); + + if(factory != null) + { + e = factory.read(s); + } + else + { + e = new OpaqueEndpointI(type, s); + } + + s.endReadEncaps(); + + return e; + } + } + + internal void destroy() + { + for(int i = 0; i < _factories.Count; i++) + { + EndpointFactory f = (EndpointFactory)_factories[i]; + f.destroy(); + } + _factories.Clear(); + } + + private readonly Instance instance_; + private readonly List<EndpointFactory> _factories; + } + +} diff --git a/csharp/src/Ice/EndpointHostResolver.cs b/csharp/src/Ice/EndpointHostResolver.cs new file mode 100644 index 00000000000..d34edbed66a --- /dev/null +++ b/csharp/src/Ice/EndpointHostResolver.cs @@ -0,0 +1,292 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#if !SILVERLIGHT +namespace IceInternal +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Net; + using System.Threading; + + public class EndpointHostResolver + { + internal EndpointHostResolver(Instance instance) + { + _instance = instance; + _protocol = instance.protocolSupport(); + _preferIPv6 = instance.preferIPv6(); + _thread = new HelperThread(this); + updateObserver(); + if(instance.initializationData().properties.getProperty("Ice.ThreadPriority").Length > 0) + { + ThreadPriority priority = IceInternal.Util.stringToThreadPriority( + instance.initializationData().properties.getProperty("Ice.ThreadPriority")); + _thread.Start(priority); + } + else + { + _thread.Start(ThreadPriority.Normal); + } + } + + public void resolve(string host, int port, Ice.EndpointSelectionType selType, IPEndpointI endpoint, + EndpointI_connectors callback) + { + // + // Try to get the addresses without DNS lookup. If this doesn't work, we queue a resolve + // entry and the thread will take care of getting the endpoint addresses. + // + NetworkProxy networkProxy = _instance.networkProxy(); + if(networkProxy == null) + { + try + { + List<EndPoint> addrs = Network.getAddresses(host, port, _protocol, selType, _preferIPv6, false); + if(addrs.Count > 0) + { + callback.connectors(endpoint.connectors(addrs, null)); + return; + } + } + catch(Ice.LocalException ex) + { + callback.exception(ex); + return; + } + } + + lock(this) + { + Debug.Assert(!_destroyed); + + ResolveEntry entry = new ResolveEntry(); + entry.host = host; + entry.port = port; + entry.selType = selType; + entry.endpoint = endpoint; + entry.callback = callback; + + Ice.Instrumentation.CommunicatorObserver obsv = _instance.initializationData().observer; + if(obsv != null) + { + entry.observer = obsv.getEndpointLookupObserver(endpoint); + if(entry.observer != null) + { + entry.observer.attach(); + } + } + + _queue.AddLast(entry); + System.Threading.Monitor.Pulse(this); + } + } + + public void destroy() + { + lock(this) + { + Debug.Assert(!_destroyed); + _destroyed = true; + System.Threading.Monitor.Pulse(this); + } + } + + public void joinWithThread() + { + if(_thread != null) + { + _thread.Join(); + } + } + + public void run() + { + while(true) + { + ResolveEntry r; + Ice.Instrumentation.ThreadObserver threadObserver; + + lock(this) + { + while(!_destroyed && _queue.Count == 0) + { + System.Threading.Monitor.Wait(this); + } + + if(_destroyed) + { + break; + } + + r = _queue.First.Value; + _queue.RemoveFirst(); + threadObserver = _observer; + } + + if(threadObserver != null) + { + threadObserver.stateChanged(Ice.Instrumentation.ThreadState.ThreadStateIdle, + Ice.Instrumentation.ThreadState.ThreadStateInUseForOther); + } + + try + { + + NetworkProxy networkProxy = _instance.networkProxy(); + int protocol = _protocol; + if(networkProxy != null) + { + networkProxy = networkProxy.resolveHost(protocol); + if(networkProxy != null) + { + protocol = networkProxy.getProtocolSupport(); + } + } + + r.callback.connectors(r.endpoint.connectors(Network.getAddresses(r.host, + r.port, + protocol, + r.selType, + _preferIPv6, + true), + networkProxy)); + } + catch(Ice.LocalException ex) + { + if(r.observer != null) + { + r.observer.failed(ex.ice_name()); + } + r.callback.exception(ex); + } + finally + { + if(threadObserver != null) + { + threadObserver.stateChanged(Ice.Instrumentation.ThreadState.ThreadStateInUseForOther, + Ice.Instrumentation.ThreadState.ThreadStateIdle); + } + if(r.observer != null) + { + r.observer.detach(); + } + } + } + + foreach(ResolveEntry entry in _queue) + { + Ice.CommunicatorDestroyedException ex = new Ice.CommunicatorDestroyedException(); + if(entry.observer != null) + { + entry.observer.failed(ex.ice_name()); + entry.observer.detach(); + } + entry.callback.exception(ex); + } + _queue.Clear(); + + if(_observer != null) + { + _observer.detach(); + } + } + + public void + updateObserver() + { + lock(this) + { + Ice.Instrumentation.CommunicatorObserver obsv = _instance.initializationData().observer; + if(obsv != null) + { + _observer = obsv.getThreadObserver("Communicator", + _thread.getName(), + Ice.Instrumentation.ThreadState.ThreadStateIdle, + _observer); + if(_observer != null) + { + _observer.attach(); + } + } + } + } + + private class ResolveEntry + { + internal string host; + internal int port; + internal Ice.EndpointSelectionType selType; + internal IPEndpointI endpoint; + internal EndpointI_connectors callback; + internal Ice.Instrumentation.Observer observer; + } + + private readonly Instance _instance; + private readonly int _protocol; + private readonly bool _preferIPv6; + private bool _destroyed; + private LinkedList<ResolveEntry> _queue = new LinkedList<ResolveEntry>(); + private Ice.Instrumentation.ThreadObserver _observer; + + private sealed class HelperThread + { + internal HelperThread(EndpointHostResolver resolver) + { + _resolver = resolver; + _name = _resolver._instance.initializationData().properties.getProperty("Ice.ProgramName"); + if(_name.Length > 0) + { + _name += "-"; + } + _name += "Ice.HostResolver"; + } + + public void Join() + { + _thread.Join(); + } + + public void Start(ThreadPriority priority) + { + _thread = new Thread(new ThreadStart(Run)); + _thread.IsBackground = true; + _thread.Name = _name; + _thread.Priority = priority; + _thread.Start(); + } + + public void Run() + { + try + { + _resolver.run(); + } + catch(System.Exception ex) + { + string s = "exception in endpoint host resolver thread " + _name + ":\n" + ex; + _resolver._instance.initializationData().logger.error(s); + } + } + + public string getName() + { + return _name; + } + + private EndpointHostResolver _resolver; + private string _name; + private Thread _thread; + } + + private HelperThread _thread; + } +} +#endif diff --git a/csharp/src/Ice/EndpointI.cs b/csharp/src/Ice/EndpointI.cs new file mode 100644 index 00000000000..4f02c43daa4 --- /dev/null +++ b/csharp/src/Ice/EndpointI.cs @@ -0,0 +1,209 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections.Generic; + using System.Diagnostics; + using System.Net; + using System; + + public interface EndpointI_connectors + { + void connectors(List<Connector> connectors); + void exception(Ice.LocalException ex); + } + + public abstract class EndpointI : Ice.Endpoint, System.IComparable<EndpointI> + { + public override string ToString() + { + return ice_toString_(); + } + + public virtual string ice_toString_() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + return protocol() + options(); + } + + public abstract Ice.EndpointInfo getInfo(); + + public override bool Equals(object obj) + { + if(!(obj is EndpointI)) + { + return false; + } + return CompareTo((EndpointI)obj) == 0; + } + + public override int GetHashCode() // Avoids a compiler warning. + { + Debug.Assert(false); + return 0; + } + + // + // Marshal the endpoint. + // + public abstract void streamWrite(BasicStream s); + + // + // Return the endpoint type. + // + public abstract short type(); + + // + // Return the protocol name. + // + public abstract string protocol(); + + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + public abstract int timeout(); + + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + public abstract EndpointI timeout(int t); + + // + // Return the connection ID. + // + public abstract string connectionId(); + + // + // Return a new endpoint with a different connection id. + // + public abstract EndpointI connectionId(string connectionId); + + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + public abstract bool compress(); + + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + public abstract EndpointI compress(bool co); + + // + // Return true if the endpoint is datagram-based. + // + public abstract bool datagram(); + + // + // Return true if the endpoint is secure. + // + public abstract bool secure(); + + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. + // + public abstract Transceiver transceiver(); + + // + // Return a connector for this endpoint, or empty list if no connector + // is available. + // + public abstract void connectors_async(Ice.EndpointSelectionType selType, EndpointI_connectors callback); + + // + // Return an acceptor for this endpoint, or null if no acceptors + // is available. + // + public abstract Acceptor acceptor(string adapterName); + + // + // Expand endpoint out in to separate endpoints for each local + // host if listening on INADDR_ANY on server side or if no host + // was specified on client side. + // + public abstract List<EndpointI> expand(); + + // + // Check whether the endpoint is equivalent to another one. + // + public abstract bool equivalent(EndpointI endpoint); + + public abstract int CompareTo(EndpointI obj); + + public abstract string options(); + + public virtual void initWithOptions(List<string> args) + { + List<string> unknown = new List<string>(); + + string str = "`" + protocol() + " "; + foreach(string p in args) + { + if(IceUtilInternal.StringUtil.findFirstOf(p, " \t\n\r") != -1) + { + str += " \"" + p + "\""; + } + else + { + str += " " + p; + } + } + str += "'"; + + for(int n = 0; n < args.Count; ++n) + { + string option = args[n]; + if(option.Length < 2 || option[0] != '-') + { + unknown.Add(option); + continue; + } + + string argument = null; + if(n + 1 < args.Count && args[n + 1][0] != '-') + { + argument = args[++n]; + } + + if(!checkOption(option, argument, str)) + { + unknown.Add(option); + if(argument != null) + { + unknown.Add(argument); + } + } + } + + args.Clear(); + args.AddRange(unknown); + } + + protected virtual bool checkOption(string option, string argument, string endpoint) + { + // Must be overridden to check for options. + return false; + } + } + +} diff --git a/csharp/src/Ice/EventHandler.cs b/csharp/src/Ice/EventHandler.cs new file mode 100644 index 00000000000..8cb8371df40 --- /dev/null +++ b/csharp/src/Ice/EventHandler.cs @@ -0,0 +1,43 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + +using System; + +public abstract class EventHandler +{ + // + // Called to start a new asynchronous read or write operation. + // + abstract public bool startAsync(int op, AsyncCallback cb, ref bool completedSynchronously); + + abstract public bool finishAsync(int op); + + // + // Called when there's a message ready to be processed. + // + abstract public void message(ref ThreadPoolCurrent op); + + // + // Called when the event handler is unregistered. + // + abstract public void finished(ref ThreadPoolCurrent op); + + internal int _ready = 0; + internal int _pending = 0; + internal int _started = 0; + internal bool _finish = false; + + internal bool _hasMoreData = false; + internal int _registered = 0; +} + +} diff --git a/csharp/src/Ice/Exception.cs b/csharp/src/Ice/Exception.cs new file mode 100644 index 00000000000..34612f73722 --- /dev/null +++ b/csharp/src/Ice/Exception.cs @@ -0,0 +1,274 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.Serialization; + +namespace IceInternal +{ + public class Ex + { + public static void throwUOE(string expectedType, string actualType) + { + throw new Ice.UnexpectedObjectException( + "expected element of type `" + expectedType + "' but received '" + actualType, + actualType, expectedType); + } + + public static void throwMemoryLimitException(int requested, int maximum) + { + throw new Ice.MemoryLimitException("requested " + requested + " bytes, maximum allowed is " + maximum + + " bytes (see Ice.MessageSizeMax)"); + } + } +} + +namespace Ice +{ + /// <summary> + /// Base class for Ice exceptions. + /// </summary> +#if !SILVERLIGHT + [System.Serializable] +#endif + public abstract class Exception : System.Exception, System.ICloneable + { + /// <summary> + /// Creates and returns a copy of this exception. + /// </summary> + /// <returns>A copy of this exception.</returns> + public object Clone() + { + return MemberwiseClone(); + } + + /// <summary> + /// Creates a default-initialized exception. + /// </summary> + public Exception() {} + + /// <summary> + /// Creates a default-initialized exception and sets the InnerException + /// property to the passed exception. + /// </summary> + /// <param name="ex">The inner exception.</param> + public Exception(System.Exception ex) : base("", ex) {} + +#if !SILVERLIGHT + /// <summary> + /// Initializes a new instance of the exception with serialized data. + /// </summary> + /// <param name="info">Holds the serialized object data about the exception being thrown.</param> + /// <param name="context">Contains contextual information about the source or destination.</param> + protected Exception(SerializationInfo info, StreamingContext context) : base(info, context) {} +#endif + + /// <summary> + /// Returns the name of this exception. + /// </summary> + /// <returns>The name of this exception.</returns> + public abstract string ice_name(); + + /// <summary> + /// Returns a string representation of this exception, including + /// any inner exceptions. + /// </summary> + /// <returns>The string representation of this exception.</returns> + public override string ToString() + { + // + // This prints the exception Java style. That is, the outermost + // exception, "Caused by:" to the innermost exception. The + // stack trace is not nicely indented as with Java, but + // without string parsing (perhaps tokenize on "\n"), it + // doesn't appear to be possible to reformat it. + // + System.IO.StringWriter sw = new System.IO.StringWriter(CultureInfo.CurrentCulture); + IceUtilInternal.OutputBase op = new IceUtilInternal.OutputBase(sw); + op.setUseTab(false); + op.print(GetType().FullName); + op.inc(); + IceInternal.ValueWriter.write(this, op); + sw.Write("\n"); + sw.Write(StackTrace); + + System.Exception curr = InnerException; + while(curr != null) + { + sw.Write("\nCaused by: "); + sw.Write(curr.GetType().FullName); + if(!(curr is Ice.Exception)) + { + sw.Write(": "); + sw.Write(curr.Message); + } + sw.Write("\n"); + sw.Write(curr.StackTrace); + curr = curr.InnerException; + } + + return sw.ToString(); + } + } + + /// <summary> + /// Base class for local exceptions. + /// </summary> +#if !SILVERLIGHT + [System.Serializable] +#endif + public abstract class LocalException : Exception + { + /// <summary> + /// Creates a default-initialized local exception. + /// </summary> + public LocalException() {} + + /// <summary> + /// Creates a default-initialized local exception and sets the InnerException + /// property to the passed exception. + /// </summary> + /// <param name="ex">The inner exception.</param> + public LocalException(System.Exception ex) : base(ex) {} + +#if !SILVERLIGHT + /// <summary> + /// Initializes a new instance of the exception with serialized data. + /// </summary> + /// <param name="info">Holds the serialized object data about the exception being thrown.</param> + /// <param name="context">Contains contextual information about the source or destination.</param> + protected LocalException(SerializationInfo info, StreamingContext context) : base(info, context) {} +#endif + } + + /// <summary> + /// Base class for Ice run-time exceptions. + /// </summary> +#if !SILVERLIGHT + [System.Serializable] +#endif + public abstract class SystemException : Exception + { + /// <summary> + /// Creates a default-initialized run-time exception. + /// </summary> + public SystemException() {} + + /// <summary> + /// Creates a default-initialized run-time exception and sets the InnerException + /// property to the passed exception. + /// </summary> + /// <param name="ex">The inner exception.</param> + public SystemException(System.Exception ex) : base(ex) {} + +#if !SILVERLIGHT + /// <summary> + /// Initializes a new instance of the exception with serialized data. + /// </summary> + /// <param name="info">Holds the serialized object data about the exception being thrown.</param> + /// <param name="context">Contains contextual information about the source or destination.</param> + protected SystemException(SerializationInfo info, StreamingContext context) : base(info, context) {} +#endif + } + + /// <summary> + /// Base class for Slice user exceptions. + /// </summary> +#if !SILVERLIGHT + [System.Serializable] +#endif + public abstract class UserException : Exception + { + /// <summary> + /// Creates a default-initialized user exception. + /// </summary> + public UserException() {} + + /// <summary> + /// Creates a default-initialized user exception and sets the InnerException + /// property to the passed exception. + /// </summary> + /// <param name="ex">The inner exception.</param> + public UserException(System.Exception ex) : base(ex) {} + +#if !SILVERLIGHT + /// <summary> + /// Initializes a new instance of the exception with serialized data. + /// </summary> + /// <param name="info">Holds the serialized object data about the exception being thrown.</param> + /// <param name="context">Contains contextual information about the source or destination.</param> + protected UserException(SerializationInfo info, StreamingContext context) : base(info, context) {} +#endif + + public virtual void write__(IceInternal.BasicStream os__) + { + os__.startWriteException(null); + writeImpl__(os__); + os__.endWriteException(); + } + + public virtual void read__(IceInternal.BasicStream is__) + { + is__.startReadException(); + readImpl__(is__); + is__.endReadException(false); + } + + public virtual void write__(OutputStream os__) + { + os__.startException(null); + writeImpl__(os__); + os__.endException(); + } + + public virtual void read__(InputStream is__) + { + is__.startException(); + readImpl__(is__); + is__.endException(false); + } + + public virtual bool usesClasses__() + { + return false; + } + + protected abstract void writeImpl__(IceInternal.BasicStream os__); + protected abstract void readImpl__(IceInternal.BasicStream is__); + + protected virtual void writeImpl__(OutputStream os__) + { + throw new MarshalException("exception was not generated with stream support"); + } + + protected virtual void readImpl__(InputStream is__) + { + throw new MarshalException("exception was not generated with stream support"); + } + } +} + +namespace IceInternal +{ + public class RetryException : System.Exception + { + public RetryException(Ice.LocalException ex) + { + _ex = ex; + } + + public Ice.LocalException get() + { + return _ex; + } + + private Ice.LocalException _ex; + } +} diff --git a/csharp/src/Ice/FormatType.cs b/csharp/src/Ice/FormatType.cs new file mode 100644 index 00000000000..f4cb664b793 --- /dev/null +++ b/csharp/src/Ice/FormatType.cs @@ -0,0 +1,21 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// This enumeration describes the possible formats for classes and exceptions. + /// </summary> + public enum FormatType + { + DefaultFormat, + CompactFormat, + SlicedFormat + } +} diff --git a/csharp/src/Ice/HashSet.cs b/csharp/src/Ice/HashSet.cs new file mode 100644 index 00000000000..813633d69b7 --- /dev/null +++ b/csharp/src/Ice/HashSet.cs @@ -0,0 +1,114 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#if COMPACT + +// +// System.Collections.Generic.HashSet is not available in the .NET Compact Framework. +// This class is a minimal implementation that provides only the methods required by +// Ice internals. +// +using System; +using System.Collections.Generic; + +namespace IceInternal +{ + public class HashSet<T> : ICollection<T> + { + public HashSet() + { + entries_ = new Dictionary<T, bool>(); + } + + public HashSet(int capacity) + { + entries_ = new Dictionary<T, bool>(capacity); + } + + void ICollection<T>.Add(T item) + { + try + { + entries_.Add(item, false); + } + catch(ArgumentException) + { + // Item already present. + } + } + + public bool Add(T item) + { + try + { + entries_.Add(item, false); + } + catch(ArgumentException) + { + return false; // Item already present. + } + return true; + } + + public void Clear() + { + entries_.Clear(); + } + + public bool Contains(T item) + { + return entries_.ContainsKey(item); + } + + public void CopyTo(T[] a, int idx) + { + entries_.Keys.CopyTo(a, idx); + } + + public void CopyTo(T[] a) + { + entries_.Keys.CopyTo(a, 0); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return entries_.Keys.GetEnumerator(); + } + + public IEnumerator<T> GetEnumerator() + { + return entries_.Keys.GetEnumerator(); + } + + public bool Remove(T item) + { + return entries_.Remove(item); + } + + public int Count + { + get + { + return entries_.Count; + } + } + + public bool IsReadOnly + { + get + { + return false; + } + } + + private Dictionary<T, bool> entries_; + } +} + +#endif diff --git a/csharp/src/Ice/HttpParser.cs b/csharp/src/Ice/HttpParser.cs new file mode 100644 index 00000000000..32399400e34 --- /dev/null +++ b/csharp/src/Ice/HttpParser.cs @@ -0,0 +1,774 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Collections.Generic; + using System.Text; + + internal sealed class WebSocketException : System.Exception + { + internal WebSocketException() : + base("", null) + { + } + + internal WebSocketException(string message) : + base(message, null) + { + } + + internal WebSocketException(string message, System.Exception cause) : + base(message, cause) + { + } + + internal WebSocketException(System.Exception cause) : + base("", cause) + { + } + } + + internal sealed class HttpParser + { + internal HttpParser() + { + _type = Type.Unknown; + _versionMajor = 0; + _versionMinor = 0; + _status = 0; + _state = State.Init; + } + + internal enum Type + { + Unknown, + Request, + Response + }; + + internal int isCompleteMessage(IceInternal.ByteBuffer buf, int begin, int end) + { + byte[] raw = buf.rawBytes(); + int p = begin; + + // + // Skip any leading CR-LF characters. + // + while(p < end) + { + byte ch = raw[p]; + if(ch != (byte)'\r' && ch != (byte)'\n') + { + break; + } + ++p; + } + + // + // Look for adjacent CR-LF/CR-LF or LF/LF. + // + bool seenFirst = false; + while(p < end) + { + byte ch = raw[p++]; + if(ch == (byte)'\n') + { + if(seenFirst) + { + return p; + } + else + { + seenFirst = true; + } + } + else if(ch != (byte)'\r') + { + seenFirst = false; + } + } + + return -1; + } + + internal bool parse(IceInternal.ByteBuffer buf, int begin, int end) + { + byte[] raw = buf.rawBytes(); + int p = begin; + int start = 0; + const char CR = '\r'; + const char LF = '\n'; + + if(_state == State.Complete) + { + _state = State.Init; + } + + while(p != end && _state != State.Complete) + { + char c = (char)raw[p]; + + switch(_state) + { + case State.Init: + { + _method = new StringBuilder(); + _uri = new StringBuilder(); + _versionMajor = -1; + _versionMinor = -1; + _status = -1; + _reason = ""; + _headers.Clear(); + _state = State.Type; + continue; + } + case State.Type: + { + if(c == CR || c == LF) + { + break; + } + else if(c == 'H') + { + // + // Could be the start of "HTTP/1.1" or "HEAD". + // + _state = State.TypeCheck; + break; + } + else + { + _state = State.Request; + continue; + } + } + case State.TypeCheck: + { + if(c == 'T') // Continuing "H_T_TP/1.1" + { + _state = State.Response; + } + else if(c == 'E') // Expecting "HEAD" + { + _state = State.Request; + _method.Append('H'); + _method.Append('E'); + } + else + { + throw new WebSocketException("malformed request or response"); + } + break; + } + case State.Request: + { + _type = Type.Request; + _state = State.RequestMethod; + continue; + } + case State.RequestMethod: + { + if(c == ' ' || c == CR || c == LF) + { + _state = State.RequestMethodSP; + continue; + } + _method.Append(c); + break; + } + case State.RequestMethodSP: + { + if(c == ' ') + { + break; + } + else if(c == CR || c == LF) + { + throw new WebSocketException("malformed request"); + } + _state = State.RequestURI; + continue; + } + case State.RequestURI: + { + if(c == ' ' || c == CR || c == LF) + { + _state = State.RequestURISP; + continue; + } + _uri.Append(c); + break; + } + case State.RequestURISP: + { + if(c == ' ') + { + break; + } + else if(c == CR || c == LF) + { + throw new WebSocketException("malformed request"); + } + _state = State.Version; + continue; + } + case State.RequestLF: + { + if(c != LF) + { + throw new WebSocketException("malformed request"); + } + _state = State.HeaderFieldStart; + break; + } + case State.HeaderFieldStart: + { + // + // We've already seen a LF to reach this state. + // + // Another CR or LF indicates the end of the header fields. + // + if(c == CR) + { + _state = State.HeaderFieldEndLF; + break; + } + else if(c == LF) + { + _state = State.Complete; + break; + } + else if(c == ' ') + { + // + // Could be a continuation line. + // + _state = State.HeaderFieldContStart; + break; + } + + _state = State.HeaderFieldNameStart; + continue; + } + case State.HeaderFieldContStart: + { + if(c == ' ') + { + break; + } + + _state = State.HeaderFieldCont; + start = p; + continue; + } + case State.HeaderFieldCont: + { + if(c == CR || c == LF) + { + if(p > start) + { + if(_headerName.Length == 0) + { + throw new WebSocketException("malformed header"); + } + Debug.Assert(_headers.ContainsKey(_headerName)); + string s = _headers[_headerName]; + StringBuilder newValue = new StringBuilder(s); + newValue.Append(' '); + for(int i = start; i < p; ++i) + { + newValue.Append((char)raw[i]); + } + _headers[_headerName] = newValue.ToString(); + _state = c == CR ? State.HeaderFieldLF : State.HeaderFieldStart; + } + else + { + // + // Could mark the end of the header fields. + // + _state = c == CR ? State.HeaderFieldEndLF : State.Complete; + } + } + + break; + } + case State.HeaderFieldNameStart: + { + Debug.Assert(c != ' '); + start = p; + _headerName = ""; + _state = State.HeaderFieldName; + continue; + } + case State.HeaderFieldName: + { + if(c == ' ' || c == ':') + { + _state = State.HeaderFieldNameEnd; + continue; + } + else if(c == CR || c == LF) + { + throw new WebSocketException("malformed header"); + } + break; + } + case State.HeaderFieldNameEnd: + { + if(_headerName.Length == 0) + { + StringBuilder str = new StringBuilder(); + for(int i = start; i < p; ++i) + { + str.Append((char)raw[i]); + } + _headerName = str.ToString().ToLower(); + // + // Add a placeholder entry if necessary. + // + if(!_headers.ContainsKey(_headerName)) + { + _headers[_headerName] = ""; + _headerNames[_headerName] = str.ToString(); + } + } + + if(c == ' ') + { + break; + } + else if(c != ':' || p == start) + { + throw new WebSocketException("malformed header"); + } + + _state = State.HeaderFieldValueStart; + break; + } + case State.HeaderFieldValueStart: + { + if(c == ' ') + { + break; + } + + // + // Check for "Name:\r\n" + // + if(c == CR) + { + _state = State.HeaderFieldLF; + break; + } + else if(c == LF) + { + _state = State.HeaderFieldStart; + break; + } + + start = p; + _state = State.HeaderFieldValue; + continue; + } + case State.HeaderFieldValue: + { + if(c == CR || c == LF) + { + _state = State.HeaderFieldValueEnd; + continue; + } + break; + } + case State.HeaderFieldValueEnd: + { + Debug.Assert(c == CR || c == LF); + if(p > start) + { + StringBuilder str = new StringBuilder(); + for(int i = start; i < p; ++i) + { + str.Append((char)raw[i]); + } + string s = null; + if(!_headers.TryGetValue(_headerName, out s) || s.Length == 0) + { + _headers[_headerName] = str.ToString(); + } + else + { + _headers[_headerName] = s + ", " + str.ToString(); + } + } + + if(c == CR) + { + _state = State.HeaderFieldLF; + } + else + { + _state = State.HeaderFieldStart; + } + break; + } + case State.HeaderFieldLF: + { + if(c != LF) + { + throw new WebSocketException("malformed header"); + } + _state = State.HeaderFieldStart; + break; + } + case State.HeaderFieldEndLF: + { + if(c != LF) + { + throw new WebSocketException("malformed header"); + } + _state = State.Complete; + break; + } + case State.Version: + { + if(c != 'H') + { + throw new WebSocketException("malformed version"); + } + _state = State.VersionH; + break; + } + case State.VersionH: + { + if(c != 'T') + { + throw new WebSocketException("malformed version"); + } + _state = State.VersionHT; + break; + } + case State.VersionHT: + { + if(c != 'T') + { + throw new WebSocketException("malformed version"); + } + _state = State.VersionHTT; + break; + } + case State.VersionHTT: + { + if(c != 'P') + { + throw new WebSocketException("malformed version"); + } + _state = State.VersionHTTP; + break; + } + case State.VersionHTTP: + { + if(c != '/') + { + throw new WebSocketException("malformed version"); + } + _state = State.VersionMajor; + break; + } + case State.VersionMajor: + { + if(c == '.') + { + if(_versionMajor == -1) + { + throw new WebSocketException("malformed version"); + } + _state = State.VersionMinor; + break; + } + else if(c < '0' || c > '9') + { + throw new WebSocketException("malformed version"); + } + if(_versionMajor == -1) + { + _versionMajor = 0; + } + _versionMajor *= 10; + _versionMajor += (int)(c - '0'); + break; + } + case State.VersionMinor: + { + if(c == CR) + { + if(_versionMinor == -1 || _type != Type.Request) + { + throw new WebSocketException("malformed version"); + } + _state = State.RequestLF; + break; + } + else if(c == LF) + { + if(_versionMinor == -1 || _type != Type.Request) + { + throw new WebSocketException("malformed version"); + } + _state = State.HeaderFieldStart; + break; + } + else if(c == ' ') + { + if(_versionMinor == -1 || _type != Type.Response) + { + throw new WebSocketException("malformed version"); + } + _state = State.ResponseVersionSP; + break; + } + else if(c < '0' || c > '9') + { + throw new WebSocketException("malformed version"); + } + if(_versionMinor == -1) + { + _versionMinor = 0; + } + _versionMinor *= 10; + _versionMinor += (int)(c - '0'); + break; + } + case State.Response: + { + _type = Type.Response; + _state = State.VersionHT; + continue; + } + case State.ResponseVersionSP: + { + if(c == ' ') + { + break; + } + + _state = State.ResponseStatus; + continue; + } + case State.ResponseStatus: + { + // TODO: Is reason string optional? + if(c == CR) + { + if(_status == -1) + { + throw new WebSocketException("malformed response status"); + } + _state = State.ResponseLF; + break; + } + else if(c == LF) + { + if(_status == -1) + { + throw new WebSocketException("malformed response status"); + } + _state = State.HeaderFieldStart; + break; + } + else if(c == ' ') + { + if(_status == -1) + { + throw new WebSocketException("malformed response status"); + } + _state = State.ResponseReasonStart; + break; + } + else if(c < '0' || c > '9') + { + throw new WebSocketException("malformed response status"); + } + if(_status == -1) + { + _status = 0; + } + _status *= 10; + _status += (int)(c - '0'); + break; + } + case State.ResponseReasonStart: + { + // + // Skip leading spaces. + // + if(c == ' ') + { + break; + } + + _state = State.ResponseReason; + start = p; + continue; + } + case State.ResponseReason: + { + if(c == CR || c == LF) + { + if(p > start) + { + StringBuilder str = new StringBuilder(); + for(int i = start; i < p; ++i) + { + str.Append((char)raw[i]); + } + _reason = str.ToString(); + } + _state = c == CR ? State.ResponseLF : State.HeaderFieldStart; + } + + break; + } + case State.ResponseLF: + { + if(c != LF) + { + throw new WebSocketException("malformed status line"); + } + _state = State.HeaderFieldStart; + break; + } + case State.Complete: + { + Debug.Assert(false); // Shouldn't reach + break; + } + } + + ++p; + } + + return _state == State.Complete; + } + + internal Type type() + { + return _type; + } + + internal string method() + { + Debug.Assert(_type == Type.Request); + return _method.ToString(); + } + + internal string uri() + { + Debug.Assert(_type == Type.Request); + return _uri.ToString(); + } + + internal int versionMajor() + { + return _versionMajor; + } + + internal int versionMinor() + { + return _versionMinor; + } + + internal int status() + { + return _status; + } + + internal string reason() + { + return _reason; + } + + internal string getHeader(string name, bool toLower) + { + string s = null; + if(_headers.TryGetValue(name.ToLower(), out s)) + { + return toLower ? s.Trim().ToLower() : s.Trim(); + } + + return null; + } + + internal Dictionary<string, string> getHeaders() + { + Dictionary<string, string> dict = new Dictionary<string, string>(); + foreach(KeyValuePair<string, string> e in _headers) + { + dict[_headerNames[e.Key]] = e.Value.Trim(); + } + return dict; + } + + private Type _type; + + private StringBuilder _method = new StringBuilder(); + private StringBuilder _uri = new StringBuilder(); + + private Dictionary<string, string> _headers = new Dictionary<string, string>(); + private Dictionary<string, string> _headerNames = new Dictionary<string, string>(); + private string _headerName = ""; + + private int _versionMajor; + private int _versionMinor; + + private int _status; + private string _reason; + + private enum State + { + Init, + Type, + TypeCheck, + Request, + RequestMethod, + RequestMethodSP, + RequestURI, + RequestURISP, + RequestLF, + HeaderFieldStart, + HeaderFieldContStart, + HeaderFieldCont, + HeaderFieldNameStart, + HeaderFieldName, + HeaderFieldNameEnd, + HeaderFieldValueStart, + HeaderFieldValue, + HeaderFieldValueEnd, + HeaderFieldLF, + HeaderFieldEndLF, + Version, + VersionH, + VersionHT, + VersionHTT, + VersionHTTP, + VersionMajor, + VersionMinor, + Response, + ResponseVersionSP, + ResponseStatus, + ResponseReasonStart, + ResponseReason, + ResponseLF, + Complete + }; + private State _state; + } +} diff --git a/csharp/src/Ice/IPEndpointI.cs b/csharp/src/Ice/IPEndpointI.cs new file mode 100644 index 00000000000..7741670b615 --- /dev/null +++ b/csharp/src/Ice/IPEndpointI.cs @@ -0,0 +1,407 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Net; + using System; + + public abstract class IPEndpointI : EndpointI + { + public IPEndpointI(ProtocolInstance instance, string host, int port, EndPoint sourceAddr, string connectionId) + { + instance_ = instance; + host_ = host; + port_ = port; + sourceAddr_ = sourceAddr; + connectionId_ = connectionId; + _hashInitialized = false; + } + + public IPEndpointI(ProtocolInstance instance) + { + instance_ = instance; + host_ = null; + port_ = 0; + sourceAddr_ = null; + connectionId_ = ""; + _hashInitialized = false; + } + + public IPEndpointI(ProtocolInstance instance, BasicStream s) + { + instance_ = instance; + host_ = s.readString(); + port_ = s.readInt(); + sourceAddr_ = null; + connectionId_ = ""; + _hashInitialized = false; + } + + private sealed class InfoI : Ice.IPEndpointInfo + { + public InfoI(IPEndpointI e) + { + _endpoint = e; + } + + override public short type() + { + return _endpoint.type(); + } + + override public bool datagram() + { + return _endpoint.datagram();; + } + + override public bool secure() + { + return _endpoint.secure(); + } + + private IPEndpointI _endpoint; + } + + public override Ice.EndpointInfo getInfo() + { + InfoI info = new InfoI(this); + fillEndpointInfo(info); + return info; + } + + public override void streamWrite(BasicStream s) + { + s.startWriteEncaps(); + streamWriteImpl(s); + s.endWriteEncaps(); + } + + public override short type() + { + return instance_.type(); + } + + public override string protocol() + { + return instance_.protocol(); + } + + public override bool secure() + { + return instance_.secure(); + } + + public override string connectionId() + { + return connectionId_; + } + + public override EndpointI connectionId(string connectionId) + { + if(connectionId.Equals(connectionId_)) + { + return this; + } + else + { + return createEndpoint(host_, port_, connectionId); + } + } + + public override void connectors_async(Ice.EndpointSelectionType selType, EndpointI_connectors callback) + { +#if SILVERLIGHT + callback.connectors(connectors(selType)); +#else + instance_.resolve(host_, port_, selType, this, callback); +#endif + } + + public override List<EndpointI> expand() + { + List<EndpointI> endps = new List<EndpointI>(); + List<string> hosts = Network.getHostsForEndpointExpand(host_, instance_.protocolSupport(), false); + if(hosts == null || hosts.Count == 0) + { + endps.Add(this); + } + else + { + foreach(string h in hosts) + { + endps.Add(createEndpoint(h, port_, connectionId_)); + } + } + return endps; + } + + public override bool equivalent(EndpointI endpoint) + { + if(!(endpoint is IPEndpointI)) + { + return false; + } + IPEndpointI ipEndpointI = (IPEndpointI)endpoint; + return ipEndpointI.type() == type() && ipEndpointI.host_.Equals(host_) && ipEndpointI.port_ == port_ && + Network.addressEquals(ipEndpointI.sourceAddr_, sourceAddr_); + } + + public virtual List<Connector> connectors(List<EndPoint> addresses, NetworkProxy proxy) + { + List<Connector> connectors = new List<Connector>(); + foreach(EndPoint p in addresses) + { + connectors.Add(createConnector(p, proxy)); + } + return connectors; + } + +#if SILVERLIGHT + public List<Connector> connectors(Ice.EndpointSelectionType selType) + { + return connectors(Network.getAddresses(host_, port_, instance_.protocolSupport(), selType, + instance_.preferIPv6(), false), + instance_.networkProxy()); + } +#endif + + public override string options() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + string s = ""; + + if(host_ != null && host_.Length > 0) + { + s += " -h "; + bool addQuote = host_.IndexOf(':') != -1; + if(addQuote) + { + s += "\""; + } + s += host_; + if(addQuote) + { + s += "\""; + } + } + + s += " -p " + port_; + + if(sourceAddr_ != null) + { + s += " --sourceAddress " + Network.endpointAddressToString(sourceAddr_); + } + + return s; + } + + public override int GetHashCode() + { + if(!_hashInitialized) + { + _hashValue = 5381; + HashUtil.hashAdd(ref _hashValue, type()); + hashInit(ref _hashValue); + _hashInitialized = true; + } + return _hashValue; + } + + public override int CompareTo(EndpointI obj) + { + if(!(obj is IPEndpointI)) + { + return type() < obj.type() ? -1 : 1; + } + + IPEndpointI p = (IPEndpointI)obj; + if(this == p) + { + return 0; + } + + int v = string.Compare(host_, p.host_, StringComparison.Ordinal); + if(v != 0) + { + return v; + } + + if(port_ < p.port_) + { + return -1; + } + else if(p.port_ < port_) + { + return 1; + } + + int rc = string.Compare(Network.endpointAddressToString(sourceAddr_), + Network.endpointAddressToString(p.sourceAddr_), StringComparison.Ordinal); + if(rc != 0) + { + return rc; + } + + return string.Compare(connectionId_, p.connectionId_, StringComparison.Ordinal); + } + + public string host() + { + return host_; + } + + public int port() + { + return port_; + } + + public virtual void streamWriteImpl(BasicStream s) + { + s.writeString(host_); + s.writeInt(port_); + } + + public virtual void hashInit(ref int h) + { + HashUtil.hashAdd(ref h, host_); + HashUtil.hashAdd(ref h, port_); + if(sourceAddr_ != null) + { + HashUtil.hashAdd(ref h, sourceAddr_); + } + HashUtil.hashAdd(ref h, connectionId_); + } + + public virtual void fillEndpointInfo(Ice.IPEndpointInfo info) + { + info.host = host_; + info.port = port_; + info.sourceAddress = Network.endpointAddressToString(sourceAddr_); + } + + public virtual void initWithOptions(List<string> args, bool oaEndpoint) + { + base.initWithOptions(args); + + if(host_ == null || host_.Length == 0) + { + host_ = instance_.defaultHost(); + } + else if(host_.Equals("*")) + { + if(oaEndpoint) + { + host_ = ""; + } + else + { + throw new Ice.EndpointParseException("`-h *' not valid for proxy endpoint `" + ToString() + "'"); + } + } + + if(host_ == null) + { + host_ = ""; + } + + if(sourceAddr_ != null) + { + if(oaEndpoint) + { + throw new Ice.EndpointParseException("`--sourceAddress' not valid for object adapter endpoint `" + + ToString() + "'"); + } + } + else if(!oaEndpoint) + { + sourceAddr_ = instance_.defaultSourceAddress(); + } + } + + protected override bool checkOption(string option, string argument, string endpoint) + { + if(option.Equals("-h")) + { + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for -h option in endpoint " + + endpoint); + } + host_ = argument; + } + else if(option.Equals("-p")) + { + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for -p option in endpoint " + + endpoint); + } + + try + { + port_ = System.Int32.Parse(argument, CultureInfo.InvariantCulture); + } + catch(System.FormatException ex) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(ex); + e.str = "invalid port value `" + argument + "' in endpoint " + endpoint; + throw e; + } + + if(port_ < 0 || port_ > 65535) + { + throw new Ice.EndpointParseException("port value `" + argument + + "' out of range in endpoint " + endpoint); + } + } + else if(option.Equals("--sourceAddress")) + { + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for --sourceAddress option in endpoint " + + endpoint); + } + sourceAddr_ = Network.getNumericAddress(argument); + if(sourceAddr_ == null) + { + throw new Ice.EndpointParseException( + "invalid IP address provided for --sourceAddress option in endpoint " + endpoint); + } + } + else + { + return false; + } + return true; + } + + protected abstract Connector createConnector(EndPoint addr, NetworkProxy proxy); + protected abstract IPEndpointI createEndpoint(string host, int port, string connectionId); + + protected ProtocolInstance instance_; + protected string host_; + protected int port_; + protected EndPoint sourceAddr_; + protected string connectionId_; + private bool _hashInitialized; + private int _hashValue; + } + +} diff --git a/csharp/src/Ice/Ice.dll.config b/csharp/src/Ice/Ice.dll.config new file mode 100644 index 00000000000..7cf597f8b6f --- /dev/null +++ b/csharp/src/Ice/Ice.dll.config @@ -0,0 +1,3 @@ +<configuration> + <dllmap dll="bzip2.dll" target="libbz2.so.1"/> +</configuration> diff --git a/csharp/src/Ice/ImplicitContextI.cs b/csharp/src/Ice/ImplicitContextI.cs new file mode 100644 index 00000000000..ba830f89115 --- /dev/null +++ b/csharp/src/Ice/ImplicitContextI.cs @@ -0,0 +1,409 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + using System.Collections; + using System.Collections.Generic; + using System.Threading; + + // + // The base class for all ImplicitContext implementations + // + public abstract class ImplicitContextI : ImplicitContext + { + public static ImplicitContextI create(string kind) + { + if(kind.Equals("None") || kind.Length == 0) + { + return null; + } + else if(kind.Equals("Shared")) + { + return new SharedImplicitContext(); + } + else if(kind.Equals("PerThread")) + { + return new PerThreadImplicitContext(); + } + else + { + throw new Ice.InitializationException( + "'" + kind + "' is not a valid value for Ice.ImplicitContext"); + } + } + + public abstract Dictionary<string, string> getContext(); + public abstract void setContext(Dictionary<string, string> newContext); + public abstract bool containsKey(string key); + public abstract string get(string key); + public abstract string put(string key, string value); + public abstract string remove(string key); + + abstract public void write(Dictionary<string, string> prxContext, + IceInternal.BasicStream os); + abstract internal Dictionary<string, string> combine(Dictionary<string, string> prxContext); + } + + + internal class SharedImplicitContext : ImplicitContextI + { + public override Dictionary<string, string> getContext() + { + lock(this) + { + return new Dictionary<string, string>(_context); + } + } + + public override void setContext(Dictionary<string, string> context) + { + lock(this) + { + if(context != null && context.Count != 0) + { + _context = new Dictionary<string, string>(context); + } + else + { + _context.Clear(); + } + } + } + + public override bool containsKey(string key) + { + lock(this) + { + if(key == null) + { + key = ""; + } + + return _context.ContainsKey(key); + } + } + + public override string get(string key) + { + lock(this) + { + if(key == null) + { + key = ""; + } + + string val = _context[key]; + if(val == null) + { + val = ""; + } + return val; + } + } + + + public override string put(string key, string value) + { + lock(this) + { + if(key == null) + { + key = ""; + } + if(value == null) + { + value = ""; + } + + string oldVal; + _context.TryGetValue(key, out oldVal); + if(oldVal == null) + { + oldVal = ""; + } + _context[key] = value; + + return oldVal; + } + } + + public override string remove(string key) + { + lock(this) + { + if(key == null) + { + key = ""; + } + + string val = _context[key]; + + if(val == null) + { + val = ""; + } + else + { + _context.Remove(key); + } + + return val; + } + } + + public override void write(Dictionary<string, string> prxContext, IceInternal.BasicStream os) + { + if(prxContext.Count == 0) + { + lock(this) + { + ContextHelper.write(os, _context); + } + } + else + { + Dictionary<string, string> ctx = null; + lock(this) + { + ctx = _context.Count == 0 ? prxContext : combine(prxContext); + } + ContextHelper.write(os, ctx); + } + } + + internal override Dictionary<string, string> combine(Dictionary<string, string> prxContext) + { + lock(this) + { + Dictionary<string, string> combined = new Dictionary<string, string>(prxContext); + foreach(KeyValuePair<string, string> e in _context) + { + try + { + combined.Add(e.Key, e.Value); + } + catch(System.ArgumentException) + { + // Ignore. + } + } + return combined; + } + } + + private Dictionary<string, string> _context = new Dictionary<string, string>(); + } + + internal class PerThreadImplicitContext : ImplicitContextI + { + public override Dictionary<string, string> getContext() + { + Dictionary<string, string> threadContext = null; + Thread currentThread = Thread.CurrentThread; + lock(this) + { + if(_map.ContainsKey(currentThread)) + { + threadContext = (Dictionary<string, string>)_map[currentThread]; + } + } + + if(threadContext == null) + { + threadContext = new Dictionary<string, string>(); + } + return threadContext; + } + + public override void setContext(Dictionary<string, string> context) + { + if(context == null || context.Count == 0) + { + lock(this) + { + _map.Remove(Thread.CurrentThread); + } + } + else + { + Dictionary<string, string> threadContext = new Dictionary<string, string>(context); + + lock(this) + { + _map.Add(Thread.CurrentThread, threadContext); + } + } + } + + public override bool containsKey(string key) + { + if(key == null) + { + key = ""; + } + + Dictionary<string, string> threadContext = null; + lock(this) + { + if(!_map.TryGetValue(Thread.CurrentThread, out threadContext)) + { + return false; + } + } + + return threadContext.ContainsKey(key); + } + + public override string get(string key) + { + if(key == null) + { + key = ""; + } + + Dictionary<string, string> threadContext = null; + lock(this) + { + if(!_map.TryGetValue(Thread.CurrentThread, out threadContext)) + { + return ""; + } + } + + string val = threadContext[key]; + if(val == null) + { + val = ""; + } + return val; + } + + public override string put(string key, string value) + { + if(key == null) + { + key = ""; + } + if(value == null) + { + value = ""; + } + + Dictionary<string, string> threadContext = null; + lock(this) + { + if(!_map.TryGetValue(Thread.CurrentThread, out threadContext)) + { + threadContext = new Dictionary<string, string>(); + _map.Add(Thread.CurrentThread, threadContext); + } + } + + string oldVal; + if(!threadContext.TryGetValue(key, out oldVal)) + { + oldVal = ""; + } + + threadContext[key] = value; + return oldVal; + } + + public override string remove(string key) + { + if(key == null) + { + key = ""; + } + + Dictionary<string, string> threadContext = null; + lock(this) + { + if(!_map.TryGetValue(Thread.CurrentThread, out threadContext)) + { + return ""; + } + } + + + string val = null; + if(!threadContext.TryGetValue(key, out val)) + { + val = ""; + } + else + { + threadContext.Remove(key); + } + return val; + } + + public override void write(Dictionary<string, string> prxContext, IceInternal.BasicStream os) + { + Dictionary<string, string> threadContext = null; + lock(this) + { + _map.TryGetValue(Thread.CurrentThread, out threadContext); + } + + if(threadContext == null || threadContext.Count == 0) + { + ContextHelper.write(os, prxContext); + } + else if(prxContext.Count == 0) + { + ContextHelper.write(os, threadContext); + } + else + { + Dictionary<string, string> combined = new Dictionary<string, string>(prxContext); + foreach(KeyValuePair<string, string> e in threadContext) + { + try + { + combined.Add(e.Key, e.Value); + } + catch(System.ArgumentException) + { + // Ignore. + } + } + ContextHelper.write(os, combined); + } + } + + internal override Dictionary<string, string> combine(Dictionary<string, string> prxContext) + { + Dictionary<string, string> threadContext = null; + lock(this) + { + if(!_map.TryGetValue(Thread.CurrentThread, out threadContext)) + { + return new Dictionary<string, string>(prxContext); + } + } + + Dictionary<string, string> combined = new Dictionary<string, string>(prxContext); + foreach(KeyValuePair<string, string> e in threadContext) + { + combined.Add(e.Key, e.Value); + } + return combined; + } + + // + // map Thread -> Context + // + private Dictionary<Thread, Dictionary<string, string> > _map = + new Dictionary<Thread, Dictionary<string, string> >(); + } +} + + diff --git a/csharp/src/Ice/Incoming.cs b/csharp/src/Ice/Incoming.cs new file mode 100644 index 00000000000..1033b0a9083 --- /dev/null +++ b/csharp/src/Ice/Incoming.cs @@ -0,0 +1,936 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Globalization; + + public class IncomingBase + { + protected internal IncomingBase(Instance instance, ResponseHandler handler, Ice.ConnectionI connection, + Ice.ObjectAdapter adapter, bool response, byte compress, int requestId) + { + instance_ = instance; + responseHandler_ = handler; + response_ = response; + compress_ = compress; + if(response_) + { + os_ = new BasicStream(instance, Ice.Util.currentProtocolEncoding); + } + + current_ = new Ice.Current(); + current_.id = new Ice.Identity(); + current_.adapter = adapter; + current_.con = connection; + current_.requestId = requestId; + + cookie_ = null; + } + + protected internal IncomingBase(IncomingBase inc) // Adopts the argument. It must not be used afterwards. + { + // + // We don't change current_ as it's exposed by Ice::Request. + // + current_ = inc.current_; + + // + // Deep copy + // + if(inc.interceptorAsyncCallbackList_ != null) + { + // + // Copy, not just reference + // + interceptorAsyncCallbackList_ = + new List<Ice.DispatchInterceptorAsyncCallback>(inc.interceptorAsyncCallbackList_); + } + + adopt(inc); + } + + internal void + adopt(IncomingBase inc) + { + instance_ = inc.instance_; + //inc.instance_ = null; // Don't reset instance_. + + observer_ = inc.observer_; + inc.observer_ = null; + + servant_ = inc.servant_; + inc.servant_ = null; + + locator_ = inc.locator_; + inc.locator_ = null; + + cookie_ = inc.cookie_; + inc.cookie_ = null; + + response_ = inc.response_; + inc.response_ = false; + + compress_ = inc.compress_; + inc.compress_ = 0; + + // + // Adopt the stream - it creates less garbage. + // + os_ = inc.os_; + inc.os_ = null; + + responseHandler_ = inc.responseHandler_; + inc.responseHandler_ = null; + } + + public BasicStream startWriteParams__(Ice.FormatType format) + { + if(!response_) + { + throw new Ice.MarshalException("can't marshal out parameters for oneway dispatch"); + } + + Debug.Assert(os_.size() == Protocol.headerSize + 4); // Reply status position. + os_.writeByte((byte)0); + os_.startWriteEncaps(current_.encoding, format); + return os_; + } + + public void endWriteParams__(bool ok) + { + if(!ok && observer_ != null) + { + observer_.userException(); + } + + if(response_) + { + int save = os_.pos(); + os_.pos(Protocol.headerSize + 4); // Reply status position. + os_.writeByte(ok ? ReplyStatus.replyOK : ReplyStatus.replyUserException); + os_.pos(save); + os_.endWriteEncaps(); + } + } + + public void writeEmptyParams__() + { + if(response_) + { + Debug.Assert(os_.size() == Protocol.headerSize + 4); // Reply status position. + os_.writeByte(ReplyStatus.replyOK); + os_.writeEmptyEncaps(current_.encoding); + } + } + + public void writeParamEncaps__(byte[] v, bool ok) + { + if(!ok && observer_ != null) + { + observer_.userException(); + } + + if(response_) + { + Debug.Assert(os_.size() == Protocol.headerSize + 4); // Reply status position. + os_.writeByte(ok ? ReplyStatus.replyOK : ReplyStatus.replyUserException); + if(v == null || v.Length == 0) + { + os_.writeEmptyEncaps(current_.encoding); + } + else + { + os_.writeEncaps(v); + } + } + } + + public void writeUserException__(Ice.UserException ex, Ice.FormatType format) + { + BasicStream os__ = startWriteParams__(format); + os__.writeUserException(ex); + endWriteParams__(false); + } + + // + // These functions allow this object to be reused, rather than reallocated. + // + public virtual void reset(Instance instance, ResponseHandler handler, Ice.ConnectionI connection, + Ice.ObjectAdapter adapter, bool response, byte compress, int requestId) + { + instance_ = instance; + + // + // Don't recycle the Current object, because servants may keep a reference to it. + // + current_ = new Ice.Current(); + current_.id = new Ice.Identity(); + current_.adapter = adapter; + current_.con = connection; + current_.requestId = requestId; + + Debug.Assert(cookie_ == null); + + response_ = response; + + compress_ = compress; + + if(response_ && os_ == null) + { + os_ = new BasicStream(instance, Ice.Util.currentProtocolEncoding); + } + + responseHandler_ = handler; + interceptorAsyncCallbackList_ = null; + } + + public virtual void reclaim() + { + servant_ = null; + + locator_ = null; + + cookie_ = null; + + observer_ = null; + + if(os_ != null) + { + os_.reset(); + } + + interceptorAsyncCallbackList_ = null; + } + + protected internal void warning__(System.Exception ex) + { + Debug.Assert(instance_ != null); + + using(StringWriter sw = new StringWriter(CultureInfo.CurrentCulture)) + { + IceUtilInternal.OutputBase output = new IceUtilInternal.OutputBase(sw); + output.setUseTab(false); + output.print("dispatch exception:"); + output.print("\nidentity: " + instance_.identityToString(current_.id)); + output.print("\nfacet: " + IceUtilInternal.StringUtil.escapeString(current_.facet, "")); + output.print("\noperation: " + current_.operation); + if(current_.con != null) + { + Ice.ConnectionInfo connInfo = current_.con.getInfo(); + if(connInfo is Ice.IPConnectionInfo) + { + Ice.IPConnectionInfo ipConnInfo = (Ice.IPConnectionInfo)connInfo; + output.print("\nremote host: " + ipConnInfo.remoteAddress + " remote port: " + + ipConnInfo.remotePort.ToString()); + } + } + output.print("\n"); + output.print(ex.ToString()); + instance_.initializationData().logger.warning(sw.ToString()); + } + } + + protected bool servantLocatorFinished__(bool amd) + { + Debug.Assert(locator_ != null && servant_ != null); + try + { + locator_.finished(current_, servant_, cookie_); + return true; + } + catch(Ice.UserException ex) + { + Debug.Assert(responseHandler_ != null); + + if(observer_ != null) + { + observer_.userException(); + } + + // + // The operation may have already marshaled a reply; we must overwrite that reply. + // + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUserException); + os_.startWriteEncaps(); + os_.writeUserException(ex); + os_.endWriteEncaps(); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + responseHandler_ = null; + } + catch(System.Exception ex) + { + handleException__(ex, amd); + } + return false; + } + + protected internal void handleException__(System.Exception exc, bool amd) + { + Debug.Assert(responseHandler_ != null); + + try + { + throw exc; + } + catch(Ice.RequestFailedException ex) + { + if(ex.id == null || ex.id.name == null || ex.id.name.Length == 0) + { + ex.id = current_.id; + } + + if(ex.facet == null || ex.facet.Length == 0) + { + ex.facet = current_.facet; + } + + if(ex.operation == null || ex.operation.Length == 0) + { + ex.operation = current_.operation; + } + + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 1) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + if(ex is Ice.ObjectNotExistException) + { + os_.writeByte(ReplyStatus.replyObjectNotExist); + } + else if(ex is Ice.FacetNotExistException) + { + os_.writeByte(ReplyStatus.replyFacetNotExist); + } + else if(ex is Ice.OperationNotExistException) + { + os_.writeByte(ReplyStatus.replyOperationNotExist); + } + else + { + Debug.Assert(false); + } + ex.id.write__(os_); + + // + // For compatibility with the old FacetPath. + // + if(ex.facet == null || ex.facet.Length == 0) + { + os_.writeStringSeq(null); + } + else + { + string[] facetPath2 = { ex.facet }; + os_.writeStringSeq(facetPath2); + } + + os_.writeString(ex.operation); + + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + catch(Ice.UnknownLocalException ex) + { + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUnknownLocalException); + os_.writeString(ex.unknown); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + catch(Ice.UnknownUserException ex) + { + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUnknownUserException); + os_.writeString(ex.unknown); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + catch(Ice.UnknownException ex) + { + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUnknownException); + os_.writeString(ex.unknown); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + catch(Ice.UserException ex) + { + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUnknownUserException); + os_.writeString(ex.ice_name() + "\n" + ex.StackTrace); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + catch(Ice.Exception ex) + { + if(ex is Ice.SystemException) + { + if(responseHandler_.systemException(current_.requestId, (Ice.SystemException)ex, amd)) + { + return; + } + } + + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.ice_name()); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUnknownLocalException); + os_.writeString(ex.ice_name() + "\n" + ex.StackTrace); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + catch(System.Exception ex) + { + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + + if(observer_ != null) + { + observer_.failed(ex.GetType().FullName); + } + + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); // Reply status position. + os_.writeByte(ReplyStatus.replyUnknownException); + os_.writeString(ex.ToString()); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, amd); + } + else + { + responseHandler_.sendNoResponse(); + } + } + + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + responseHandler_ = null; + } + + protected internal Instance instance_; + protected internal Ice.Current current_; + protected internal Ice.Object servant_; + protected internal Ice.ServantLocator locator_; + protected internal System.Object cookie_; + protected internal Ice.Instrumentation.DispatchObserver observer_; + + protected internal bool response_; + protected internal byte compress_; + + protected internal BasicStream os_; + + protected ResponseHandler responseHandler_; + + protected List<Ice.DispatchInterceptorAsyncCallback> interceptorAsyncCallbackList_; + } + + sealed public class Incoming : IncomingBase, Ice.Request + { + public Incoming(Instance instance, ResponseHandler handler, Ice.ConnectionI connection, + Ice.ObjectAdapter adapter, bool response, byte compress, int requestId) + : base(instance, handler, connection, adapter, response, compress, requestId) + { + // + // Prepare the response if necessary. + // + if(response) + { + os_.writeBlob(IceInternal.Protocol.replyHdr); + + // + // Add the request ID. + // + os_.writeInt(requestId); + } + } + + public Ice.Current + getCurrent() + { + return current_; + } + + // + // These functions allow this object to be reused, rather than reallocated. + // + public override void reset(Instance instance, ResponseHandler handler, Ice.ConnectionI connection, + Ice.ObjectAdapter adapter, bool response, byte compress, int requestId) + { + _cb = null; + _inParamPos = -1; + + base.reset(instance, handler, connection, adapter, response, compress, requestId); + + // + // Prepare the response if necessary. + // + if(response) + { + os_.writeBlob(IceInternal.Protocol.replyHdr); + + // + // Add the request ID. + // + os_.writeInt(requestId); + } + } + + public override void reclaim() + { + _cb = null; + _inParamPos = -1; + + base.reclaim(); + } + + public void invoke(ServantManager servantManager, BasicStream stream) + { + _is = stream; + + int start = _is.pos(); + + // + // Read the current. + // + current_.id.read__(_is); + + // + // For compatibility with the old FacetPath. + // + string[] facetPath = _is.readStringSeq(); + if(facetPath.Length > 0) + { + if(facetPath.Length > 1) + { + throw new Ice.MarshalException(); + } + current_.facet = facetPath[0]; + } + else + { + current_.facet = ""; + } + + current_.operation = _is.readString(); + current_.mode = (Ice.OperationMode)(int)_is.readByte(); + current_.ctx = new Dictionary<string, string>(); + int sz = _is.readSize(); + while(sz-- > 0) + { + string first = _is.readString(); + string second = _is.readString(); + current_.ctx[first] = second; + } + + Ice.Instrumentation.CommunicatorObserver obsv = instance_.initializationData().observer; + if(obsv != null) + { + // Read the encapsulation size. + int size = _is.readInt(); + _is.pos(_is.pos() - 4); + + observer_ = obsv.getDispatchObserver(current_, _is.pos() - start + size); + if(observer_ != null) + { + observer_.attach(); + } + } + + // + // Don't put the code above into the try block below. Exceptions + // in the code above are considered fatal, and must propagate to + // the caller of this operation. + // + + if(servantManager != null) + { + servant_ = servantManager.findServant(current_.id, current_.facet); + if(servant_ == null) + { + locator_ = servantManager.findServantLocator(current_.id.category); + if(locator_ == null && current_.id.category.Length > 0) + { + locator_ = servantManager.findServantLocator(""); + } + + if(locator_ != null) + { + try + { + servant_ = locator_.locate(current_, out cookie_); + } + catch(Ice.UserException ex) + { + Ice.EncodingVersion encoding = _is.skipEncaps(); // Required for batch requests. + + if(observer_ != null) + { + observer_.userException(); + } + + if(response_) + { + os_.writeByte(ReplyStatus.replyUserException); + os_.startWriteEncaps(encoding, Ice.FormatType.DefaultFormat); + os_.writeUserException(ex); + os_.endWriteEncaps(); + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, false); + } + else + { + responseHandler_.sendNoResponse(); + } + + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + responseHandler_ = null; + return; + } + catch(System.Exception ex) + { + _is.skipEncaps(); // Required for batch requests. + handleException__(ex, false); + return; + } + } + } + } + + try + { + if(servant_ != null) + { + // + // DispatchAsync is a "pseudo dispatch status", used internally only + // to indicate async dispatch. + // + if(servant_.dispatch__(this, current_) == Ice.DispatchStatus.DispatchAsync) + { + // + // If this was an asynchronous dispatch, we're done here. + // + return; + } + + if(locator_ != null && !servantLocatorFinished__(false)) + { + return; + } + } + else + { + // + // Skip the input parameters, this is required for reading + // the next batch request if dispatching batch requests. + // + _is.skipEncaps(); + + if(servantManager != null && servantManager.hasServant(current_.id)) + { + throw new Ice.FacetNotExistException(current_.id, current_.facet, current_.operation); + } + else + { + throw new Ice.ObjectNotExistException(current_.id, current_.facet, current_.operation); + } + } + } + catch(System.Exception ex) + { + if(servant_ != null && locator_ != null && !servantLocatorFinished__(false)) + { + return; + } + handleException__(ex, false); + return; + } + + // + // Don't put the code below into the try block above. Exceptions + // in the code below are considered fatal, and must propagate to + // the caller of this operation. + // + + Debug.Assert(responseHandler_ != null); + + if(response_) + { + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, false); + } + else + { + responseHandler_.sendNoResponse(); + } + + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + responseHandler_ = null; + } + + public void push(Ice.DispatchInterceptorAsyncCallback cb) + { + if(interceptorAsyncCallbackList_ == null) + { + interceptorAsyncCallbackList_ = new List<Ice.DispatchInterceptorAsyncCallback>(); + } + + interceptorAsyncCallbackList_.Insert(0, cb); + } + + public void pop() + { + Debug.Assert(interceptorAsyncCallbackList_ != null); + interceptorAsyncCallbackList_.RemoveAt(0); + } + + public void startOver() + { + if(_inParamPos == -1) + { + // + // That's the first startOver, so almost nothing to do + // + _inParamPos = _is.pos(); + } + else + { + killAsync(); + + // + // Let's rewind _is and clean-up os_ + // + _is.pos(_inParamPos); + if(response_) + { + os_.resize(Protocol.headerSize + 4, false); + } + } + } + + public void killAsync() + { + // + // Always runs in the dispatch thread + // + if(_cb != null) + { + // + // May raise ResponseSentException + // + _cb.deactivate__(this); + _cb = null; + } + } + + public BasicStream startReadParams() + { + // + // Remember the encoding used by the input parameters, we'll + // encode the response parameters with the same encoding. + // + current_.encoding = _is.startReadEncaps(); + return _is; + } + + public void endReadParams() + { + _is.endReadEncaps(); + } + + public void readEmptyParams() + { + current_.encoding = _is.skipEmptyEncaps(); + } + + public byte[] readParamEncaps() + { + return _is.readEncaps(out current_.encoding); + } + + internal void setActive(IncomingAsync cb) + { + Debug.Assert(_cb == null); + _cb = cb; + } + + internal bool isRetriable() + { + return _inParamPos != -1; + } + + public Incoming next; // For use by Connection. + + private BasicStream _is; + + private IncomingAsync _cb; + private int _inParamPos = -1; + } + +} diff --git a/csharp/src/Ice/IncomingAsync.cs b/csharp/src/Ice/IncomingAsync.cs new file mode 100644 index 00000000000..5f9205c027e --- /dev/null +++ b/csharp/src/Ice/IncomingAsync.cs @@ -0,0 +1,247 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Diagnostics; + + public class IncomingAsync : IncomingBase, Ice.AMDCallback + { + public IncomingAsync(Incoming inc) + : base(inc) + { + _retriable = inc.isRetriable(); + if(_retriable) + { + inc.setActive(this); + _active = true; + } + } + + virtual public void ice_exception(System.Exception ex) + { + // + // Only call exception__ if this incoming is not retriable or if + // all the interceptors return true and no response has been sent + // yet. + // + + if(_retriable) + { + try + { + if(interceptorAsyncCallbackList_ != null) + { + foreach(Ice.DispatchInterceptorAsyncCallback cb in interceptorAsyncCallbackList_) + { + if(cb.exception(ex) == false) + { + return; + } + } + } + } + catch(System.Exception) + { + return; + } + + lock(this) + { + if(!_active) + { + return; + } + _active = false; + } + } + + if(responseHandler_ != null) + { + exception__(ex); + } + else + { + // + // Response has already been sent. + // + if(instance_.initializationData().properties.getPropertyAsIntWithDefault("Ice.Warn.Dispatch", 1) > 0) + { + warning__(ex); + } + } + } + + internal void deactivate__(Incoming inc) + { + Debug.Assert(_retriable); + + lock(this) + { + if(!_active) + { + // + // Since __deactivate can only be called on an active object, + // this means the response has already been sent (see validateXXX below) + // + throw new Ice.ResponseSentException(); + } + _active = false; + } + inc.adopt(this); + } + + protected void response__() + { + try + { + if(locator_ != null && !servantLocatorFinished__(true)) + { + return; + } + + Debug.Assert(responseHandler_ != null); + + if(response_) + { + if(observer_ != null) + { + observer_.reply(os_.size() - Protocol.headerSize - 4); + } + responseHandler_.sendResponse(current_.requestId, os_, compress_, true); + } + else + { + responseHandler_.sendNoResponse(); + } + + if(observer_ != null) + { + observer_.detach(); + observer_ = null; + } + responseHandler_ = null; + } + catch(Ice.LocalException ex) + { + responseHandler_.invokeException(current_.requestId, ex, 1, true); + } + } + + protected internal void exception__(System.Exception exc) + { + try + { + if(locator_ != null && !servantLocatorFinished__(true)) + { + return; + } + + handleException__(exc, true); + } + catch(Ice.LocalException ex) + { + responseHandler_.invokeException(current_.requestId, ex, 1, true); + } + } + + protected internal BasicStream getOs__() + { + return os_; + } + + protected bool validateResponse__(bool ok) + { + // + // Only returns true if this incoming is not retriable or if all + // the interceptors return true and no response has been sent + // yet. Upon getting a true return value, the caller should send + // the response. + // + + if(_retriable) + { + try + { + if(interceptorAsyncCallbackList_ != null) + { + foreach(Ice.DispatchInterceptorAsyncCallback cb in interceptorAsyncCallbackList_) + { + if(cb.response(ok) == false) + { + return false; + } + } + } + } + catch(System.Exception) + { + return false; + } + + lock(this) + { + if(!_active) + { + return false; + } + _active = false; + } + } + return true; + } + + private readonly bool _retriable; + private bool _active = false; // only meaningful when _retriable == true + } +} + +namespace Ice +{ + + /// <summary> + /// Callback interface for Blobject AMD servants. + /// </summary> + public interface AMD_Object_ice_invoke : Ice.AMDCallback + { + /// <summary> + /// Indicates to the Ice run time that an operation + /// completed. + /// </summary> + /// <param name="ok">True indicates that the operation + /// completed successfully; false indicates that the + /// operation raised a user exception.</param> + /// <param name="outEncaps">The encoded out-parameters for the operation or, + /// if ok is false, the encoded user exception.</param> + void ice_response(bool ok, byte[] outEncaps); + } + + sealed class _AMD_Object_ice_invoke : IceInternal.IncomingAsync, AMD_Object_ice_invoke + { + public _AMD_Object_ice_invoke(IceInternal.Incoming inc) + : base(inc) + { + } + + public void ice_response(bool ok, byte[] outEncaps) + { + try + { + writeParamEncaps__(outEncaps, ok); + } + catch(Ice.LocalException ex) + { + exception__(ex); + return; + } + response__(); + } + } +} diff --git a/csharp/src/Ice/Instance.cs b/csharp/src/Ice/Instance.cs new file mode 100644 index 00000000000..8dd99f3860f --- /dev/null +++ b/csharp/src/Ice/Instance.cs @@ -0,0 +1,1581 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Text; + using System.Threading; + using System; + + public sealed class BufSizeWarnInfo + { + // Whether send size warning has been emitted + public bool sndWarn; + + // The send size for which the warning wwas emitted + public int sndSize; + + // Whether receive size warning has been emitted + public bool rcvWarn; + + // The receive size for which the warning wwas emitted + public int rcvSize; + } + + public sealed class Instance + { + private class ObserverUpdaterI : Ice.Instrumentation.ObserverUpdater + { + public ObserverUpdaterI(Instance instance) + { + _instance = instance; + } + + public void updateConnectionObservers() + { + _instance.updateConnectionObservers(); + } + + public void updateThreadObservers() + { + _instance.updateThreadObservers(); + } + + private Instance _instance; + } + + public bool destroyed() + { + return _state == StateDestroyed; + } + + public Ice.InitializationData initializationData() + { + // + // No check for destruction. It must be possible to access the + // initialization data after destruction. + // + // No mutex lock, immutable. + // + return _initData; + } + + public TraceLevels traceLevels() + { + // No mutex lock, immutable. + Debug.Assert(_traceLevels != null); + return _traceLevels; + } + + public DefaultsAndOverrides defaultsAndOverrides() + { + // No mutex lock, immutable. + Debug.Assert(_defaultsAndOverrides != null); + return _defaultsAndOverrides; + } + +#if COMPACT || SILVERLIGHT + public string[] factoryAssemblies() + { + return _factoryAssemblies; + } +#endif + + public RouterManager routerManager() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_routerManager != null); + return _routerManager; + } + } + + public LocatorManager locatorManager() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_locatorManager != null); + return _locatorManager; + } + } + + public ReferenceFactory referenceFactory() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_referenceFactory != null); + return _referenceFactory; + } + } + + public RequestHandlerFactory requestHandlerFactory() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_requestHandlerFactory != null); + return _requestHandlerFactory; + } + } + + public ProxyFactory proxyFactory() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_proxyFactory != null); + return _proxyFactory; + } + } + + public OutgoingConnectionFactory outgoingConnectionFactory() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_outgoingConnectionFactory != null); + return _outgoingConnectionFactory; + } + } + + public ObjectFactoryManager servantFactoryManager() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_servantFactoryManager != null); + return _servantFactoryManager; + } + } + + public ObjectAdapterFactory objectAdapterFactory() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_objectAdapterFactory != null); + return _objectAdapterFactory; + } + } + + public int protocolSupport() + { + return _protocolSupport; + } + + public bool preferIPv6() + { + return _preferIPv6; + } + + public NetworkProxy networkProxy() + { + return _networkProxy; + } + + public ThreadPool clientThreadPool() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_clientThreadPool != null); + return _clientThreadPool; + } + } + + public ThreadPool serverThreadPool() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_serverThreadPool == null) // Lazy initialization. + { + if(_state == StateDestroyInProgress) + { + throw new Ice.CommunicatorDestroyedException(); + } + int timeout = _initData.properties.getPropertyAsInt("Ice.ServerIdleTime"); + _serverThreadPool = new ThreadPool(this, "Ice.ThreadPool.Server", timeout); + } + + return _serverThreadPool; + } + } + + public AsyncIOThread + asyncIOThread() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_asyncIOThread == null) // Lazy initialization. + { + _asyncIOThread = new AsyncIOThread(this); + } + + return _asyncIOThread; + } + } + +#if !SILVERLIGHT + public EndpointHostResolver endpointHostResolver() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_endpointHostResolver != null); + return _endpointHostResolver; + } + } +#endif + public RetryQueue + retryQueue() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_retryQueue != null); + return _retryQueue; + } + } + + public Timer + timer() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_timer != null); + return _timer; + } + } + + public EndpointFactoryManager endpointFactoryManager() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_endpointFactoryManager != null); + return _endpointFactoryManager; + } + } + + public Ice.PluginManager pluginManager() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Debug.Assert(_pluginManager != null); + return _pluginManager; + } + } + + public int messageSizeMax() + { + // No mutex lock, immutable. + return _messageSizeMax; + } + + public int batchAutoFlushSize() + { + // No mutex lock, immutable. + return _batchAutoFlushSize; + } + + public int cacheMessageBuffers() + { + // No mutex lock, immutable. + return _cacheMessageBuffers; + } + + public ACMConfig clientACM() + { + // No mutex lock, immutable. + return _clientACM; + } + + public ACMConfig serverACM() + { + // No mutex lock, immutable. + return _serverACM; + } + + public Ice.ImplicitContextI getImplicitContext() + { + return _implicitContext; + } + + public Ice.Identity stringToIdentity(string s) + { + return Ice.Util.stringToIdentity(s); + } + + public string identityToString(Ice.Identity ident) + { + return Ice.Util.identityToString(ident); + } + + + public Ice.ObjectPrx + createAdmin(Ice.ObjectAdapter adminAdapter, Ice.Identity adminIdentity) + { + bool createAdapter = (adminAdapter == null); + + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(adminIdentity == null || string.IsNullOrEmpty(adminIdentity.name)) + { + throw new Ice.IllegalIdentityException(adminIdentity); + } + + if(_adminAdapter != null) + { + throw new Ice.InitializationException("Admin already created"); + } + + if(!_adminEnabled) + { + throw new Ice.InitializationException("Admin is disabled"); + } + + if(createAdapter) + { + if(_initData.properties.getProperty("Ice.Admin.Endpoints").Length > 0) + { + adminAdapter = _objectAdapterFactory.createObjectAdapter("Ice.Admin", null); + } + else + { + throw new Ice.InitializationException("Ice.Admin.Endpoints is not set"); + } + } + + _adminIdentity = adminIdentity; + _adminAdapter = adminAdapter; + addAllAdminFacets(); + } + + if(createAdapter) + { + try + { + adminAdapter.activate(); + } + catch(Ice.LocalException) + { + // + // We cleanup _adminAdapter, however this error is not recoverable + // (can't call again getAdmin() after fixing the problem) + // since all the facets (servants) in the adapter are lost + // + adminAdapter.destroy(); + lock(this) + { + _adminAdapter = null; + } + throw; + } + } + setServerProcessProxy(adminAdapter, adminIdentity); + return adminAdapter.createProxy(adminIdentity); + } + + public Ice.ObjectPrx + getAdmin() + { + Ice.ObjectAdapter adminAdapter; + Ice.Identity adminIdentity; + + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_adminAdapter != null) + { + return _adminAdapter.createProxy(_adminIdentity); + } + else if(_adminEnabled) + { + if(_initData.properties.getProperty("Ice.Admin.Endpoints").Length > 0) + { + adminAdapter = _objectAdapterFactory.createObjectAdapter("Ice.Admin", null); + } + else + { + return null; + } + adminIdentity = new Ice.Identity("admin", _initData.properties.getProperty("Ice.Admin.InstanceName")); + if(adminIdentity.category.Length == 0) + { + adminIdentity.category = System.Guid.NewGuid().ToString(); + } + + _adminIdentity = adminIdentity; + _adminAdapter = adminAdapter; + addAllAdminFacets(); + // continue below outside synchronization + } + else + { + return null; + } + } + + try + { + adminAdapter.activate(); + } + catch(Ice.LocalException) + { + // + // We cleanup _adminAdapter, however this error is not recoverable + // (can't call again getAdmin() after fixing the problem) + // since all the facets (servants) in the adapter are lost + // + adminAdapter.destroy(); + lock(this) + { + _adminAdapter = null; + } + throw; + } + + setServerProcessProxy(adminAdapter, adminIdentity); + return adminAdapter.createProxy(adminIdentity); + } + + public void + addAdminFacet(Ice.Object servant, string facet) + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_adminAdapter == null || (_adminFacetFilter.Count > 0 && !_adminFacetFilter.Contains(facet))) + { + if(_adminFacets.ContainsKey(facet)) + { + throw new Ice.AlreadyRegisteredException("facet", facet); + } + _adminFacets.Add(facet, servant); + } + else + { + _adminAdapter.addFacet(servant, _adminIdentity, facet); + } + } + } + + public Ice.Object + removeAdminFacet(string facet) + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Ice.Object result = null; + if(_adminAdapter == null || (_adminFacetFilter.Count > 0 && !_adminFacetFilter.Contains(facet))) + { + try + { + result = _adminFacets[facet]; + } + catch(KeyNotFoundException) + { + throw new Ice.NotRegisteredException("facet", facet); + } + + _adminFacets.Remove(facet); + } + else + { + result = _adminAdapter.removeFacet(_adminIdentity, facet); + } + return result; + } + } + + public Ice.Object + findAdminFacet(string facet) + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Ice.Object result = null; + if(_adminAdapter == null || (_adminFacetFilter.Count > 0 && !_adminFacetFilter.Contains(facet))) + { + try + { + result = _adminFacets[facet]; + } + catch(KeyNotFoundException) + { + } + } + else + { + result = _adminAdapter.findFacet(_adminIdentity, facet); + } + return result; + } + } + + public Dictionary<string, Ice.Object> + findAllAdminFacets() + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + if(_adminAdapter == null) + { + return new Dictionary<string, Ice.Object>(_adminFacets); + } + else + { + Dictionary<string, Ice.Object> result = _adminAdapter.findAllFacets(_adminIdentity); + if(_adminFacets.Count > 0) + { + foreach(KeyValuePair<string, Ice.Object> p in _adminFacets) + { + result.Add(p.Key, p.Value); + } + } + return result; + } + } + } + + public void + setDefaultLocator(Ice.LocatorPrx locator) + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + _referenceFactory = _referenceFactory.setDefaultLocator(locator); + } + } + + public void + setDefaultRouter(Ice.RouterPrx router) + { + lock(this) + { + if(_state == StateDestroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + _referenceFactory = _referenceFactory.setDefaultRouter(router); + } + } + + public void + setLogger(Ice.Logger logger) + { + // + // No locking, as it can only be called during plug-in loading + // + _initData.logger = logger; + } + + public void + setThreadHook(Ice.ThreadNotification threadHook) + { + // + // No locking, as it can only be called during plug-in loading + // + _initData.threadHook = threadHook; + } + + // + // Only for use by Ice.CommunicatorI + // + public Instance(Ice.Communicator communicator, Ice.InitializationData initData) + { + _state = StateActive; + _initData = initData; + + try + { + if(_initData.properties == null) + { + _initData.properties = Ice.Util.createProperties(); + } +#if !SILVERLIGHT && !UNITY + lock(_staticLock) + { + if(!_oneOfDone) + { + string stdOut = _initData.properties.getProperty("Ice.StdOut"); + string stdErr = _initData.properties.getProperty("Ice.StdErr"); + + System.IO.StreamWriter outStream = null; + + if(stdOut.Length > 0) + { + try + { + outStream = System.IO.File.AppendText(stdOut); + } + catch(System.IO.IOException ex) + { + Ice.FileException fe = new Ice.FileException(ex); + fe.path = stdOut; + throw fe; + } + outStream.AutoFlush = true; + System.Console.Out.Close(); + System.Console.SetOut(outStream); + } + if(stdErr.Length > 0) + { + if(stdErr.Equals(stdOut)) + { + System.Console.SetError(outStream); + } + else + { + System.IO.StreamWriter errStream = null; + try + { + errStream = System.IO.File.AppendText(stdErr); + } + catch(System.IO.IOException ex) + { + Ice.FileException fe = new Ice.FileException(ex); + fe.path = stdErr; + throw fe; + } + errStream.AutoFlush = true; + System.Console.Error.Close(); + System.Console.SetError(errStream); + } + } + + _oneOfDone = true; + } + } +#endif + + if(_initData.logger == null) + { +#if !SILVERLIGHT && !UNITY + string logfile = _initData.properties.getProperty("Ice.LogFile"); + if(_initData.properties.getPropertyAsInt("Ice.UseSyslog") > 0 && + AssemblyUtil.platform_ != AssemblyUtil.Platform.Windows) + { + if(logfile.Length != 0) + { + throw new Ice.InitializationException("Ice.LogFile and Ice.UseSyslog cannot both be set."); + } + _initData.logger = new Ice.SysLoggerI(_initData.properties.getProperty("Ice.ProgramName"), + _initData.properties.getPropertyWithDefault("Ice.SyslogFacility", "LOG_USER")); + } + else if(logfile.Length != 0) + { + + _initData.logger = + new Ice.FileLoggerI(_initData.properties.getProperty("Ice.ProgramName"), logfile); + } + else if(Ice.Util.getProcessLogger() is Ice.LoggerI) + { + // + // Ice.ConsoleListener is enabled by default. + // +# if COMPACT + _initData.logger = + new Ice.ConsoleLoggerI(_initData.properties.getProperty("Ice.ProgramName")); +# else + bool console = + _initData.properties.getPropertyAsIntWithDefault("Ice.ConsoleListener", 1) > 0; + _initData.logger = + new Ice.TraceLoggerI(_initData.properties.getProperty("Ice.ProgramName"), console); +# endif + } +#else + if(Ice.Util.getProcessLogger() is Ice.LoggerI) + { + _initData.logger = + new Ice.ConsoleLoggerI(_initData.properties.getProperty("Ice.ProgramName")); + } +#endif + else + { + _initData.logger = Ice.Util.getProcessLogger(); + } + } + + _traceLevels = new TraceLevels(_initData.properties); + + _defaultsAndOverrides = new DefaultsAndOverrides(_initData.properties, _initData.logger); + + _clientACM = new ACMConfig(_initData.properties, + _initData.logger, + "Ice.ACM.Client", + new ACMConfig(_initData.properties, _initData.logger, "Ice.ACM", + new ACMConfig(false))); + + _serverACM = new ACMConfig(_initData.properties, + _initData.logger, + "Ice.ACM.Server", + new ACMConfig(_initData.properties, _initData.logger, "Ice.ACM", + new ACMConfig(true))); + +#if COMPACT || SILVERLIGHT + char[] separators = { ' ', '\t', '\n', '\r' }; + _factoryAssemblies = _initData.properties.getProperty("Ice.FactoryAssemblies").Split(separators); +#endif + { + const int defaultMessageSizeMax = 1024; + int num = + _initData.properties.getPropertyAsIntWithDefault("Ice.MessageSizeMax", defaultMessageSizeMax); + if(num < 1 || num > 0x7fffffff / 1024) + { + _messageSizeMax = 0x7fffffff; + } + else + { + _messageSizeMax = num * 1024; // Property is in kilobytes, _messageSizeMax in bytes + } + } + + if(_initData.properties.getProperty("Ice.BatchAutoFlushSize").Length == 0 && + _initData.properties.getProperty("Ice.BatchAutoFlush").Length > 0) + { + if(_initData.properties.getPropertyAsInt("Ice.BatchAutoFlush") > 0) + { + _batchAutoFlushSize = _messageSizeMax; + } + } + else + { + int num = _initData.properties.getPropertyAsIntWithDefault("Ice.BatchAutoFlushSize", 1024); // 1MB + if(num < 1) + { + _batchAutoFlushSize = num; + } + else if(num > 0x7fffffff / 1024) + { + _batchAutoFlushSize = 0x7fffffff; + } + else + { + _batchAutoFlushSize = num * 1024; // Property is in kilobytes, _batchAutoFlushSize in bytes + } + } + + _cacheMessageBuffers = _initData.properties.getPropertyAsIntWithDefault("Ice.CacheMessageBuffers", 2); + + _implicitContext = Ice.ImplicitContextI.create(_initData.properties.getProperty("Ice.ImplicitContext")); + _routerManager = new RouterManager(); + + _locatorManager = new LocatorManager(_initData.properties); + + _referenceFactory = new ReferenceFactory(this, communicator); + + _proxyFactory = new ProxyFactory(this); + + _requestHandlerFactory = new RequestHandlerFactory(this); + + bool isIPv6Supported = Network.isIPv6Supported(); + bool ipv4 = _initData.properties.getPropertyAsIntWithDefault("Ice.IPv4", 1) > 0; + bool ipv6 = _initData.properties.getPropertyAsIntWithDefault("Ice.IPv6", isIPv6Supported ? 1 : 0) > 0; + if(!ipv4 && !ipv6) + { + throw new Ice.InitializationException("Both IPV4 and IPv6 support cannot be disabled."); + } + else if(ipv4 && ipv6) + { + _protocolSupport = Network.EnableBoth; + } + else if(ipv4) + { + _protocolSupport = Network.EnableIPv4; + } + else + { + _protocolSupport = Network.EnableIPv6; + } + _preferIPv6 = _initData.properties.getPropertyAsInt("Ice.PreferIPv6Address") > 0; + + _networkProxy = createNetworkProxy(_initData.properties, _protocolSupport); + + _endpointFactoryManager = new EndpointFactoryManager(this); + + ProtocolInstance tcpInstance = new ProtocolInstance(this, Ice.TCPEndpointType.value, "tcp", false); + _endpointFactoryManager.add(new TcpEndpointFactory(tcpInstance)); + + ProtocolInstance udpInstance = new ProtocolInstance(this, Ice.UDPEndpointType.value, "udp", false); + _endpointFactoryManager.add(new UdpEndpointFactory(udpInstance)); + +#if !SILVERLIGHT + _pluginManager = new Ice.PluginManagerI(communicator); +#endif + + _outgoingConnectionFactory = new OutgoingConnectionFactory(communicator, this); + + _servantFactoryManager = new ObjectFactoryManager(); + + _objectAdapterFactory = new ObjectAdapterFactory(this, communicator); + + _retryQueue = new RetryQueue(this); + } + catch(Ice.LocalException) + { + destroy(); + throw; + } + } + + public void finishSetup(ref string[] args, Ice.Communicator communicator) + { + // + // Load plug-ins. + // + Debug.Assert(_serverThreadPool == null); +#if !SILVERLIGHT + Ice.PluginManagerI pluginManagerImpl = (Ice.PluginManagerI)_pluginManager; + pluginManagerImpl.loadPlugins(ref args); +#endif + + // + // Add WS and WSS endpoint factories if TCP/SSL factories are installed. + // + EndpointFactory tcpFactory = _endpointFactoryManager.get(Ice.TCPEndpointType.value); + if(tcpFactory != null) + { + ProtocolInstance instance = new ProtocolInstance(this, Ice.WSEndpointType.value, "ws", false); + _endpointFactoryManager.add(new WSEndpointFactory(instance, tcpFactory.clone(instance))); + } + EndpointFactory sslFactory = _endpointFactoryManager.get(Ice.SSLEndpointType.value); + if(sslFactory != null) + { + ProtocolInstance instance = new ProtocolInstance(this, Ice.WSSEndpointType.value, "wss", true); + _endpointFactoryManager.add(new WSEndpointFactory(instance, sslFactory.clone(instance))); + } + + // + // Create Admin facets, if enabled. + // + // Note that any logger-dependent admin facet must be created after we load all plugins, + // since one of these plugins can be a Logger plugin that sets a new logger during loading + // + + if(_initData.properties.getProperty("Ice.Admin.Enabled").Length == 0) + { + _adminEnabled = _initData.properties.getProperty("Ice.Admin.Endpoints").Length > 0; + } + else + { + _adminEnabled = _initData.properties.getPropertyAsInt("Ice.Admin.Enabled") > 0; + } + + string[] facetFilter = _initData.properties.getPropertyAsList("Ice.Admin.Facets"); + if(facetFilter.Length > 0) + { + foreach(string s in facetFilter) + { + _adminFacetFilter.Add(s); + } + } + + if(_adminEnabled) + { + // + // Process facet + // + string processFacetName = "Process"; + if(_adminFacetFilter.Count == 0 || _adminFacetFilter.Contains(processFacetName)) + { + _adminFacets.Add(processFacetName, new ProcessI(communicator)); + } + + // + // Logger facet + // + string loggerFacetName = "Logger"; + if(_adminFacetFilter.Count == 0 || _adminFacetFilter.Contains(loggerFacetName)) + { + LoggerAdminLogger logger = new LoggerAdminLoggerI(_initData.properties, _initData.logger); + setLogger(logger); + _adminFacets.Add(loggerFacetName, logger.getFacet()); + } + + // + // Properties facet + // + string propertiesFacetName = "Properties"; + PropertiesAdminI propsAdmin = null; + if(_adminFacetFilter.Count == 0 || _adminFacetFilter.Contains(propertiesFacetName)) + { + propsAdmin= new PropertiesAdminI(_initData.properties, _initData.logger); + _adminFacets.Add(propertiesFacetName, propsAdmin); + } + + // + // Metrics facet + // + string metricsFacetName = "Metrics"; + if(_adminFacetFilter.Count == 0 || _adminFacetFilter.Contains(metricsFacetName)) + { + CommunicatorObserverI observer = new CommunicatorObserverI(_initData); + _initData.observer = observer; + _adminFacets.Add(metricsFacetName, observer.getFacet()); + + // + // Make sure the admin plugin receives property updates. + // + if(propsAdmin != null) + { + propsAdmin.addUpdateCallback(observer.getFacet()); + } + } + } + + // + // Set observer updater + // + if(_initData.observer != null) + { + _initData.observer.setObserverUpdater(new ObserverUpdaterI(this)); + } + + // + // Create threads. + // + try + { +#if !SILVERLIGHT + if(initializationData().properties.getProperty("Ice.ThreadPriority").Length > 0) + { + ThreadPriority priority = IceInternal.Util.stringToThreadPriority( + initializationData().properties.getProperty("Ice.ThreadPriority")); + _timer = new Timer(this, priority); + } + else + { + _timer = new Timer(this); + } +#else + _timer = new Timer(this); +#endif + } + catch(System.Exception ex) + { + string s = "cannot create thread for timer:\n" + ex; + _initData.logger.error(s); + throw; + } + +#if !SILVERLIGHT + try + { + _endpointHostResolver = new EndpointHostResolver(this); + } + catch(System.Exception ex) + { + string s = "cannot create thread for endpoint host resolver:\n" + ex; + _initData.logger.error(s); + throw; + } +#endif + _clientThreadPool = new ThreadPool(this, "Ice.ThreadPool.Client", 0); + + // + // The default router/locator may have been set during the loading of plugins. + // Therefore we make sure it is not already set before checking the property. + // + if(_referenceFactory.getDefaultRouter() == null) + { + Ice.RouterPrx r = Ice.RouterPrxHelper.uncheckedCast( + _proxyFactory.propertyToProxy("Ice.Default.Router")); + if(r != null) + { + _referenceFactory = _referenceFactory.setDefaultRouter(r); + } + } + + if(_referenceFactory.getDefaultLocator() == null) + { + Ice.LocatorPrx l = Ice.LocatorPrxHelper.uncheckedCast( + _proxyFactory.propertyToProxy("Ice.Default.Locator")); + if(l != null) + { + _referenceFactory = _referenceFactory.setDefaultLocator(l); + } + } + + // + // Show process id if requested (but only once). + // +#if !SILVERLIGHT + lock(this) + { + if(!_printProcessIdDone && _initData.properties.getPropertyAsInt("Ice.PrintProcessId") > 0) + { + using(Process p = Process.GetCurrentProcess()) + { + System.Console.WriteLine(p.Id); + } + _printProcessIdDone = true; + } + } +#endif + + // + // Server thread pool initialization is lazy in serverThreadPool(). + // + + // + // An application can set Ice.InitPlugins=0 if it wants to postpone + // initialization until after it has interacted directly with the + // plug-ins. + // +#if !SILVERLIGHT + if(_initData.properties.getPropertyAsIntWithDefault("Ice.InitPlugins", 1) > 0) + { + pluginManagerImpl.initializePlugins(); + } +#endif + // + // This must be done last as this call creates the Ice.Admin object adapter + // and eventually registers a process proxy with the Ice locator (allowing + // remote clients to invoke on Ice.Admin facets as soon as it's registered). + // + if(_initData.properties.getPropertyAsIntWithDefault("Ice.Admin.DelayCreation", 0) <= 0) + { + getAdmin(); + } + } + + // + // Only for use by Ice.CommunicatorI + // + public void destroy() + { + lock(this) + { + // + // If destroy is in progress, wait for it to be done. This + // is necessary in case destroy() is called concurrently + // by multiple threads. + // + while(_state == StateDestroyInProgress) + { + Monitor.Wait(this); + } + + if(_state == StateDestroyed) + { + return; + } + _state = StateDestroyInProgress; + } + + // + // Shutdown and destroy all the incoming and outgoing Ice + // connections and wait for the connections to be finished. + // + if(_objectAdapterFactory != null) + { + _objectAdapterFactory.shutdown(); + } + + if(_outgoingConnectionFactory != null) + { + _outgoingConnectionFactory.destroy(); + } + + if(_objectAdapterFactory != null) + { + _objectAdapterFactory.destroy(); + } + + if(_outgoingConnectionFactory != null) + { + _outgoingConnectionFactory.waitUntilFinished(); + } + + if(_retryQueue != null) + { + _retryQueue.destroy(); // Must be called before destroying thread pools. + } + + if(_initData.observer != null) + { + _initData.observer.setObserverUpdater(null); + } + + LoggerAdminLogger logger = _initData.logger as LoggerAdminLogger; + if(logger != null) + { + logger.destroy(); + } + + // + // Now, destroy the thread pools. This must be done *only* after + // all the connections are finished (the connections destruction + // can require invoking callbacks with the thread pools). + // + if(_serverThreadPool != null) + { + _serverThreadPool.destroy(); + } + if(_clientThreadPool != null) + { + _clientThreadPool.destroy(); + } + if(_asyncIOThread != null) + { + _asyncIOThread.destroy(); + } +#if !SILVERLIGHT + if(_endpointHostResolver != null) + { + _endpointHostResolver.destroy(); + } +#endif + + // + // Wait for all the threads to be finished. + // + if(_timer != null) + { + _timer.destroy(); + } + if(_clientThreadPool != null) + { + _clientThreadPool.joinWithAllThreads(); + } + if(_serverThreadPool != null) + { + _serverThreadPool.joinWithAllThreads(); + } + if(_asyncIOThread != null) + { + _asyncIOThread.joinWithThread(); + } +#if !SILVERLIGHT + if(_endpointHostResolver != null) + { + _endpointHostResolver.joinWithThread(); + } +#endif + + if(_servantFactoryManager != null) + { + _servantFactoryManager.destroy(); + } + + if(_routerManager != null) + { + _routerManager.destroy(); + } + + if(_locatorManager != null) + { + _locatorManager.destroy(); + } + + if(_endpointFactoryManager != null) + { + _endpointFactoryManager.destroy(); + } + + if(_initData.properties.getPropertyAsInt("Ice.Warn.UnusedProperties") > 0) + { + List<string> unusedProperties = ((Ice.PropertiesI)_initData.properties).getUnusedProperties(); + if (unusedProperties.Count != 0) + { + StringBuilder message = new StringBuilder("The following properties were set but never read:"); + foreach (string s in unusedProperties) + { + message.Append("\n "); + message.Append(s); + } + _initData.logger.warning(message.ToString()); + } + } + + // + // Destroy last so that a Logger plugin can receive all log/traces before its destruction. + // + if(_pluginManager != null) + { + _pluginManager.destroy(); + } + + lock(this) + { + _objectAdapterFactory = null; + _outgoingConnectionFactory = null; + _retryQueue = null; + + _serverThreadPool = null; + _clientThreadPool = null; + _asyncIOThread = null; +#if !SILVERLIGHT + _endpointHostResolver = null; +#endif + _timer = null; + + _servantFactoryManager = null; + _referenceFactory = null; + _requestHandlerFactory = null; + _proxyFactory = null; + _routerManager = null; + _locatorManager = null; + _endpointFactoryManager = null; + _pluginManager = null; + + _adminAdapter = null; + _adminFacets.Clear(); + + _state = StateDestroyed; + Monitor.PulseAll(this); + } + } + + public BufSizeWarnInfo getBufSizeWarn(short type) + { + lock(_setBufSizeWarn) + { + BufSizeWarnInfo info; + if(!_setBufSizeWarn.ContainsKey(type)) + { + info = new BufSizeWarnInfo(); + info.sndWarn = false; + info.sndSize = -1; + info.rcvWarn = false; + info.rcvSize = -1; + _setBufSizeWarn.Add(type, info); + } + else + { + info = _setBufSizeWarn[type]; + } + return info; + } + } + + public void setSndBufSizeWarn(short type, int size) + { + lock(_setBufSizeWarn) + { + BufSizeWarnInfo info = getBufSizeWarn(type); + info.sndWarn = true; + info.sndSize = size; + _setBufSizeWarn[type] = info; + } + } + + public void setRcvBufSizeWarn(short type, int size) + { + lock(_setBufSizeWarn) + { + BufSizeWarnInfo info = getBufSizeWarn(type); + info.rcvWarn = true; + info.rcvSize = size; + _setBufSizeWarn[type] = info; + } + } + + internal void updateConnectionObservers() + { + try + { + Debug.Assert(_outgoingConnectionFactory != null); + _outgoingConnectionFactory.updateConnectionObservers(); + Debug.Assert(_objectAdapterFactory != null); + _objectAdapterFactory.updateConnectionObservers(); + } + catch(Ice.CommunicatorDestroyedException) + { + } + } + + internal void updateThreadObservers() + { + try + { + if(_clientThreadPool != null) + { + _clientThreadPool.updateObservers(); + } + if(_serverThreadPool != null) + { + _serverThreadPool.updateObservers(); + } + Debug.Assert(_objectAdapterFactory != null); + _objectAdapterFactory.updateThreadObservers(); +#if !SILVERLIGHT + if(_endpointHostResolver != null) + { + _endpointHostResolver.updateObserver(); + } +#endif + if(_asyncIOThread != null) + { + _asyncIOThread.updateObserver(); + } + if(_timer != null) + { + _timer.updateObserver(_initData.observer); + } + } + catch(Ice.CommunicatorDestroyedException) + { + } + } + + internal void addAllAdminFacets() + { + lock(this) + { + Dictionary<string, Ice.Object> filteredFacets = new Dictionary<string, Ice.Object>(); + + foreach(KeyValuePair<string, Ice.Object> entry in _adminFacets) + { + if(_adminFacetFilter.Count == 0 || _adminFacetFilter.Contains(entry.Key)) + { + _adminAdapter.addFacet(entry.Value, _adminIdentity, entry.Key); + } + else + { + filteredFacets.Add(entry.Key, entry.Value); + } + } + _adminFacets = filteredFacets; + } + } + + internal void setServerProcessProxy(Ice.ObjectAdapter adminAdapter, Ice.Identity adminIdentity) + { + Ice.ObjectPrx admin = adminAdapter.createProxy(adminIdentity); + Ice.LocatorPrx locator = adminAdapter.getLocator(); + string serverId = _initData.properties.getProperty("Ice.Admin.ServerId"); + + if(locator != null && serverId.Length > 0) + { + Ice.ProcessPrx process = Ice.ProcessPrxHelper.uncheckedCast(admin.ice_facet("Process")); + try + { + // + // Note that as soon as the process proxy is registered, the communicator might be + // shutdown by a remote client and admin facets might start receiving calls. + // + locator.getRegistry().setServerProcessProxy(serverId, process); + } + catch(Ice.ServerNotFoundException) + { + if(_traceLevels.location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't register server `" + serverId + "' with the locator registry:\n"); + s.Append("the server is not known to the locator registry"); + _initData.logger.trace(_traceLevels.locationCat, s.ToString()); + } + + throw new Ice.InitializationException("Locator knows nothing about server `" + serverId + "'"); + } + catch(Ice.LocalException ex) + { + if(_traceLevels.location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't register server `" + serverId + "' with the locator registry:\n" + ex); + _initData.logger.trace(_traceLevels.locationCat, s.ToString()); + } + throw; // TODO: Shall we raise a special exception instead of a non obvious local exception? + } + + if(_traceLevels.location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("registered server `" + serverId + "' with the locator registry"); + _initData.logger.trace(_traceLevels.locationCat, s.ToString()); + } + } + } + + private NetworkProxy createNetworkProxy(Ice.Properties props, int protocolSupport) + { + string proxyHost; + + proxyHost = props.getProperty("Ice.SOCKSProxyHost"); + if(proxyHost.Length > 0) + { + if(protocolSupport == Network.EnableIPv6) + { + throw new Ice.InitializationException("IPv6 only is not supported with SOCKS4 proxies"); + } + int proxyPort = props.getPropertyAsIntWithDefault("Ice.SOCKSProxyPort", 1080); + return new SOCKSNetworkProxy(proxyHost, proxyPort); + } + + proxyHost = props.getProperty("Ice.HTTPProxyHost"); + if(proxyHost.Length > 0) + { + return new HTTPNetworkProxy(proxyHost, props.getPropertyAsIntWithDefault("Ice.HTTPProxyPort", 1080)); + } + + return null; + } + + private const int StateActive = 0; + private const int StateDestroyInProgress = 1; + private const int StateDestroyed = 2; + private int _state; + private Ice.InitializationData _initData; // Immutable, not reset by destroy(). + private TraceLevels _traceLevels; // Immutable, not reset by destroy(). + private DefaultsAndOverrides _defaultsAndOverrides; // Immutable, not reset by destroy(). +#if COMPACT || SILVERLIGHT + private string[] _factoryAssemblies; // Immutable, not reset by destroy(). +#endif + private int _messageSizeMax; // Immutable, not reset by destroy(). + private int _batchAutoFlushSize; // Immutable, not reset by destroy(). + private int _cacheMessageBuffers; // Immutable, not reset by destroy(). + private ACMConfig _clientACM; // Immutable, not reset by destroy(). + private ACMConfig _serverACM; // Immutable, not reset by destroy(). + private Ice.ImplicitContextI _implicitContext; // Immutable + private RouterManager _routerManager; + private LocatorManager _locatorManager; + private ReferenceFactory _referenceFactory; + private RequestHandlerFactory _requestHandlerFactory; + private ProxyFactory _proxyFactory; + private OutgoingConnectionFactory _outgoingConnectionFactory; + private ObjectFactoryManager _servantFactoryManager; + private ObjectAdapterFactory _objectAdapterFactory; + private int _protocolSupport; + private bool _preferIPv6; + private NetworkProxy _networkProxy; + private ThreadPool _clientThreadPool; + private ThreadPool _serverThreadPool; + private AsyncIOThread _asyncIOThread; +#if !SILVERLIGHT + private EndpointHostResolver _endpointHostResolver; +#endif + private Timer _timer; + private RetryQueue _retryQueue; + private EndpointFactoryManager _endpointFactoryManager; + private Ice.PluginManager _pluginManager; + private bool _adminEnabled = false; + private Ice.ObjectAdapter _adminAdapter; + private Dictionary<string, Ice.Object> _adminFacets = new Dictionary<string, Ice.Object>(); + private HashSet<string> _adminFacetFilter = new HashSet<string>(); + private Ice.Identity _adminIdentity; + private Dictionary<short, BufSizeWarnInfo> _setBufSizeWarn = new Dictionary<short, BufSizeWarnInfo>(); + +#if !SILVERLIGHT + private static bool _printProcessIdDone = false; +#endif + +#if !SILVERLIGHT && !UNITY + private static bool _oneOfDone = false; +#endif + + private static System.Object _staticLock = new System.Object(); + } +} diff --git a/csharp/src/Ice/InstrumentationI.cs b/csharp/src/Ice/InstrumentationI.cs new file mode 100644 index 00000000000..f49e832cd50 --- /dev/null +++ b/csharp/src/Ice/InstrumentationI.cs @@ -0,0 +1,1222 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Text; + using System.Collections.Generic; + + using IceMX; + + public class ObserverWithDelegate<T, O> : Observer<T> + where T : Metrics, new() + where O : Ice.Instrumentation.Observer + { + override public void + attach() + { + base.attach(); + if(delegate_ != null) + { + delegate_.attach(); + } + } + + override public void + detach() + { + base.detach(); + if(delegate_ != null) + { + delegate_.detach(); + } + } + + override public void + failed(string exceptionName) + { + base.failed(exceptionName); + if(delegate_ != null) + { + delegate_.failed(exceptionName); + } + } + + public O + getDelegate() + { + return delegate_; + } + + public void + setDelegate(O del) + { + delegate_ = del; + } + + public Observer getObserver<S, ObserverImpl, Observer>(string mapName, MetricsHelper<S> helper, Observer del) + where S : Metrics, new() + where ObserverImpl : ObserverWithDelegate<S, Observer>, Observer, new() + where Observer : Ice.Instrumentation.Observer + { + ObserverImpl obsv = base.getObserver<S, ObserverImpl>(mapName, helper); + if(obsv != null) + { + obsv.setDelegate(del); + return (Observer)obsv; + } + return del; + } + + protected O delegate_; + }; + + public class ObserverFactoryWithDelegate<T, OImpl, O> : ObserverFactory<T, OImpl> + where T : Metrics, new() + where OImpl : ObserverWithDelegate<T, O>, O, new() + where O : Ice.Instrumentation.Observer + { + public ObserverFactoryWithDelegate(IceInternal.MetricsAdminI metrics, string name) : base(metrics, name) + { + } + + public O getObserver(MetricsHelper<T> helper, O del) + { + OImpl o = base.getObserver(helper); + if(o != null) + { + o.setDelegate(del); + return o; + } + return del; + } + + public O getObserver(MetricsHelper<T> helper, object observer, O del) + { + OImpl o = base.getObserver(helper, observer); + if(o != null) + { + o.setDelegate(del); + return o; + } + return del; + } + } + + static class AttrsUtil + { + public static void + addEndpointAttributes<T>(MetricsHelper<T>.AttributeResolver r, Type cl) where T : IceMX.Metrics + { + r.add("endpoint", cl.GetMethod("getEndpoint")); + + Type cli = typeof(Ice.EndpointInfo); + r.add("endpointType", cl.GetMethod("getEndpointInfo"), cli.GetMethod("type")); + r.add("endpointIsDatagram", cl.GetMethod("getEndpointInfo"), cli.GetMethod("datagram")); + r.add("endpointIsSecure", cl.GetMethod("getEndpointInfo"), cli.GetMethod("secure")); + r.add("endpointTimeout", cl.GetMethod("getEndpointInfo"), cli.GetField("timeout")); + r.add("endpointCompress", cl.GetMethod("getEndpointInfo"), cli.GetField("compress")); + + cli = typeof(Ice.IPEndpointInfo); + r.add("endpointHost", cl.GetMethod("getEndpointInfo"), cli.GetField("host")); + r.add("endpointPort", cl.GetMethod("getEndpointInfo"), cli.GetField("port")); + } + + public static void + addConnectionAttributes<T>(MetricsHelper<T>.AttributeResolver r, Type cl) where T : IceMX.Metrics + { + Type cli = typeof(Ice.ConnectionInfo); + r.add("incoming", cl.GetMethod("getConnectionInfo"), cli.GetField("incoming")); + r.add("adapterName", cl.GetMethod("getConnectionInfo"), cli.GetField("adapterName")); + r.add("connectionId", cl.GetMethod("getConnectionInfo"), cli.GetField("connectionId")); + + cli = typeof(Ice.IPConnectionInfo); + r.add("localHost", cl.GetMethod("getConnectionInfo"), cli.GetField("localAddress")); + r.add("localPort", cl.GetMethod("getConnectionInfo"), cli.GetField("localPort")); + r.add("remoteHost", cl.GetMethod("getConnectionInfo"), cli.GetField("remoteAddress")); + r.add("remotePort", cl.GetMethod("getConnectionInfo"), cli.GetField("remotePort")); + + cli = typeof(Ice.UDPConnectionInfo); + r.add("mcastHost", cl.GetMethod("getConnectionInfo"), cli.GetField("mcastAddress")); + r.add("mcastPort", cl.GetMethod("getConnectionInfo"), cli.GetField("mcastPort")); + + AttrsUtil.addEndpointAttributes<T>(r, cl); + } + } + + class ConnectionHelper : MetricsHelper<ConnectionMetrics> + { + class AttributeResolverI : MetricsHelper<ConnectionMetrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(ConnectionHelper); + add("parent", cl.GetMethod("getParent")); + add("id", cl.GetMethod("getId")); + add("state", cl.GetMethod("getState")); + AttrsUtil.addConnectionAttributes<ConnectionMetrics>(this, cl); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public ConnectionHelper(Ice.ConnectionInfo con, Ice.Endpoint endpt, Ice.Instrumentation.ConnectionState state) + : base(_attributes) + { + _connectionInfo = con; + _endpoint = endpt; + _state = state; + } + + public string getId() + { + if(_id == null) + { + StringBuilder os = new StringBuilder(); + if(_connectionInfo is Ice.IPConnectionInfo) + { + Ice.IPConnectionInfo info = (Ice.IPConnectionInfo)_connectionInfo; + os.Append(info.localAddress).Append(':').Append(info.localPort); + os.Append(" -> "); + os.Append(info.remoteAddress).Append(':').Append(info.remotePort); + } + else + { + os.Append("connection-").Append(_connectionInfo); + } + if(_connectionInfo.connectionId.Length > 0) + { + os.Append(" [").Append(_connectionInfo.connectionId).Append("]"); + } + _id = os.ToString(); + } + return _id; + } + + public string getState() + { + switch(_state) + { + case Ice.Instrumentation.ConnectionState.ConnectionStateValidating: + return "validating"; + case Ice.Instrumentation.ConnectionState.ConnectionStateHolding: + return "holding"; + case Ice.Instrumentation.ConnectionState.ConnectionStateActive: + return "active"; + case Ice.Instrumentation.ConnectionState.ConnectionStateClosing: + return "closing"; + case Ice.Instrumentation.ConnectionState.ConnectionStateClosed: + return "closed"; + default: + Debug.Assert(false); + return ""; + } + } + + public string getParent() + { + if(_connectionInfo.adapterName != null && _connectionInfo.adapterName.Length > 0) + { + return _connectionInfo.adapterName; + } + else + { + return "Communicator"; + } + } + + public Ice.ConnectionInfo getConnectionInfo() + { + return _connectionInfo; + } + + public Ice.Endpoint getEndpoint() + { + return _endpoint; + } + + public Ice.EndpointInfo getEndpointInfo() + { + if(_endpointInfo == null) + { + _endpointInfo = _endpoint.getInfo(); + } + return _endpointInfo; + } + + readonly private Ice.ConnectionInfo _connectionInfo; + readonly private Ice.Endpoint _endpoint; + readonly private Ice.Instrumentation.ConnectionState _state; + private string _id; + private Ice.EndpointInfo _endpointInfo; + }; + + class DispatchHelper : MetricsHelper<DispatchMetrics> + { + class AttributeResolverI : MetricsHelper<DispatchMetrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(DispatchHelper); + add("parent", cl.GetMethod("getParent")); + add("id", cl.GetMethod("getId")); + + AttrsUtil.addConnectionAttributes<DispatchMetrics>(this, cl); + + Type clc = typeof(Ice.Current); + add("operation", cl.GetMethod("getCurrent"), clc.GetField("operation")); + add("identity", cl.GetMethod("getIdentity")); + add("facet", cl.GetMethod("getCurrent"), clc.GetField("facet")); + add("current", cl.GetMethod("getCurrent"), clc.GetField("requestId")); + add("mode", cl.GetMethod("getMode")); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public DispatchHelper(Ice.Current current, int size) : base(_attributes) + { + _current = current; + _size = size; + } + + override protected string defaultResolve(string attribute) + { + if(attribute.IndexOf("context.", 0) == 0) + { + string v; + if(_current.ctx.TryGetValue(attribute.Substring(8), out v)) + { + return v; + } + } + throw new ArgumentOutOfRangeException(attribute); + } + + override public void initMetrics(DispatchMetrics v) + { + v.size += _size; + } + + public string getMode() + { + return _current.requestId == 0 ? "oneway" : "twoway"; + } + + public string getId() + { + if(_id == null) + { + StringBuilder os = new StringBuilder(); + if(_current.id.category != null && _current.id.category.Length > 0) + { + os.Append(_current.id.category).Append('/'); + } + os.Append(_current.id.name).Append(" [").Append(_current.operation).Append(']'); + _id = os.ToString(); + } + return _id; + } + + public string getParent() + { + return _current.adapter.getName(); + } + + public Ice.ConnectionInfo getConnectionInfo() + { + if(_current.con != null) + { + return _current.con.getInfo(); + } + return null; + } + + public Ice.Endpoint getEndpoint() + { + if(_current.con != null) + { + return _current.con.getEndpoint(); + } + return null; + } + + public Ice.Connection getConnection() + { + return _current.con; + } + + public Ice.EndpointInfo getEndpointInfo() + { + if(_current.con != null && _endpointInfo == null) + { + _endpointInfo = _current.con.getEndpoint().getInfo(); + } + return _endpointInfo; + } + + public Ice.Current getCurrent() + { + return _current; + } + + public string getIdentity() + { + return _current.adapter.getCommunicator().identityToString(_current.id); + } + + readonly private Ice.Current _current; + readonly private int _size; + private string _id; + private Ice.EndpointInfo _endpointInfo; + }; + + class InvocationHelper : MetricsHelper<InvocationMetrics> + { + class AttributeResolverI : MetricsHelper<InvocationMetrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(InvocationHelper); + add("parent", cl.GetMethod("getParent")); + add("id", cl.GetMethod("getId")); + + add("operation", cl.GetMethod("getOperation")); + add("identity", cl.GetMethod("getIdentity")); + + Type cli = typeof(Ice.ObjectPrx); + add("facet", cl.GetMethod("getProxy"), cli.GetMethod("ice_getFacet")); + add("encoding", cl.GetMethod("getEncodingVersion")); + add("mode", cl.GetMethod("getMode")); + add("proxy", cl.GetMethod("getProxy")); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public InvocationHelper(Ice.ObjectPrx proxy, string op, Dictionary<string, string> ctx) : base(_attributes) + { + _proxy = proxy; + _operation = op; + _context = ctx; + } + + override protected string defaultResolve(string attribute) + { + if(attribute.IndexOf("context.", 0) == 0) + { + string v; + if(_context.TryGetValue(attribute.Substring(8), out v)) + { + return v; + } + } + throw new ArgumentOutOfRangeException(attribute); + } + + public string getMode() + { + if(_proxy == null) + { + throw new ArgumentOutOfRangeException("mode"); + } + + if(_proxy.ice_isTwoway()) + { + return "twoway"; + } + else if(_proxy.ice_isOneway()) + { + return "oneway"; + } + else if(_proxy.ice_isBatchOneway()) + { + return "batch-oneway"; + } + else if(_proxy.ice_isDatagram()) + { + return "datagram"; + } + else if(_proxy.ice_isBatchDatagram()) + { + return "batch-datagram"; + } + else + { + throw new ArgumentOutOfRangeException("mode"); + } + } + + public string getId() + { + if(_id == null) + { + if(_proxy != null) + { + StringBuilder os = new StringBuilder(); + try + { + os.Append(_proxy.ice_endpoints(emptyEndpoints)).Append(" [").Append(_operation).Append(']'); + } + catch(Ice.Exception) + { + // Either a fixed proxy or the communicator is destroyed. + os.Append(_proxy.ice_getCommunicator().identityToString(_proxy.ice_getIdentity())); + os.Append(" [").Append(_operation).Append(']'); + } + _id = os.ToString(); + } + else + { + _id = _operation; + } + } + return _id; + } + + public string getParent() + { + return "Communicator"; + } + + public Ice.ObjectPrx getProxy() + { + return _proxy; + } + + public string getEncodingVersion() + { + return Ice.Util.encodingVersionToString(_proxy.ice_getEncodingVersion()); + } + + public string getIdentity() + { + if(_proxy != null) + { + return _proxy.ice_getCommunicator().identityToString(_proxy.ice_getIdentity()); + } + else + { + return ""; + } + } + + public string getOperation() + { + return _operation; + } + + readonly private Ice.ObjectPrx _proxy; + readonly private string _operation; + readonly private Dictionary<string, string> _context; + private string _id; + + readonly static private Ice.Endpoint[] emptyEndpoints = new Ice.Endpoint[0]; + }; + + class ThreadHelper : MetricsHelper<ThreadMetrics> + { + class AttributeResolverI : MetricsHelper<ThreadMetrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(ThreadHelper); + add("parent", cl.GetField("_parent")); + add("id", cl.GetField("_id")); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public ThreadHelper(string parent, string id, Ice.Instrumentation.ThreadState state) : base(_attributes) + { + _parent = parent; + _id = id; + _state = state; + } + + override public void initMetrics(ThreadMetrics v) + { + switch(_state) + { + case Ice.Instrumentation.ThreadState.ThreadStateInUseForIO: + ++v.inUseForIO; + break; + case Ice.Instrumentation.ThreadState.ThreadStateInUseForUser: + ++v.inUseForUser; + break; + case Ice.Instrumentation.ThreadState.ThreadStateInUseForOther: + ++v.inUseForOther; + break; + default: + break; + } + } + + readonly public string _parent; + readonly public string _id; + readonly private Ice.Instrumentation.ThreadState _state; + }; + + class EndpointHelper : MetricsHelper<Metrics> + { + class AttributeResolverI : MetricsHelper<Metrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(EndpointHelper); + add("parent", cl.GetMethod("getParent")); + add("id", cl.GetMethod("getId")); + AttrsUtil.addEndpointAttributes<Metrics>(this, cl); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public EndpointHelper(Ice.Endpoint endpt, string id) : base(_attributes) + { + _endpoint = endpt; + _id = id; + } + + public EndpointHelper(Ice.Endpoint endpt) : base(_attributes) + { + _endpoint = endpt; + } + + public Ice.EndpointInfo getEndpointInfo() + { + if(_endpointInfo == null) + { + _endpointInfo = _endpoint.getInfo(); + } + return _endpointInfo; + } + + public string getParent() + { + return "Communicator"; + } + + public string getId() + { + if(_id == null) + { + _id = _endpoint.ToString(); + } + return _id; + } + + public string getEndpoint() + { + return _endpoint.ToString(); + } + + readonly private Ice.Endpoint _endpoint; + private string _id; + private Ice.EndpointInfo _endpointInfo; + }; + + public class RemoteInvocationHelper : MetricsHelper<RemoteMetrics> + { + class AttributeResolverI : MetricsHelper<RemoteMetrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(RemoteInvocationHelper); + add("parent", cl.GetMethod("getParent")); + add("id", cl.GetMethod("getId")); + add("requestId", cl.GetMethod("getRequestId")); + AttrsUtil.addConnectionAttributes<RemoteMetrics>(this, cl); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public RemoteInvocationHelper(Ice.ConnectionInfo con, Ice.Endpoint endpt, int requestId, int size) : + base(_attributes) + { + _connectionInfo = con; + _endpoint = endpt; + _requestId = requestId; + _size = size; + } + + override public void initMetrics(RemoteMetrics v) + { + v.size += _size; + } + + public string getId() + { + if(_id == null) + { + _id = _endpoint.ToString(); + if(_connectionInfo.connectionId != null && _connectionInfo.connectionId.Length > 0) + { + _id += " [" + _connectionInfo.connectionId + "]"; + } + } + return _id; + } + + public int getRequestId() + { + return _requestId; + } + + public string getParent() + { + if(_connectionInfo.adapterName != null && _connectionInfo.adapterName.Length > 0) + { + return _connectionInfo.adapterName; + } + else + { + return "Communicator"; + } + } + + public Ice.ConnectionInfo getConnectionInfo() + { + return _connectionInfo; + } + + public Ice.Endpoint getEndpoint() + { + return _endpoint; + } + + public Ice.EndpointInfo getEndpointInfo() + { + if(_endpointInfo == null) + { + _endpointInfo = _endpoint.getInfo(); + } + return _endpointInfo; + } + + readonly private Ice.ConnectionInfo _connectionInfo; + readonly private Ice.Endpoint _endpoint; + readonly private int _size; + readonly private int _requestId; + private string _id; + private Ice.EndpointInfo _endpointInfo; + }; + + public class CollocatedInvocationHelper : MetricsHelper<CollocatedMetrics> + { + class AttributeResolverI : MetricsHelper<CollocatedMetrics>.AttributeResolver + { + public AttributeResolverI() + { + try + { + Type cl = typeof(CollocatedInvocationHelper); + add("parent", cl.GetMethod("getParent")); + add("id", cl.GetMethod("getId")); + add("requestId", cl.GetMethod("getRequestId")); + } + catch(Exception) + { + Debug.Assert(false); + } + } + }; + static AttributeResolver _attributes = new AttributeResolverI(); + + public CollocatedInvocationHelper(Ice.ObjectAdapter adapter, int requestId, int size) : + base(_attributes) + { + _id = adapter.getName(); + _requestId = requestId; + _size = size; + } + + override public void initMetrics(CollocatedMetrics v) + { + v.size += _size; + } + + public string getId() + { + return _id; + } + + public int getRequestId() + { + return _requestId; + } + + public string getParent() + { + return "Communicator"; + } + + readonly private int _size; + readonly private int _requestId; + readonly private string _id; + }; + + public class ObserverWithDelegateI : ObserverWithDelegate<Metrics, Ice.Instrumentation.Observer> + { + }; + + public class ConnectionObserverI : ObserverWithDelegate<ConnectionMetrics, Ice.Instrumentation.ConnectionObserver>, + Ice.Instrumentation.ConnectionObserver + { + public void sentBytes(int num) + { + _sentBytes = num; + forEach(sentBytesUpdate); + if(delegate_ != null) + { + delegate_.sentBytes(num); + } + } + + public void receivedBytes(int num) + { + _receivedBytes = num; + forEach(receivedBytesUpdate); + if(delegate_ != null) + { + delegate_.receivedBytes(num); + } + } + + private void sentBytesUpdate(ConnectionMetrics v) + { + v.sentBytes += _sentBytes; + } + + private void receivedBytesUpdate(ConnectionMetrics v) + { + v.receivedBytes += _receivedBytes; + } + + private int _sentBytes; + private int _receivedBytes; + }; + + public class DispatchObserverI : ObserverWithDelegate<DispatchMetrics, Ice.Instrumentation.DispatchObserver>, + Ice.Instrumentation.DispatchObserver + { + public void + userException() + { + forEach(userException); + if(delegate_ != null) + { + delegate_.userException(); + } + } + + public void reply(int size) + { + forEach((DispatchMetrics v) => { + v.replySize += size; + }); + if(delegate_ != null) + { + delegate_.reply(size); + } + } + + private void userException(DispatchMetrics v) + { + ++v.userException; + } + } + + public class RemoteObserverI : ObserverWithDelegate<RemoteMetrics, Ice.Instrumentation.RemoteObserver>, + Ice.Instrumentation.RemoteObserver + { + public void reply(int size) + { + forEach((RemoteMetrics v) => { + v.replySize += size; + }); + if(delegate_ != null) + { + delegate_.reply(size); + } + } + } + + public class CollocatedObserverI : ObserverWithDelegate<CollocatedMetrics, Ice.Instrumentation.CollocatedObserver>, + Ice.Instrumentation.CollocatedObserver + { + public void reply(int size) + { + forEach((CollocatedMetrics v) => { + v.replySize += size; + }); + if(delegate_ != null) + { + delegate_.reply(size); + } + } + } + + public class InvocationObserverI : ObserverWithDelegate<InvocationMetrics, Ice.Instrumentation.InvocationObserver>, + Ice.Instrumentation.InvocationObserver + { + public void + userException() + { + forEach(userException); + if(delegate_ != null) + { + delegate_.userException(); + } + } + + public void + retried() + { + forEach(incrementRetry); + if(delegate_ != null) + { + delegate_.retried(); + } + } + + public Ice.Instrumentation.RemoteObserver getRemoteObserver(Ice.ConnectionInfo con, Ice.Endpoint endpt, + int requestId, int size) + { + Ice.Instrumentation.RemoteObserver del = null; + if(delegate_ != null) + { + del = delegate_.getRemoteObserver(con, endpt, requestId, size); + } + return getObserver<RemoteMetrics, RemoteObserverI, + Ice.Instrumentation.RemoteObserver>("Remote", + new RemoteInvocationHelper(con, endpt, requestId, size), + del); + } + + public Ice.Instrumentation.CollocatedObserver getCollocatedObserver(Ice.ObjectAdapter adapter, + int requestId, + int size) + { + Ice.Instrumentation.CollocatedObserver del = null; + if(delegate_ != null) + { + del = delegate_.getCollocatedObserver(adapter, requestId, size); + } + return getObserver<CollocatedMetrics, CollocatedObserverI, + Ice.Instrumentation.CollocatedObserver>("Collocated", + new CollocatedInvocationHelper(adapter, requestId, size), + del); + } + + private void incrementRetry(InvocationMetrics v) + { + ++v.retry; + } + + private void userException(InvocationMetrics v) + { + ++v.userException; + } + } + + public class ThreadObserverI : ObserverWithDelegate<ThreadMetrics, Ice.Instrumentation.ThreadObserver>, + Ice.Instrumentation.ThreadObserver + { + public void stateChanged(Ice.Instrumentation.ThreadState oldState, Ice.Instrumentation.ThreadState newState) + { + _oldState = oldState; + _newState = newState; + forEach(threadStateUpdate); + if(delegate_ != null) + { + delegate_.stateChanged(oldState, newState); + } + } + + private void threadStateUpdate(ThreadMetrics v) + { + switch(_oldState) + { + case Ice.Instrumentation.ThreadState.ThreadStateInUseForIO: + --v.inUseForIO; + break; + case Ice.Instrumentation.ThreadState.ThreadStateInUseForUser: + --v.inUseForUser; + break; + case Ice.Instrumentation.ThreadState.ThreadStateInUseForOther: + --v.inUseForOther; + break; + default: + break; + } + switch(_newState) + { + case Ice.Instrumentation.ThreadState.ThreadStateInUseForIO: + ++v.inUseForIO; + break; + case Ice.Instrumentation.ThreadState.ThreadStateInUseForUser: + ++v.inUseForUser; + break; + case Ice.Instrumentation.ThreadState.ThreadStateInUseForOther: + ++v.inUseForOther; + break; + default: + break; + } + } + + private Ice.Instrumentation.ThreadState _oldState; + private Ice.Instrumentation.ThreadState _newState; + }; + + public class CommunicatorObserverI : Ice.Instrumentation.CommunicatorObserver + { + public CommunicatorObserverI(Ice.InitializationData initData) + { + _metrics = new MetricsAdminI(initData.properties, initData.logger); + _delegate = initData.observer; + _connections = new ObserverFactoryWithDelegate<ConnectionMetrics, ConnectionObserverI, + Ice.Instrumentation.ConnectionObserver>(_metrics, "Connection"); + _dispatch = new ObserverFactoryWithDelegate<DispatchMetrics, DispatchObserverI, + Ice.Instrumentation.DispatchObserver>(_metrics, "Dispatch"); + _invocations = new ObserverFactoryWithDelegate<InvocationMetrics, InvocationObserverI, + Ice.Instrumentation.InvocationObserver>(_metrics, "Invocation"); + _threads = new ObserverFactoryWithDelegate<ThreadMetrics, ThreadObserverI, + Ice.Instrumentation.ThreadObserver>(_metrics, "Thread"); + _connects = new ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, + Ice.Instrumentation.Observer>(_metrics, "ConnectionEstablishment"); + _endpointLookups = new ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, + Ice.Instrumentation.Observer>(_metrics, "EndpointLookup"); + + try + { + Type cl = typeof(InvocationMetrics); + _invocations.registerSubMap<RemoteMetrics>("Remote", cl.GetField("remotes")); + _invocations.registerSubMap<CollocatedMetrics>("Collocated", cl.GetField("collocated")); + } + catch(Exception) + { + Debug.Assert(false); + } + } + + public Ice.Instrumentation.Observer getConnectionEstablishmentObserver(Ice.Endpoint endpt, string connector) + { + if(_connects.isEnabled()) + { + try + { + Ice.Instrumentation.Observer del = null; + if(_delegate != null) + { + del = _delegate.getConnectionEstablishmentObserver(endpt, connector); + } + return _connects.getObserver(new EndpointHelper(endpt, connector), del); + } + catch(Exception ex) + { + _metrics.getLogger().error("unexpected exception trying to obtain observer:\n" + ex); + } + } + return null; + } + + public Ice.Instrumentation.Observer getEndpointLookupObserver(Ice.Endpoint endpt) + { + if(_endpointLookups.isEnabled()) + { + try + { + Ice.Instrumentation.Observer del = null; + if(_delegate != null) + { + del = _delegate.getEndpointLookupObserver(endpt); + } + return _endpointLookups.getObserver(new EndpointHelper(endpt), del); + } + catch(Exception ex) + { + _metrics.getLogger().error("unexpected exception trying to obtain observer:\n" + ex); + } + } + return null; + } + + public Ice.Instrumentation.ConnectionObserver getConnectionObserver(Ice.ConnectionInfo c, + Ice.Endpoint e, + Ice.Instrumentation.ConnectionState s, + Ice.Instrumentation.ConnectionObserver obsv) + { + if(_connections.isEnabled()) + { + try + { + Ice.Instrumentation.ConnectionObserver del = null; + ConnectionObserverI o = obsv is ConnectionObserverI ? (ConnectionObserverI)obsv : null; + if(_delegate != null) + { + del = _delegate.getConnectionObserver(c, e, s, o != null ? o.getDelegate() : obsv); + } + return _connections.getObserver(new ConnectionHelper(c, e, s), obsv, del); + } + catch(Exception ex) + { + _metrics.getLogger().error("unexpected exception trying to obtain observer:\n" + ex); + } + } + return null; + } + + public Ice.Instrumentation.ThreadObserver getThreadObserver(string parent, string id, + Ice.Instrumentation.ThreadState s, + Ice.Instrumentation.ThreadObserver obsv) + { + if(_threads.isEnabled()) + { + try + { + Ice.Instrumentation.ThreadObserver del = null; + ThreadObserverI o = obsv is ThreadObserverI ? (ThreadObserverI)obsv : null; + if(_delegate != null) + { + del = _delegate.getThreadObserver(parent, id, s, o != null ? o.getDelegate() : obsv); + } + return _threads.getObserver(new ThreadHelper(parent, id, s), obsv, del); + } + catch(Exception ex) + { + _metrics.getLogger().error("unexpected exception trying to obtain observer:\n" + ex); + } + } + return null; + } + + public Ice.Instrumentation.InvocationObserver getInvocationObserver(Ice.ObjectPrx prx, string operation, + Dictionary<string, string> ctx) + { + if(_invocations.isEnabled()) + { + try + { + Ice.Instrumentation.InvocationObserver del = null; + if(_delegate != null) + { + del = _delegate.getInvocationObserver(prx, operation, ctx); + } + return _invocations.getObserver(new InvocationHelper(prx, operation, ctx), del); + } + catch(Exception ex) + { + _metrics.getLogger().error("unexpected exception trying to obtain observer:\n" + ex); + } + } + return null; + } + + public Ice.Instrumentation.DispatchObserver getDispatchObserver(Ice.Current c, int size) + { + if(_dispatch.isEnabled()) + { + try + { + Ice.Instrumentation.DispatchObserver del = null; + if(_delegate != null) + { + del = _delegate.getDispatchObserver(c, size); + } + return _dispatch.getObserver(new DispatchHelper(c, size), del); + } + catch(Exception ex) + { + _metrics.getLogger().error("unexpected exception trying to obtain observer:\n" + ex); + } + } + return null; + } + + public void setObserverUpdater(Ice.Instrumentation.ObserverUpdater updater) + { + if(updater == null) + { + _connections.setUpdater(null); + _threads.setUpdater(null); + } + else + { + _connections.setUpdater(updater.updateConnectionObservers); + _threads.setUpdater(updater.updateThreadObservers); + } + if(_delegate != null) + { + _delegate.setObserverUpdater(updater); + } + } + + public IceInternal.MetricsAdminI getFacet() + { + return _metrics; + } + + readonly private IceInternal.MetricsAdminI _metrics; + readonly private Ice.Instrumentation.CommunicatorObserver _delegate; + readonly private ObserverFactoryWithDelegate<ConnectionMetrics, ConnectionObserverI, + Ice.Instrumentation.ConnectionObserver> _connections; + readonly private ObserverFactoryWithDelegate<DispatchMetrics, DispatchObserverI, + Ice.Instrumentation.DispatchObserver> _dispatch; + readonly private ObserverFactoryWithDelegate<InvocationMetrics, InvocationObserverI, + Ice.Instrumentation.InvocationObserver> _invocations; + readonly private ObserverFactoryWithDelegate<ThreadMetrics, ThreadObserverI, + Ice.Instrumentation.ThreadObserver> _threads; + readonly private ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, + Ice.Instrumentation.Observer> _connects; + readonly private ObserverFactoryWithDelegate<Metrics, ObserverWithDelegateI, + Ice.Instrumentation.Observer> _endpointLookups; + } +}
\ No newline at end of file diff --git a/csharp/src/Ice/LocatorInfo.cs b/csharp/src/Ice/LocatorInfo.cs new file mode 100644 index 00000000000..1b3960df6d1 --- /dev/null +++ b/csharp/src/Ice/LocatorInfo.cs @@ -0,0 +1,1009 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace IceInternal +{ + public sealed class LocatorInfo + { + public interface GetEndpointsCallback + { + void setEndpoints(EndpointI[] endpoints, bool cached); + void setException(Ice.LocalException ex); + } + + private class RequestCallback + { + public void + response(LocatorInfo locatorInfo, Ice.ObjectPrx proxy) + { + EndpointI[] endpoints = null; + if(proxy != null) + { + Reference r = ((Ice.ObjectPrxHelperBase)proxy).reference__(); + if(_ref.isWellKnown() && !Protocol.isSupported(_ref.getEncoding(), r.getEncoding())) + { + // + // If a well-known proxy and the returned + // proxy encoding isn't supported, we're done: + // there's no compatible endpoint we can use. + // + } + else if(!r.isIndirect()) + { + endpoints = r.getEndpoints(); + } + else if(_ref.isWellKnown() && !r.isWellKnown()) + { + // + // We're resolving the endpoints of a well-known object and the proxy returned + // by the locator is an indirect proxy. We now need to resolve the endpoints + // of this indirect proxy. + // + locatorInfo.getEndpoints(r, _ref, _ttl, _callback); + return; + } + } + + if(_ref.getInstance().traceLevels().location >= 1) + { + locatorInfo.getEndpointsTrace(_ref, endpoints, false); + } + if(_callback != null) + { + _callback.setEndpoints(endpoints == null ? new EndpointI[0] : endpoints, false); + } + } + + public void + exception(LocatorInfo locatorInfo, Ice.Exception exc) + { + try + { + locatorInfo.getEndpointsException(_ref, exc); // This throws. + } + catch(Ice.LocalException ex) + { + if(_callback != null) + { + _callback.setException(ex); + } + } + } + + public + RequestCallback(Reference @ref, int ttl, GetEndpointsCallback cb) + { + _ref = @ref; + _ttl = ttl; + _callback = cb; + } + + readonly Reference _ref; + readonly int _ttl; + readonly GetEndpointsCallback _callback; + } + + private abstract class Request + { + public void + addCallback(Reference @ref, Reference wellKnownRef, int ttl, GetEndpointsCallback cb) + { + lock(this) + { + RequestCallback callback = new RequestCallback(@ref, ttl, cb); + if(_response) + { + callback.response(_locatorInfo, _proxy); + } + else if(_exception != null) + { + callback.exception(_locatorInfo, _exception); + } + else + { + _callbacks.Add(callback); + if(wellKnownRef != null) + { + // This request is to resolve the endpoints of a cached well-known object ref + _wellKnownRefs.Add(wellKnownRef); + } + if(!_sent) + { + _sent = true; + send(); + } + } + } + } + + public EndpointI[] + getEndpoints(Reference @ref, Reference wellKnownRef, int ttl, out bool cached) + { + lock(this) + { + if(!_response || _exception == null) + { + if(wellKnownRef != null) + { + // This request is to resolve the endpoints of a cached well-known object ref + _wellKnownRefs.Add(wellKnownRef); + } + if(!_sent) + { + _sent = true; + send(); + } + + while(!_response && _exception == null) + { + System.Threading.Monitor.Wait(this); + } + } + + if(_exception != null) + { + _locatorInfo.getEndpointsException(@ref, _exception); // This throws. + } + + Debug.Assert(_response); + EndpointI[] endpoints = null; + if(_proxy != null) + { + Reference r = ((Ice.ObjectPrxHelperBase)_proxy).reference__(); + if(!r.isIndirect()) + { + endpoints = r.getEndpoints(); + } + else if(@ref.isWellKnown() && !r.isWellKnown()) + { + // + // We're resolving the endpoints of a well-known object and the proxy returned + // by the locator is an indirect proxy. We now need to resolve the endpoints + // of this indirect proxy. + // + return _locatorInfo.getEndpoints(r, @ref, ttl, out cached); + } + } + + cached = false; + if(_ref.getInstance().traceLevels().location >= 1) + { + _locatorInfo.getEndpointsTrace(@ref, endpoints, false); + } + return endpoints == null ? new EndpointI[0] : endpoints; + } + } + + public Request(LocatorInfo locatorInfo, Reference @ref) + { + _locatorInfo = locatorInfo; + _ref = @ref; + _sent = false; + _response = false; + } + + public void + response(Ice.ObjectPrx proxy) + { + lock(this) + { + _locatorInfo.finishRequest(_ref, _wellKnownRefs, proxy, false); + _response = true; + _proxy = proxy; + foreach(RequestCallback callback in _callbacks) + { + callback.response(_locatorInfo, proxy); + } + System.Threading.Monitor.PulseAll(this); + } + } + + public void + exception(Ice.Exception ex) + { + lock(this) + { + _locatorInfo.finishRequest(_ref, _wellKnownRefs, null, ex is Ice.UserException); + _exception = ex; + foreach(RequestCallback callback in _callbacks) + { + callback.exception(_locatorInfo, ex); + } + System.Threading.Monitor.PulseAll(this); + } + } + + protected abstract void send(); + + readonly protected LocatorInfo _locatorInfo; + readonly protected Reference _ref; + + private List<RequestCallback> _callbacks = new List<RequestCallback>(); + private List<Reference> _wellKnownRefs = new List<Reference>(); + private bool _sent; + private bool _response; + private Ice.ObjectPrx _proxy; + private Ice.Exception _exception; + } + + private class ObjectRequest : Request + { + public ObjectRequest(LocatorInfo locatorInfo, Reference @ref) : base(locatorInfo, @ref) + { + } + + override protected void + send() + { + try + { + _locatorInfo.getLocator().begin_findObjectById(_ref.getIdentity()).whenCompleted( + this.response, this.exception); + } + catch(Ice.Exception ex) + { + exception(ex); + } + } + } + + private class AdapterRequest : Request + { + public AdapterRequest(LocatorInfo locatorInfo, Reference @ref) : base(locatorInfo, @ref) + { + } + + override protected void + send() + { + try + { + _locatorInfo.getLocator().begin_findAdapterById(_ref.getAdapterId()).whenCompleted( + this.response, this.exception); + } + catch(Ice.Exception ex) + { + exception(ex); + } + } + } + + internal LocatorInfo(Ice.LocatorPrx locator, LocatorTable table, bool background) + { + _locator = locator; + _table = table; + _background = background; + } + + public void destroy() + { + lock(this) + { + _locatorRegistry = null; + _table.clear(); + } + } + + public override bool Equals(object obj) + { + if(object.ReferenceEquals(this, obj)) + { + return true; + } + + LocatorInfo rhs = obj as LocatorInfo; + return rhs == null ? false : _locator.Equals(rhs._locator); + } + + public override int GetHashCode() + { + return _locator.GetHashCode(); + } + + public Ice.LocatorPrx getLocator() + { + // + // No synchronization necessary, _locator is immutable. + // + return _locator; + } + + public Ice.LocatorRegistryPrx getLocatorRegistry() + { + lock(this) + { + if(_locatorRegistry != null) + { + return _locatorRegistry; + } + } + + // + // Do not make locator calls from within sync. + // + Ice.LocatorRegistryPrx locatorRegistry = _locator.getRegistry(); + if(locatorRegistry == null) + { + return null; + } + + lock(this) + { + // + // The locator registry can't be located. We use ordered + // endpoint selection in case the locator returned a proxy + // with some endpoints which are prefered to be tried first. + // + _locatorRegistry = (Ice.LocatorRegistryPrx)locatorRegistry.ice_locator(null).ice_endpointSelection( + Ice.EndpointSelectionType.Ordered); + return _locatorRegistry; + } + } + + public EndpointI[] + getEndpoints(Reference @ref, int ttl, out bool cached) + { + return getEndpoints(@ref, null, ttl, out cached); + } + + public EndpointI[] + getEndpoints(Reference @ref, Reference wellKnownRef, int ttl, out bool cached) + { + Debug.Assert(@ref.isIndirect()); + EndpointI[] endpoints = null; + cached = false; + if(!@ref.isWellKnown()) + { + endpoints = _table.getAdapterEndpoints(@ref.getAdapterId(), ttl, out cached); + if(!cached) + { + if(_background && endpoints != null) + { + getAdapterRequest(@ref).addCallback(@ref, wellKnownRef, ttl, null); + } + else + { + return getAdapterRequest(@ref).getEndpoints(@ref, wellKnownRef, ttl, out cached); + } + } + } + else + { + Reference r = _table.getObjectReference(@ref.getIdentity(), ttl, out cached); + if(!cached) + { + if(_background && r != null) + { + getObjectRequest(@ref).addCallback(@ref, null, ttl, null); + } + else + { + return getObjectRequest(@ref).getEndpoints(@ref, null, ttl, out cached); + } + } + + if(!r.isIndirect()) + { + endpoints = r.getEndpoints(); + } + else if(!r.isWellKnown()) + { + return getEndpoints(r, @ref, ttl, out cached); + } + } + + Debug.Assert(endpoints != null); + cached = true; + if(@ref.getInstance().traceLevels().location >= 1) + { + getEndpointsTrace(@ref, endpoints, true); + } + return endpoints; + } + + public void + getEndpoints(Reference @ref, int ttl, GetEndpointsCallback callback) + { + getEndpoints(@ref, null, ttl, callback); + } + + public void + getEndpoints(Reference @ref, Reference wellKnownRef, int ttl, GetEndpointsCallback callback) + { + Debug.Assert(@ref.isIndirect()); + EndpointI[] endpoints = null; + bool cached = false; + if(!@ref.isWellKnown()) + { + endpoints = _table.getAdapterEndpoints(@ref.getAdapterId(), ttl, out cached); + if(!cached) + { + if(_background && endpoints != null) + { + getAdapterRequest(@ref).addCallback(@ref, wellKnownRef, ttl, null); + } + else + { + getAdapterRequest(@ref).addCallback(@ref, wellKnownRef, ttl, callback); + return; + } + } + } + else + { + Reference r = _table.getObjectReference(@ref.getIdentity(), ttl, out cached); + if(!cached) + { + if(_background && r != null) + { + getObjectRequest(@ref).addCallback(@ref, null, ttl, null); + } + else + { + getObjectRequest(@ref).addCallback(@ref, null, ttl, callback); + return; + } + } + + if(!r.isIndirect()) + { + endpoints = r.getEndpoints(); + } + else if(!r.isWellKnown()) + { + getEndpoints(r, @ref, ttl, callback); + return; + } + } + + Debug.Assert(endpoints != null); + if(@ref.getInstance().traceLevels().location >= 1) + { + getEndpointsTrace(@ref, endpoints, true); + } + if(callback != null) + { + callback.setEndpoints(endpoints, true); + } + } + + public void clearCache(Reference rf) + { + Debug.Assert(rf.isIndirect()); + if(!rf.isWellKnown()) + { + EndpointI[] endpoints = _table.removeAdapterEndpoints(rf.getAdapterId()); + + if(endpoints != null && rf.getInstance().traceLevels().location >= 2) + { + trace("removed endpoints from locator table\n", rf, endpoints); + } + } + else + { + Reference r = _table.removeObjectReference(rf.getIdentity()); + if(r != null) + { + if(!r.isIndirect()) + { + if(rf.getInstance().traceLevels().location >= 2) + { + trace("removed endpoints from locator table", rf, r.getEndpoints()); + } + } + else if(!r.isWellKnown()) + { + clearCache(r); + } + } + } + } + + private void trace(string msg, Reference r, EndpointI[] endpoints) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append(msg + "\n"); + if(r.getAdapterId().Length > 0) + { + s.Append("adapter = " + r.getAdapterId() + "\n"); + } + else + { + s.Append("object = " + r.getInstance().identityToString(r.getIdentity()) + "\n"); + } + + s.Append("endpoints = "); + int sz = endpoints.Length; + for (int i = 0; i < sz; i++) + { + s.Append(endpoints[i].ToString()); + if(i + 1 < sz) + { + s.Append(":"); + } + } + + r.getInstance().initializationData().logger.trace(r.getInstance().traceLevels().locationCat, s.ToString()); + } + + private void getEndpointsException(Reference @ref, System.Exception exc) + { + try + { + throw exc; + } + catch(Ice.AdapterNotFoundException ex) + { + Instance instance = @ref.getInstance(); + if(instance.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("adapter not found\n"); + s.Append("adapter = " + @ref.getAdapterId()); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); + } + + Ice.NotRegisteredException e = new Ice.NotRegisteredException(ex); + e.kindOfObject = "object adapter"; + e.id = @ref.getAdapterId(); + throw e; + } + catch(Ice.ObjectNotFoundException ex) + { + Instance instance = @ref.getInstance(); + if(instance.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("object not found\n"); + s.Append("object = " + instance.identityToString(@ref.getIdentity())); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); + } + + Ice.NotRegisteredException e = new Ice.NotRegisteredException(ex); + e.kindOfObject = "object"; + e.id = instance.identityToString(@ref.getIdentity()); + throw e; + } + catch(Ice.NotRegisteredException) + { + throw; + } + catch(Ice.LocalException ex) + { + Instance instance = @ref.getInstance(); + if(instance.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't contact the locator to retrieve adapter endpoints\n"); + if(@ref.getAdapterId().Length > 0) + { + s.Append("adapter = " + @ref.getAdapterId() + "\n"); + } + else + { + s.Append("object = " + instance.identityToString(@ref.getIdentity()) + "\n"); + } + s.Append("reason = " + ex); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); + } + throw; + } + catch(System.Exception) + { + Debug.Assert(false); + } + } + + private void getEndpointsTrace(Reference @ref, EndpointI[] endpoints, bool cached) + { + if(endpoints != null && endpoints.Length > 0) + { + if(cached) + { + trace("found endpoints in locator table", @ref, endpoints); + } + else + { + trace("retrieved endpoints from locator, adding to locator table", @ref, endpoints); + } + } + else + { + Instance instance = @ref.getInstance(); + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("no endpoints configured for "); + if(@ref.getAdapterId().Length > 0) + { + s.Append("adapter\n"); + s.Append("adapter = " + @ref.getAdapterId()); + } + else + { + s.Append("object\n"); + s.Append("object = " + instance.identityToString(@ref.getIdentity())); + } + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); + } + } + + private Request + getAdapterRequest(Reference @ref) + { + if(@ref.getInstance().traceLevels().location >= 1) + { + Instance instance = @ref.getInstance(); + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("searching for adapter by id\nadapter = "); + s.Append(@ref.getAdapterId()); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); + } + + lock(this) + { + Request request; + if(_adapterRequests.TryGetValue(@ref.getAdapterId(), out request)) + { + return request; + } + + request = new AdapterRequest(this, @ref); + _adapterRequests.Add(@ref.getAdapterId(), request); + return request; + } + } + + private Request + getObjectRequest(Reference @ref) + { + if(@ref.getInstance().traceLevels().location >= 1) + { + Instance instance = @ref.getInstance(); + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("searching for object by id\nobject = "); + s.Append(instance.identityToString(@ref.getIdentity())); + instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); + } + + lock(this) + { + Request request; + if(_objectRequests.TryGetValue(@ref.getIdentity(), out request)) + { + return request; + } + + request = new ObjectRequest(this, @ref); + _objectRequests.Add(@ref.getIdentity(), request); + return request; + } + } + + private void + finishRequest(Reference @ref, List<Reference> wellKnownRefs, Ice.ObjectPrx proxy, bool notRegistered) + { + Ice.ObjectPrxHelperBase @base = proxy as Ice.ObjectPrxHelperBase; + if(proxy == null || @base.reference__().isIndirect()) + { + // + // Remove the cached references of well-known objects for which we tried + // to resolved the endpoints if these endpoints are empty. + // + foreach(Reference r in wellKnownRefs) + { + _table.removeObjectReference(r.getIdentity()); + } + } + + if(!@ref.isWellKnown()) + { + if(proxy != null && !@base.reference__().isIndirect()) + { + // Cache the adapter endpoints. + _table.addAdapterEndpoints(@ref.getAdapterId(), @base.reference__().getEndpoints()); + } + else if(notRegistered) // If the adapter isn't registered anymore, remove it from the cache. + { + _table.removeAdapterEndpoints(@ref.getAdapterId()); + } + + lock(this) + { + Debug.Assert(_adapterRequests.ContainsKey(@ref.getAdapterId())); + _adapterRequests.Remove(@ref.getAdapterId()); + } + } + else + { + if(proxy != null && !@base.reference__().isWellKnown()) + { + // Cache the well-known object reference. + _table.addObjectReference(@ref.getIdentity(), @base.reference__()); + } + else if(notRegistered) // If the well-known object isn't registered anymore, remove it from the cache. + { + _table.removeObjectReference(@ref.getIdentity()); + } + + lock(this) + { + Debug.Assert(_objectRequests.ContainsKey(@ref.getIdentity())); + _objectRequests.Remove(@ref.getIdentity()); + } + } + } + + private readonly Ice.LocatorPrx _locator; + private Ice.LocatorRegistryPrx _locatorRegistry; + private readonly LocatorTable _table; + private readonly bool _background; + + private Dictionary<string, Request> _adapterRequests = new Dictionary<string, Request>(); + private Dictionary<Ice.Identity, Request> _objectRequests = new Dictionary<Ice.Identity, Request>(); + } + + public sealed class LocatorManager + { + struct LocatorKey + { + public LocatorKey(Ice.LocatorPrx prx) + { + Reference r = ((Ice.ObjectPrxHelperBase)prx).reference__(); + _id = r.getIdentity(); + _encoding = r.getEncoding(); + } + + public override bool Equals(object o) + { + LocatorKey k = (LocatorKey)o; + if(!k._id.Equals(_id)) + { + return false; + } + if(!k._encoding.Equals(_encoding)) + { + return false; + } + return true; + } + + public override int GetHashCode() + { + int h = 5381; + IceInternal.HashUtil.hashAdd(ref h, _id); + IceInternal.HashUtil.hashAdd(ref h, _encoding); + return h; + } + + private Ice.Identity _id; + private Ice.EncodingVersion _encoding; + }; + + internal LocatorManager(Ice.Properties properties) + { + _table = new Dictionary<Ice.LocatorPrx, LocatorInfo>(); + _locatorTables = new Dictionary<LocatorKey, LocatorTable>(); + _background = properties.getPropertyAsInt("Ice.BackgroundLocatorCacheUpdates") > 0; + } + + internal void destroy() + { + lock(this) + { + foreach(LocatorInfo info in _table.Values) + { + info.destroy(); + } + _table.Clear(); + _locatorTables.Clear(); + } + } + + // + // Returns locator info for a given locator. Automatically creates + // the locator info if it doesn't exist yet. + // + public LocatorInfo get(Ice.LocatorPrx loc) + { + if(loc == null) + { + return null; + } + + // + // The locator can't be located. + // + Ice.LocatorPrx locator = Ice.LocatorPrxHelper.uncheckedCast(loc.ice_locator(null)); + + // + // TODO: reap unused locator info objects? + // + + lock(this) + { + LocatorInfo info = null; + if(!_table.TryGetValue(locator, out info)) + { + // + // Rely on locator identity for the adapter table. We want to + // have only one table per locator (not one per locator + // proxy). + // + LocatorTable table = null; + LocatorKey key = new LocatorKey(locator); + if(!_locatorTables.TryGetValue(key, out table)) + { + table = new LocatorTable(); + _locatorTables[key] = table; + } + + info = new LocatorInfo(locator, table, _background); + _table[locator] = info; + } + + return info; + } + } + + private Dictionary<Ice.LocatorPrx, LocatorInfo> _table; + private Dictionary<LocatorKey, LocatorTable> _locatorTables; + private readonly bool _background; + } + + sealed class LocatorTable + { + internal LocatorTable() + { + _adapterEndpointsTable = new Dictionary<string, EndpointTableEntry>(); + _objectTable = new Dictionary<Ice.Identity, ReferenceTableEntry>(); + } + + internal void clear() + { + lock(this) + { + _adapterEndpointsTable.Clear(); + _objectTable.Clear(); + } + } + + internal IceInternal.EndpointI[] getAdapterEndpoints(string adapter, int ttl, out bool cached) + { + if(ttl == 0) // Locator cache disabled. + { + cached = false; + return null; + } + + lock(this) + { + EndpointTableEntry entry = null; + if(_adapterEndpointsTable.TryGetValue(adapter, out entry)) + { + cached = checkTTL(entry.time, ttl); + return entry.endpoints; + + } + cached = false; + return null; + } + } + + internal void addAdapterEndpoints(string adapter, IceInternal.EndpointI[] endpoints) + { + lock(this) + { + _adapterEndpointsTable[adapter] = + new EndpointTableEntry(Time.currentMonotonicTimeMillis(), endpoints); + } + } + + internal IceInternal.EndpointI[] removeAdapterEndpoints(string adapter) + { + lock(this) + { + EndpointTableEntry entry = null; + if(_adapterEndpointsTable.TryGetValue(adapter, out entry)) + { + _adapterEndpointsTable.Remove(adapter); + return entry.endpoints; + } + return null; + } + } + + internal Reference getObjectReference(Ice.Identity id, int ttl, out bool cached) + { + if(ttl == 0) // Locator cache disabled. + { + cached = false; + return null; + } + + lock(this) + { + ReferenceTableEntry entry = null; + if(_objectTable.TryGetValue(id, out entry)) + { + cached = checkTTL(entry.time, ttl); + return entry.reference; + } + cached = false; + return null; + } + } + + internal void addObjectReference(Ice.Identity id, Reference reference) + { + lock(this) + { + _objectTable[id] = new ReferenceTableEntry(Time.currentMonotonicTimeMillis(), reference); + } + } + + internal Reference removeObjectReference(Ice.Identity id) + { + lock(this) + { + ReferenceTableEntry entry = null; + if(_objectTable.TryGetValue(id, out entry)) + { + _objectTable.Remove(id); + return entry.reference; + } + return null; + } + } + + private bool checkTTL(long time, int ttl) + { + Debug.Assert(ttl != 0); + if(ttl < 0) // TTL = infinite + { + return true; + } + else + { + return Time.currentMonotonicTimeMillis() - time <= ((long)ttl * 1000); + } + } + + sealed private class EndpointTableEntry + { + public EndpointTableEntry(long time, IceInternal.EndpointI[] endpoints) + { + this.time = time; + this.endpoints = endpoints; + } + + public long time; + public IceInternal.EndpointI[] endpoints; + } + + sealed private class ReferenceTableEntry + { + public ReferenceTableEntry(long time, Reference reference) + { + this.time = time; + this.reference = reference; + } + + public long time; + public Reference reference; + } + + private Dictionary<string, EndpointTableEntry> _adapterEndpointsTable; + private Dictionary<Ice.Identity, ReferenceTableEntry> _objectTable; + } + +} diff --git a/csharp/src/Ice/LoggerAdminI.cs b/csharp/src/Ice/LoggerAdminI.cs new file mode 100644 index 00000000000..e32b098c634 --- /dev/null +++ b/csharp/src/Ice/LoggerAdminI.cs @@ -0,0 +1,483 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace IceInternal +{ +sealed class LoggerAdminI : Ice.LoggerAdminDisp_ +{ + public override void + attachRemoteLogger(Ice.RemoteLoggerPrx prx, Ice.LogMessageType[] messageTypes, string[] categories, + int messageMax, Ice.Current current) + { + if(prx == null) + { + return; // can't send this null RemoteLogger anything! + } + + Ice.RemoteLoggerPrx remoteLogger = Ice.RemoteLoggerPrxHelper.uncheckedCast(prx.ice_twoway()); + + Filters filters = new Filters(messageTypes, categories); + LinkedList<Ice.LogMessage> initLogMessages = null; + + lock(this) + { + if(_sendLogCommunicator == null) + { + if(_destroyed) + { + throw new Ice.ObjectNotExistException(); + } + + _sendLogCommunicator = + createSendLogCommunicator(current.adapter.getCommunicator(), _logger.getLocalLogger()); + } + + Ice.Identity remoteLoggerId = remoteLogger.ice_getIdentity(); + + if(_remoteLoggerMap.ContainsKey(remoteLoggerId)) + { + if(_traceLevel > 0) + { + _logger.trace(_traceCategory, "rejecting `" + remoteLogger.ToString() + + "' with RemoteLoggerAlreadyAttachedException"); + } + + throw new Ice.RemoteLoggerAlreadyAttachedException(); + } + + _remoteLoggerMap.Add(remoteLoggerId, + new RemoteLoggerData(changeCommunicator(remoteLogger, _sendLogCommunicator), filters)); + + if(messageMax != 0) + { + initLogMessages = new LinkedList<Ice.LogMessage>(_queue); // copy + } + else + { + initLogMessages = new LinkedList<Ice.LogMessage>(); + } + } + + if(_traceLevel > 0) + { + _logger.trace(_traceCategory, "attached `" + remoteLogger.ToString() + "'"); + } + + if(initLogMessages.Count > 0) + { + filterLogMessages(initLogMessages, filters.messageTypes, filters.traceCategories, messageMax); + } + + try + { + remoteLogger.begin_init(_logger.getPrefix(), initLogMessages.ToArray(), initCompleted, null); + } + catch(Ice.LocalException ex) + { + deadRemoteLogger(remoteLogger, _logger, ex, "init"); + throw ex; + } + } + + public override bool + detachRemoteLogger(Ice.RemoteLoggerPrx remoteLogger, Ice.Current current) + { + if(remoteLogger == null) + { + return false; + } + + // + // No need to convert the proxy as we only use its identity + // + bool found = removeRemoteLogger(remoteLogger); + + if(_traceLevel > 0) + { + if(found) + { + _logger.trace(_traceCategory, "detached `" + remoteLogger.ToString() + "'"); + } + else + { + _logger.trace(_traceCategory, "cannot detach `" + remoteLogger.ToString() + "': not found"); + } + } + + return found; + } + + public override Ice.LogMessage[] + getLog(Ice.LogMessageType[] messageTypes, string[] categories, int messageMax, out string prefix, + Ice.Current current) + { + LinkedList<Ice.LogMessage> logMessages = null; + lock(this) + { + if(messageMax != 0) + { + logMessages = new LinkedList<Ice.LogMessage>(_queue); + } + else + { + logMessages = new LinkedList<Ice.LogMessage>(); + } + } + + prefix = _logger.getPrefix(); + + if(logMessages.Count > 0) + { + Filters filters = new Filters(messageTypes, categories); + filterLogMessages(logMessages, filters.messageTypes, filters.traceCategories, messageMax); + } + return logMessages.ToArray(); + } + + + internal LoggerAdminI(Ice.Properties props, LoggerAdminLoggerI logger) + { + _maxLogCount = props.getPropertyAsIntWithDefault("Ice.Admin.Logger.KeepLogs", 100); + _maxTraceCount = props.getPropertyAsIntWithDefault("Ice.Admin.Logger.KeepTraces", 100); + _traceLevel = props.getPropertyAsInt("Ice.Trace.Admin.Logger"); + _logger = logger; + } + + internal void destroy() + { + Ice.Communicator sendLogCommunicator = null; + + lock(this) + { + if(!_destroyed) + { + _destroyed = true; + sendLogCommunicator = _sendLogCommunicator; + _sendLogCommunicator = null; + } + } + + // + // Destroy outside lock to avoid deadlock when there are outstanding two-way log calls sent to + // remote logggers + // + if(sendLogCommunicator != null) + { + sendLogCommunicator.destroy(); + } + } + + internal List<Ice.RemoteLoggerPrx> log(Ice.LogMessage logMessage) + { + lock(this) + { + List<Ice.RemoteLoggerPrx> remoteLoggers = null; + + // + // Put message in _queue + // + if((logMessage.type != Ice.LogMessageType.TraceMessage && _maxLogCount > 0) || + (logMessage.type == Ice.LogMessageType.TraceMessage && _maxTraceCount > 0)) + { + _queue.AddLast(logMessage); + + if(logMessage.type != Ice.LogMessageType.TraceMessage) + { + Debug.Assert(_maxLogCount > 0); + if(_logCount == _maxLogCount) + { + // + // Need to remove the oldest log from the queue + // + Debug.Assert(_oldestLog != null); + var next = _oldestLog.Next; + _queue.Remove(_oldestLog); + _oldestLog = next; + + while(_oldestLog != null && _oldestLog.Value.type == Ice.LogMessageType.TraceMessage) + { + _oldestLog = _oldestLog.Next; + } + Debug.Assert(_oldestLog != null); // remember: we just added a Log at the end + } + else + { + Debug.Assert(_logCount < _maxLogCount); + _logCount++; + if(_oldestLog == null) + { + _oldestLog = _queue.Last; + } + } + } + else + { + Debug.Assert(_maxTraceCount > 0); + if(_traceCount == _maxTraceCount) + { + // + // Need to remove the oldest trace from the queue + // + Debug.Assert(_oldestTrace != null); + var next = _oldestTrace.Next; + _queue.Remove(_oldestTrace); + _oldestTrace = next; + + while(_oldestTrace != null && _oldestTrace.Value.type != Ice.LogMessageType.TraceMessage) + { + _oldestTrace = _oldestTrace.Next; + } + Debug.Assert(_oldestTrace != null); // remember: we just added a Log at the end + } + else + { + Debug.Assert(_traceCount < _maxTraceCount); + _traceCount++; + if(_oldestTrace == null) + { + _oldestTrace = _queue.Last; + } + } + } + } + + // + // Queue updated, now find which remote loggers want this message + // + foreach(RemoteLoggerData p in _remoteLoggerMap.Values) + { + Filters filters = p.filters; + + if(filters.messageTypes.Count == 0 || filters.messageTypes.Contains(logMessage.type)) + { + if(logMessage.type != Ice.LogMessageType.TraceMessage || filters.traceCategories.Count == 0 || + filters.traceCategories.Contains(logMessage.traceCategory)) + { + if(remoteLoggers == null) + { + remoteLoggers = new List<Ice.RemoteLoggerPrx>(); + } + remoteLoggers.Add(p.remoteLogger); + } + } + } + + return remoteLoggers; + } + } + + internal void deadRemoteLogger(Ice.RemoteLoggerPrx remoteLogger, Ice.Logger logger, Ice.LocalException ex, + string operation) + { + // + // No need to convert remoteLogger as we only use its identity + // + if(removeRemoteLogger(remoteLogger)) + { + if(_traceLevel > 0) + { + logger.trace(_traceCategory, "detached `" + remoteLogger.ToString() + "' because " + + operation + " raised:\n" + ex.ToString()); + } + } + } + + internal int getTraceLevel() + { + return _traceLevel; + } + + private bool removeRemoteLogger(Ice.RemoteLoggerPrx remoteLogger) + { + lock(this) + { + return _remoteLoggerMap.Remove(remoteLogger.ice_getIdentity()); + } + } + + private void initCompleted(Ice.AsyncResult r) + { + Ice.RemoteLoggerPrx remoteLogger = Ice.RemoteLoggerPrxHelper.uncheckedCast(r.getProxy()); + + try + { + remoteLogger.end_init(r); + + if(_traceLevel > 1) + { + _logger.trace(_traceCategory, r.getOperation() + " on `" + remoteLogger.ToString() + + "' completed successfully"); + } + } + catch(Ice.LocalException ex) + { + deadRemoteLogger(remoteLogger, _logger, ex, r.getOperation()); + } + } + + private static void filterLogMessages(LinkedList<Ice.LogMessage> logMessages, + HashSet<Ice.LogMessageType> messageTypes, + HashSet<string> traceCategories, int messageMax) + { + Debug.Assert(logMessages.Count > 0 && messageMax != 0); + + // + // Filter only if one of the 3 filters is set; messageMax < 0 means "give me all" + // that match the other filters, if any. + // + if(messageTypes.Count > 0 || traceCategories.Count > 0 || messageMax > 0) + { + int count = 0; + var p = logMessages.Last; + while(p != null) + { + bool keepIt = false; + Ice.LogMessage msg = p.Value; + if(messageTypes.Count == 0 || messageTypes.Contains(msg.type)) + { + if(msg.type != Ice.LogMessageType.TraceMessage || traceCategories.Count == 0 || + traceCategories.Contains(msg.traceCategory)) + { + keepIt = true; + } + } + + if(keepIt) + { + ++count; + if(messageMax > 0 && count >= messageMax) + { + // Remove all older messages + p = p.Previous; + while(p != null) + { + var previous = p.Previous; + logMessages.Remove(p); + p = previous; + } + break; // while + } + else + { + p = p.Previous; + } + } + else + { + var previous = p.Previous; + logMessages.Remove(p); + p = previous; + } + } + } + // else, don't need any filtering + } + + // + // Change this proxy's communicator, while keeping its invocation timeout + // + private static Ice.RemoteLoggerPrx changeCommunicator(Ice.RemoteLoggerPrx prx, Ice.Communicator communicator) + { + if(prx == null) + { + return null; + } + + Ice.ObjectPrx result = communicator.stringToProxy(prx.ToString()); + return Ice.RemoteLoggerPrxHelper.uncheckedCast(result.ice_invocationTimeout(prx.ice_getInvocationTimeout())); + } + + private static void copyProperties(string prefix, Ice.Properties from, Ice.Properties to) + { + foreach(var p in from.getPropertiesForPrefix(prefix)) + { + to.setProperty(p.Key, p.Value); + } + } + + private static Ice.Communicator createSendLogCommunicator(Ice.Communicator communicator, Ice.Logger logger) + { + Ice.InitializationData initData = new Ice.InitializationData(); + initData.logger = logger; + initData.properties = Ice.Util.createProperties(); + + Ice.Properties mainProps = communicator.getProperties(); + + copyProperties("Ice.Default.Locator", mainProps, initData.properties); + copyProperties("Ice.Plugin.IceSSL", mainProps, initData.properties); + copyProperties("IceSSL.", mainProps, initData.properties); + + string[] extraProps = mainProps.getPropertyAsList("Ice.Admin.Logger.Properties"); + + if(extraProps.Length > 0) + { + for(int i = 0; i < extraProps.Length; ++i) + { + string p = extraProps[i]; + if(!p.StartsWith("--")) + { + extraProps[i] = "--" + p; + } + } + initData.properties.parseCommandLineOptions("", extraProps); + } + return Ice.Util.initialize(initData); + } + + + private readonly LinkedList<Ice.LogMessage> _queue = new LinkedList<Ice.LogMessage>(); + private int _logCount = 0; // non-trace messages + private readonly int _maxLogCount; + private int _traceCount = 0; + private readonly int _maxTraceCount; + private readonly int _traceLevel; + + private LinkedListNode<Ice.LogMessage> _oldestTrace = null; + private LinkedListNode<Ice.LogMessage> _oldestLog = null; + + private class Filters + { + internal Filters(Ice.LogMessageType[] m, string[] c) + { + messageTypes = new HashSet<Ice.LogMessageType>(m); + traceCategories = new HashSet<string>(c); + } + + internal readonly HashSet<Ice.LogMessageType> messageTypes; + internal readonly HashSet<string> traceCategories; + } + + private class RemoteLoggerData + { + internal RemoteLoggerData(Ice.RemoteLoggerPrx prx, Filters f) + { + remoteLogger = prx; + filters = f; + } + + internal readonly Ice.RemoteLoggerPrx remoteLogger; + internal readonly Filters filters; + } + + private readonly Dictionary<Ice.Identity, RemoteLoggerData> _remoteLoggerMap + = new Dictionary<Ice.Identity, RemoteLoggerData>(); + + private readonly LoggerAdminLoggerI _logger; + + private Ice.Communicator _sendLogCommunicator = null; + private bool _destroyed = false; + static private readonly string _traceCategory = "Admin.Logger"; +} + +} diff --git a/csharp/src/Ice/LoggerAdminLoggerI.cs b/csharp/src/Ice/LoggerAdminLoggerI.cs new file mode 100644 index 00000000000..67647f33dc1 --- /dev/null +++ b/csharp/src/Ice/LoggerAdminLoggerI.cs @@ -0,0 +1,241 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +namespace IceInternal +{ + +interface LoggerAdminLogger : Ice.Logger +{ + Ice.Object getFacet(); + void destroy(); +} + +sealed class LoggerAdminLoggerI : LoggerAdminLogger +{ + public void print(string message) + { + Ice.LogMessage logMessage = new Ice.LogMessage(Ice.LogMessageType.PrintMessage, now(), "", message); + _localLogger.print(message); + log(logMessage); + } + + public void trace(string category, string message) + { + Ice.LogMessage logMessage = new Ice.LogMessage(Ice.LogMessageType.TraceMessage, now(), category, message); + _localLogger.trace(category, message); + log(logMessage); + } + + public void warning(string message) + { + Ice.LogMessage logMessage = new Ice.LogMessage(Ice.LogMessageType.WarningMessage, now(), "", message); + _localLogger.warning(message); + log(logMessage); + } + + public void error(string message) + { + Ice.LogMessage logMessage = new Ice.LogMessage(Ice.LogMessageType.ErrorMessage, now(), "", message); + _localLogger.error(message); + log(logMessage); + } + + public string getPrefix() + { + return _localLogger.getPrefix(); + } + + public Ice.Logger cloneWithPrefix(string prefix) + { + return _localLogger.cloneWithPrefix(prefix); + } + + public Ice.Object getFacet() + { + return _loggerAdmin; + } + + public void destroy() + { + Thread thread = null; + lock(this) + { + if(_sendLogThread != null) + { + thread = _sendLogThread; + _sendLogThread = null; + _destroyed = true; + Monitor.PulseAll(this); + } + } + + if(thread != null) + { + thread.Join(); + } + + _loggerAdmin.destroy(); + } + + internal LoggerAdminLoggerI(Ice.Properties props, Ice.Logger localLogger) + { + LoggerAdminLoggerI wrapper = localLogger as LoggerAdminLoggerI; + + if(wrapper != null) + { + _localLogger = wrapper.getLocalLogger(); + } + else + { + _localLogger = localLogger; + } + + _loggerAdmin = new LoggerAdminI(props, this); + } + + internal Ice.Logger getLocalLogger() + { + return _localLogger; + } + + internal void log(Ice.LogMessage logMessage) + { + List<Ice.RemoteLoggerPrx> remoteLoggers = _loggerAdmin.log(logMessage); + + if(remoteLoggers != null) + { + Debug.Assert(remoteLoggers.Count > 0); + + lock(this) + { + if(_sendLogThread == null) + { + _sendLogThread = new Thread(new ThreadStart(run)); + _sendLogThread.Name = "Ice.SendLogThread"; + _sendLogThread.IsBackground = true; + _sendLogThread.Start(); + } + + _jobQueue.Enqueue(new Job(remoteLoggers, logMessage)); + Monitor.PulseAll(this); + } + } + } + + private void run() + { + if(_loggerAdmin.getTraceLevel() > 1) + { + _localLogger.trace(_traceCategory, "send log thread started"); + } + + for(;;) + { + Job job = null; + lock(this) + { + while(!_destroyed && _jobQueue.Count == 0) + { + Monitor.Wait(this); + } + + if(_destroyed) + { + break; // for(;;) + } + + Debug.Assert(_jobQueue.Count > 0); + job = _jobQueue.Dequeue(); + } + + foreach(var p in job.remoteLoggers) + { + if(_loggerAdmin.getTraceLevel() > 1) + { + _localLogger.trace(_traceCategory, "sending log message to `" + p.ToString() + "'"); + } + + try + { + // + // p is a proxy associated with the _sendLogCommunicator + // + p.begin_log(job.logMessage, logCompleted, null); + } + catch(Ice.LocalException ex) + { + _loggerAdmin.deadRemoteLogger(p, _localLogger, ex, "log"); + } + } + } + + if(_loggerAdmin.getTraceLevel() > 1) + { + _localLogger.trace(_traceCategory, "send log thread completed"); + } + } + + private void logCompleted(Ice.AsyncResult r) + { + Ice.RemoteLoggerPrx remoteLogger = Ice.RemoteLoggerPrxHelper.uncheckedCast(r.getProxy()); + + try + { + remoteLogger.end_log(r); + + if(_loggerAdmin.getTraceLevel() > 1) + { + _localLogger.trace(_traceCategory, r.getOperation() + " on `" + remoteLogger.ToString() + + "' completed successfully"); + } + } + catch(Ice.CommunicatorDestroyedException) + { + // expected if there are outstanding calls during communicator destruction + } + catch(Ice.LocalException ex) + { + _loggerAdmin.deadRemoteLogger(remoteLogger, _localLogger, ex, r.getOperation()); + } + } + + static private long now() + { + TimeSpan t = DateTime.UtcNow - _unixEpoch; + return Convert.ToInt64(t.TotalMilliseconds * 1000); + } + + private class Job + { + internal Job(List<Ice.RemoteLoggerPrx> r, Ice.LogMessage l) + { + remoteLoggers = r; + logMessage = l; + } + + internal readonly List<Ice.RemoteLoggerPrx> remoteLoggers; + internal readonly Ice.LogMessage logMessage; + } + + private readonly Ice.Logger _localLogger; + private readonly LoggerAdminI _loggerAdmin; + private bool _destroyed = false; + private Thread _sendLogThread; + private readonly Queue<Job> _jobQueue = new Queue<Job>(); + + static private readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + static private readonly string _traceCategory = "Admin.Logger"; +} + +} diff --git a/csharp/src/Ice/LoggerI.cs b/csharp/src/Ice/LoggerI.cs new file mode 100644 index 00000000000..f0e59e0eadb --- /dev/null +++ b/csharp/src/Ice/LoggerI.cs @@ -0,0 +1,264 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#define TRACE + +namespace Ice +{ + using System.Diagnostics; + using System.Globalization; +#if !SILVERLIGHT && !UNITY + using System.IO; +#endif + + public abstract class LoggerI : Logger + { + public LoggerI(string prefix) + { + _prefix = prefix; + + if(prefix.Length > 0) + { + _formattedPrefix = prefix + ": "; + } + + _date = "d"; + _time = "HH:mm:ss:fff"; + } + + public void print(string message) + { + lock(_globalMutex) + { + write(message); + } + } + + public virtual void trace(string category, string message) + { + string s = format("--", category, message); + lock(_globalMutex) + { + write(s); + } + } + + public virtual void warning(string message) + { + string s = format("-!", "warning", message); + lock(_globalMutex) + { + write(s); + } + } + + public virtual void error(string message) + { + string s = format("!!", "error", message); + lock(_globalMutex) + { + write(s); + } + } + + public string getPrefix() + { + return _prefix; + } + + private string format(string prefix, string category, string message) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(prefix); + s.Append(' '); + s.Append(System.DateTime.Now.ToString(_date, CultureInfo.CurrentCulture)); + s.Append(' '); + s.Append(System.DateTime.Now.ToString(_time, CultureInfo.CurrentCulture)); + s.Append(' '); + s.Append(_formattedPrefix); + s.Append(category); + s.Append(": "); + s.Append(message); + s.Replace("\n", "\n "); + return s.ToString(); + } + + public abstract Logger cloneWithPrefix(string prefix); + + protected abstract void write(string message); + + internal string _prefix = ""; + internal string _formattedPrefix = ""; + internal string _date = null; + internal string _time = null; + + internal static object _globalMutex = new object(); + } + + public sealed class ConsoleLoggerI : LoggerI + { + public ConsoleLoggerI(string prefix) + : base(prefix) + { + _date = "d"; + _time = "HH:mm:ss:fff"; + } + + public override Logger cloneWithPrefix(string prefix) + { + return new ConsoleLoggerI(prefix); + } + + protected override void write(string message) + { + System.Console.Error.WriteLine(message); + } + } + +#if !SILVERLIGHT && !UNITY + public sealed class FileLoggerI : LoggerI + { + public FileLoggerI(string prefix, string file) : + base(prefix) + { + _file = file; + _writer = new StreamWriter(new FileStream(file, FileMode.Append, FileAccess.Write, FileShare.None)); + } + + public override Logger cloneWithPrefix(string prefix) + { + return new FileLoggerI(prefix, _file); + } + + protected override void write(string message) + { + _writer.WriteLine(message); + _writer.Flush(); + } + + private string _file; + private TextWriter _writer; + } + + +# if !COMPACT + public class ConsoleListener : TraceListener + { + public ConsoleListener() + { + _date = "d"; + _time = "HH:mm:ss:fff"; + } + + public override void TraceEvent(TraceEventCache cache, string source, TraceEventType type, + int id, string message) + { + System.Text.StringBuilder s; + if(type == TraceEventType.Error) + { + s = new System.Text.StringBuilder("!!"); + } + else if(type == TraceEventType.Warning) + { + s = new System.Text.StringBuilder("-!"); + } + else + { + s = new System.Text.StringBuilder("--"); + } + s.Append(' '); + s.Append(System.DateTime.Now.ToString(_date, CultureInfo.CurrentCulture)); + s.Append(' '); + s.Append(System.DateTime.Now.ToString(_time, CultureInfo.CurrentCulture)); + s.Append(' '); + s.Append(message); + this.WriteLine(s.ToString()); + } + + public override void Write(string message) + { + System.Console.Error.Write(message); + } + + public override void WriteLine(string message) + { + System.Console.Error.WriteLine(message); + } + + internal string _date = null; + internal string _time = null; + } + + public sealed class TraceLoggerI : LoggerI + { + public TraceLoggerI(string prefix, bool console) + : base(prefix) + { + _console = console; + if(console && !Trace.Listeners.Contains(_consoleListener)) + { + Trace.Listeners.Add(_consoleListener); + } + } + + public override void trace(string category, string message) + { + string s = format(category, message); + lock(_globalMutex) + { + Trace.TraceInformation(s); + Trace.Flush(); + } + } + + public override void warning(string message) + { + string s = format("warning", message); + lock(_globalMutex) + { + Trace.TraceWarning(s); + Trace.Flush(); + } + } + + public override void error(string message) + { + string s = format("error", message); + { + Trace.TraceError(s); + Trace.Flush(); + } + } + + public override Logger cloneWithPrefix(string prefix) + { + return new TraceLoggerI(prefix, _console); + } + + protected override void write(string message) + { + Trace.WriteLine(message); + Trace.Flush(); + } + + private string format(string category, string message) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(_formattedPrefix); + s.Append(category); + s.Append(": "); + s.Append(message); + s.Replace("\n", "\n "); + return s.ToString(); + } + + private bool _console; + internal static ConsoleListener _consoleListener = new ConsoleListener(); + } +# endif +#endif +} diff --git a/csharp/src/Ice/LoggerPlugin.cs b/csharp/src/Ice/LoggerPlugin.cs new file mode 100644 index 00000000000..942675580ea --- /dev/null +++ b/csharp/src/Ice/LoggerPlugin.cs @@ -0,0 +1,65 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// Class to support custom loggers. Applications using a custom logger + /// instantiate a LoggerPlugin with a custom logger and + /// return the instance from their PluginFactory implementation. + /// </summary> + public class LoggerPlugin : Ice.Plugin + { + /// <summary> + /// Installs a custom logger for a communicator. + /// </summary> + /// <param name="communicator">The communicator using the custom logger.</param> + /// <param name="logger">The custom logger for the communicator.</param> + public + LoggerPlugin(Communicator communicator, Logger logger) + { + if(communicator == null) + { + PluginInitializationException ex = new PluginInitializationException(); + ex.reason = "Communicator cannot be null"; + throw ex; + } + + if(logger == null) + { + PluginInitializationException ex = new PluginInitializationException(); + ex.reason = "Logger cannot be null"; + throw ex; + } + + IceInternal.Instance instance = IceInternal.Util.getInstance(communicator); + instance.setLogger(logger); + } + + /// <summary> + /// Called by the Ice run time during communicator initialization. The derived class + /// can override this method to perform any initialization that might be required + /// by a custom logger. + /// </summary> + public void + initialize() + { + } + + /// <summary> + /// Called by the Ice run time when the communicator is destroyed. The derived class + /// can override this method to perform any finalization that might be required + /// by a custom logger. + /// </summary> + public void + destroy() + { + } + } +} diff --git a/csharp/src/Ice/Makefile b/csharp/src/Ice/Makefile new file mode 100644 index 00000000000..ee868c8c3d6 --- /dev/null +++ b/csharp/src/Ice/Makefile @@ -0,0 +1,201 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = Ice +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = Acceptor.cs \ + ACM.cs \ + AMDCallback.cs \ + Application.cs \ + Arrays.cs \ + AssemblyInfo.cs \ + AssemblyUtil.cs \ + AsyncIOThread.cs \ + AsyncResult.cs \ + Base64.cs \ + BasicStream.cs \ + BatchRequestInterceptor.cs \ + BatchRequestQueue.cs \ + Buffer.cs \ + ByteBuffer.cs \ + CollectionBase.cs \ + Collections.cs \ + CollocatedRequestHandler.cs \ + CommunicatorI.cs \ + Compare.cs \ + ConnectionFactory.cs \ + ConnectionI.cs \ + ConnectionRequestHandler.cs \ + Connector.cs \ + ConnectRequestHandler.cs \ + DefaultsAndOverrides.cs \ + DictionaryBase.cs \ + DispatchInterceptor.cs \ + EndpointFactory.cs \ + EndpointFactoryManager.cs \ + EndpointHostResolver.cs \ + EndpointI.cs \ + EventHandler.cs \ + Exception.cs \ + FormatType.cs \ + HttpParser.cs \ + ImplicitContextI.cs \ + Incoming.cs \ + IncomingAsync.cs \ + Instance.cs \ + InstrumentationI.cs \ + IPEndpointI.cs \ + LocatorInfo.cs \ + LoggerAdminI.cs \ + LoggerAdminLoggerI.cs \ + LoggerI.cs \ + LoggerPlugin.cs \ + MetricsAdminI.cs \ + MetricsObserverI.cs \ + Network.cs \ + NetworkProxy.cs \ + Object.cs \ + ObjectAdapterFactory.cs \ + ObjectAdapterI.cs \ + ObjectFactoryManager.cs \ + ObserverHelper.cs \ + OpaqueEndpointI.cs \ + Optional.cs \ + Options.cs \ + OutgoingAsync.cs \ + OutputBase.cs \ + Patcher.cs \ + PluginManagerI.cs \ + ProcessI.cs \ + PropertiesAdminI.cs \ + PropertiesI.cs \ + Property.cs \ + PropertyNames.cs \ + Protocol.cs \ + ProtocolInstance.cs \ + ProtocolPluginFacade.cs \ + Proxy.cs \ + ProxyFactory.cs \ + ProxyIdentityKey.cs \ + Reference.cs \ + ReferenceFactory.cs \ + ReplyStatus.cs \ + RequestHandler.cs \ + RequestHandlerFactory.cs \ + ResponseHandler.cs \ + RetryQueue.cs \ + RouterInfo.cs \ + ServantManager.cs \ + SliceChecksums.cs \ + SlicedData.cs \ + SocketOperation.cs \ + Stream.cs \ + StreamI.cs \ + StreamSocket.cs \ + StreamWrapper.cs \ + StringUtil.cs \ + SysLoggerI.cs \ + TcpAcceptor.cs \ + TcpConnector.cs \ + TcpEndpointI.cs \ + TcpTransceiver.cs \ + ThreadHookPlugin.cs \ + ThreadPool.cs \ + TieBase.cs \ + Time.cs \ + Timer.cs \ + TraceLevels.cs \ + TraceUtil.cs \ + Transceiver.cs \ + UdpConnector.cs \ + UdpEndpointI.cs \ + UdpTransceiver.cs \ + UnknownSlicedObject.cs \ + UserExceptionFactory.cs \ + Util.cs \ + ValueWriter.cs \ + WSAcceptor.cs \ + WSConnector.cs \ + WSEndpoint.cs \ + WSTransceiver.cs + +SLICE_SRCS = $(SDIR)/BuiltinSequences.ice \ + $(SDIR)/Communicator.ice \ + $(SDIR)/Connection.ice \ + $(SDIR)/Current.ice \ + $(SDIR)/Endpoint.ice \ + $(SDIR)/EndpointTypes.ice \ + $(SDIR)/FacetMap.ice \ + $(SDIR)/Identity.ice \ + $(SDIR)/ImplicitContext.ice \ + $(SDIR)/Instrumentation.ice \ + $(SDIR)/LocalException.ice \ + $(SDIR)/Locator.ice \ + $(SDIR)/Logger.ice \ + $(SDIR)/Metrics.ice \ + $(SDIR)/ObjectAdapter.ice \ + $(SDIR)/ObjectFactory.ice \ + $(SDIR)/Plugin.ice \ + $(SDIR)/Process.ice \ + $(SDIR)/Properties.ice \ + $(SDIR)/PropertiesAdmin.ice \ + $(SDIR)/RemoteLogger.ice \ + $(SDIR)/Router.ice \ + $(SDIR)/ServantLocator.ice \ + $(SDIR)/SliceChecksumDict.ice \ + $(SDIR)/Version.ice \ + +SDIR = $(slicedir)/Ice +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +ifneq ($(MANAGED),yes) + MCSFLAGS := $(MCSFLAGS) -unsafe+ +endif + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(subst /,$(DSEP),$^) + $(INSTALL) $(LIBNAME).config $(assembliesdir) + +clean:: + -rm -f $(assembliesdir)/$(LIBNAME).config + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +else +install:: all + $(call installdata,$(TARGETS).config,$(DESTDIR)$(install_assembliesdir)) +endif + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +clean:: + echo $(GEN_SRCS) + -rm -f $(assembliesdir)/$(PKG).xml + +$(GDIR)/BuiltinSequences.cs: $(SDIR)/BuiltinSequences.ice $(SLICE2CPP) $(SLICEPARSERLIB) + rm -f $(GDIR)/BuiltinSequences.cs + $(SLICE2CS) $(SLICE2CSFLAGS) --stream $(SDIR)/BuiltinSequences.ice + mv BuiltinSequences.cs $(GDIR) diff --git a/csharp/src/Ice/Makefile.mak b/csharp/src/Ice/Makefile.mak new file mode 100644 index 00000000000..f6c85328dc5 --- /dev/null +++ b/csharp/src/Ice/Makefile.mak @@ -0,0 +1,199 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = Ice +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = Acceptor.cs \ + ACM.cs \ + AMDCallback.cs \ + Application.cs \ + Arrays.cs \ + AssemblyInfo.cs \ + AssemblyUtil.cs \ + AsyncIOThread.cs \ + AsyncResult.cs \ + Base64.cs \ + BasicStream.cs \ + BatchRequestInterceptor.cs \ + BatchRequestQueue.cs \ + Buffer.cs \ + ByteBuffer.cs \ + CollectionBase.cs \ + Collections.cs \ + CollocatedRequestHandler.cs \ + CommunicatorI.cs \ + Compare.cs \ + ConnectionFactory.cs \ + ConnectionI.cs \ + ConnectionRequestHandler.cs \ + Connector.cs \ + ConnectRequestHandler.cs \ + DefaultsAndOverrides.cs \ + DictionaryBase.cs \ + DispatchInterceptor.cs \ + EndpointFactory.cs \ + EndpointFactoryManager.cs \ + EndpointHostResolver.cs \ + EndpointI.cs \ + EventHandler.cs \ + Exception.cs \ + FormatType.cs \ + HashSet.cs \ + HttpParser.cs \ + ImplicitContextI.cs \ + Incoming.cs \ + IncomingAsync.cs \ + Instance.cs \ + InstrumentationI.cs \ + IPEndpointI.cs \ + LocatorInfo.cs \ + LoggerAdminI.cs \ + LoggerAdminLoggerI.cs \ + LoggerI.cs \ + LoggerPlugin.cs \ + MetricsAdminI.cs \ + MetricsObserverI.cs \ + Network.cs \ + NetworkProxy.cs \ + Object.cs \ + ObjectAdapterFactory.cs \ + ObjectAdapterI.cs \ + ObjectFactoryManager.cs \ + ObserverHelper.cs \ + OpaqueEndpointI.cs \ + Optional.cs \ + Options.cs \ + OutgoingAsync.cs \ + OutputBase.cs \ + Patcher.cs \ + PluginManagerI.cs \ + ProcessI.cs \ + PropertiesAdminI.cs \ + PropertiesI.cs \ + Property.cs \ + PropertyNames.cs \ + Protocol.cs \ + ProtocolInstance.cs \ + ProtocolPluginFacade.cs \ + Proxy.cs \ + ProxyFactory.cs \ + ProxyIdentityKey.cs \ + Reference.cs \ + ReferenceFactory.cs \ + ReplyStatus.cs \ + RequestHandler.cs \ + RequestHandlerFactory.cs \ + ResponseHandler.cs \ + RetryQueue.cs \ + RouterInfo.cs \ + ServantManager.cs \ + SliceChecksums.cs \ + SlicedData.cs \ + SocketOperation.cs \ + Stream.cs \ + StreamI.cs \ + StreamSocket.cs \ + StreamWrapper.cs \ + StringUtil.cs \ + SysLoggerI.cs \ + TcpAcceptor.cs \ + TcpConnector.cs \ + TcpEndpointI.cs \ + TcpTransceiver.cs \ + ThreadHookPlugin.cs \ + ThreadPool.cs \ + TieBase.cs \ + Time.cs \ + Timer.cs \ + TraceLevels.cs \ + TraceUtil.cs \ + Transceiver.cs \ + UdpConnector.cs \ + UdpEndpointI.cs \ + UdpTransceiver.cs \ + UnknownSlicedObject.cs \ + UserExceptionFactory.cs \ + Util.cs \ + ValueWriter.cs \ + WSAcceptor.cs \ + WSConnector.cs \ + WSEndpoint.cs \ + WSTransceiver.cs + +GEN_SRCS = $(GDIR)\BuiltinSequences.cs \ + $(GDIR)\Communicator.cs \ + $(GDIR)\Connection.cs \ + $(GDIR)\Current.cs \ + $(GDIR)\Endpoint.cs \ + $(GDIR)\EndpointTypes.cs \ + $(GDIR)\FacetMap.cs \ + $(GDIR)\Identity.cs \ + $(GDIR)\ImplicitContext.cs \ + $(GDIR)\Instrumentation.cs \ + $(GDIR)\LocalException.cs \ + $(GDIR)\Locator.cs \ + $(GDIR)\Logger.cs \ + $(GDIR)\Metrics.cs \ + $(GDIR)\ObjectAdapter.cs \ + $(GDIR)\ObjectFactory.cs \ + $(GDIR)\Plugin.cs \ + $(GDIR)\Process.cs \ + $(GDIR)\Properties.cs \ + $(GDIR)\PropertiesAdmin.cs \ + $(GDIR)\RemoteLogger.cs \ + $(GDIR)\Router.cs \ + $(GDIR)\ServantLocator.cs \ + $(GDIR)\SliceChecksumDict.cs \ + $(GDIR)\Version.cs + +SDIR = $(slicedir)\Ice +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +!if "$(MANAGED)" != "yes" +MCSFLAGS = $(MCSFLAGS) /unsafe +!endif + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) --ice -I"$(slicedir)" + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) /baseaddress:0x20000000 $(MCSFLAGS) $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif + +$(GDIR)\BuiltinSequences.cs: "$(SDIR)\BuiltinSequences.ice" "$(SLICE2CS)" "$(SLICEPARSERLIB)" + del /q $(GDIR)\BuiltinSequences.cs + "$(SLICE2CS)" $(SLICE2CSFLAGS) --stream "$(SDIR)\BuiltinSequences.ice" + move BuiltinSequences.cs $(GDIR) diff --git a/csharp/src/Ice/MetricsAdminI.cs b/csharp/src/Ice/MetricsAdminI.cs new file mode 100644 index 00000000000..d6f58e9de20 --- /dev/null +++ b/csharp/src/Ice/MetricsAdminI.cs @@ -0,0 +1,1048 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Text; + using System.Diagnostics; + using System.Collections.Generic; + using System.Text.RegularExpressions; + + internal interface IMetricsMap + { + IceMX.Metrics[] getMetrics(); + IceMX.MetricsFailures[] getFailures(); + IceMX.MetricsFailures getFailures(string id); + Dictionary<string, string> getProperties(); + }; + + interface ISubMap + { + void addSubMapToMetrics(IceMX.Metrics metrics); + }; + + interface ISubMapCloneFactory + { + ISubMap create(); + }; + + interface ISubMapFactory + { + ISubMapCloneFactory createCloneFactory(string subMapPrefix, Ice.Properties properties); + }; + + internal interface IMetricsMapFactory + { + void registerSubMap<S>(string subMap, System.Reflection.FieldInfo field) where S : IceMX.Metrics, new(); + void update(); + IMetricsMap create(string mapPrefix, Ice.Properties properties); + }; + + internal class SubMap<S> : ISubMap where S : IceMX.Metrics, new() + { + internal SubMap(MetricsMap<S> map, System.Reflection.FieldInfo field) + { + _map = map; + _field = field; + } + + internal MetricsMap<S>.Entry getMatching(IceMX.MetricsHelper<S> helper) + { + return _map.getMatching(helper, null); + } + + public void addSubMapToMetrics(IceMX.Metrics metrics) + { + try + { + _field.SetValue(metrics, _map.getMetrics()); + } + catch(Exception) + { + Debug.Assert(false); + } + } + + readonly private MetricsMap<S> _map; + readonly private System.Reflection.FieldInfo _field; + }; + + internal class SubMapCloneFactory<S> : ISubMapCloneFactory where S : IceMX.Metrics, new() + { + internal SubMapCloneFactory(MetricsMap<S> map, System.Reflection.FieldInfo field) + { + _map = map; + _field = field; + } + + public ISubMap create() + { + return new SubMap<S>(new MetricsMap<S>(_map), _field); + } + + readonly private MetricsMap<S> _map; + readonly private System.Reflection.FieldInfo _field; + }; + + class SubMapFactory<S> : ISubMapFactory where S : IceMX.Metrics, new() + { + internal SubMapFactory(System.Reflection.FieldInfo field) + { + _field = field; + } + + public ISubMapCloneFactory createCloneFactory(string subMapPrefix, Ice.Properties properties) + { + return new SubMapCloneFactory<S>(new MetricsMap<S>(subMapPrefix, properties, null), _field); + } + + readonly private System.Reflection.FieldInfo _field; + }; + + public class MetricsMap<T> : IMetricsMap where T : IceMX.Metrics, new() + { + public class Entry + { + internal Entry(MetricsMap<T> map, T obj) + { + _map = map; + _object = obj; + } + + public void failed(string exceptionName) + { + lock(_map) + { + ++_object.failures; + int count; + if(_failures == null) + { + _failures = new Dictionary<string, int>(); + } + if(_failures.TryGetValue(exceptionName, out count)) + { + _failures[exceptionName] = count + 1; + } + else + { + _failures[exceptionName] = 1; + } + } + } + + internal MetricsMap<S>.Entry getMatching<S>(string mapName, IceMX.MetricsHelper<S> helper) + where S : IceMX.Metrics, new() + { + ISubMap m; + lock(_map) + { + if(_subMaps == null || !_subMaps.TryGetValue(mapName, out m)) + { + m = _map.createSubMap(mapName); + if(m == null) + { + return null; + } + if(_subMaps == null) + { + _subMaps = new Dictionary<string, ISubMap>(); + } + _subMaps.Add(mapName, m); + } + } + return ((SubMap<S>)m).getMatching(helper); + } + + public void detach(long lifetime) + { + lock(_map) + { + _object.totalLifetime += lifetime; + if(--_object.current == 0) + { + _map.detached(this); + } + } + } + + public void execute(IceMX.Observer<T>.MetricsUpdate func) + { + lock(_map) + { + func(_object); + } + } + + public MetricsMap<T> getMap() + { + return _map; + } + + internal IceMX.MetricsFailures getFailures() + { + if(_failures == null) + { + return null; + } + IceMX.MetricsFailures f = new IceMX.MetricsFailures(); + f.id = _object.id; + f.failures = new Dictionary<string, int>(_failures); + return f; + } + + internal void attach(IceMX.MetricsHelper<T> helper) + { + ++_object.total; + ++_object.current; + helper.initMetrics(_object); + } + + internal bool isDetached() + { + return _object.current == 0; + } + + internal IceMX.Metrics clone() + { + T metrics = (T)_object.Clone(); + if(_subMaps != null) + { + foreach(ISubMap s in _subMaps.Values) + { + s.addSubMapToMetrics(metrics); + } + } + return metrics; + } + + internal string getId() + { + return _object.id; + } + + private MetricsMap<T> _map; + private T _object; + private Dictionary<string, int> _failures; + private Dictionary<string, ISubMap> _subMaps; + }; + + internal MetricsMap(string mapPrefix, Ice.Properties props, Dictionary<string, ISubMapFactory> subMaps) + { + MetricsAdminI.validateProperties(mapPrefix, props); + _properties = props.getPropertiesForPrefix(mapPrefix); + + _retain = props.getPropertyAsIntWithDefault(mapPrefix + "RetainDetached", 10); + _accept = parseRule(props, mapPrefix + "Accept"); + _reject = parseRule(props, mapPrefix + "Reject"); + _groupByAttributes = new List<string>(); + _groupBySeparators = new List<string>(); + + string groupBy = props.getPropertyWithDefault(mapPrefix + "GroupBy", "id"); + if(groupBy.Length > 0) + { + string v = ""; + bool attribute = Char.IsLetter(groupBy[0]) || Char.IsDigit(groupBy[0]); + if(!attribute) + { + _groupByAttributes.Add(""); + } + + foreach(char p in groupBy) + { + bool isAlphaNum = Char.IsLetter(p) || Char.IsDigit(p) || p == '.'; + if(attribute && !isAlphaNum) + { + _groupByAttributes.Add(v); + v = "" + p; + attribute = false; + } + else if(!attribute && isAlphaNum) + { + _groupBySeparators.Add(v); + v = "" + p; + attribute = true; + } + else + { + v += p; + } + } + + if(attribute) + { + _groupByAttributes.Add(v); + } + else + { + _groupBySeparators.Add(v); + } + } + + if(subMaps != null && subMaps.Count > 0) + { + _subMaps = new Dictionary<string, ISubMapCloneFactory>(); + + List<string> subMapNames = new List<string>(); + foreach(KeyValuePair<string, ISubMapFactory> e in subMaps) + { + subMapNames.Add(e.Key); + string subMapsPrefix = mapPrefix + "Map."; + string subMapPrefix = subMapsPrefix + e.Key + '.'; + if(props.getPropertiesForPrefix(subMapPrefix).Count == 0) + { + if(props.getPropertiesForPrefix(subMapsPrefix).Count == 0) + { + subMapPrefix = mapPrefix; + } + else + { + continue; // This sub-map isn't configured. + } + } + + _subMaps.Add(e.Key, e.Value.createCloneFactory(subMapPrefix, props)); + } + } + else + { + _subMaps = null; + } + } + + internal MetricsMap(MetricsMap<T> map) + { + _properties = map._properties; + _groupByAttributes = map._groupByAttributes; + _groupBySeparators = map._groupBySeparators; + _retain = map._retain; + _accept = map._accept; + _reject = map._reject; + _subMaps = map._subMaps; + } + + public Dictionary<string, string> getProperties() + { + return _properties; + } + + public IceMX.Metrics[] getMetrics() + { + lock(this) + { + IceMX.Metrics[] metrics = new IceMX.Metrics[_objects.Count]; + int i = 0; + foreach(Entry e in _objects.Values) + { + metrics[i++] = e.clone(); + } + return metrics; + } + } + + public IceMX.MetricsFailures[] getFailures() + { + lock(this) + { + List<IceMX.MetricsFailures> failures = new List<IceMX.MetricsFailures>(); + foreach(Entry e in _objects.Values) + { + IceMX.MetricsFailures f = e.getFailures(); + if(f != null) + { + failures.Add(f); + } + } + return failures.ToArray(); + } + } + + public IceMX.MetricsFailures getFailures(string id) + { + lock(this) + { + Entry e; + if(_objects.TryGetValue(id, out e)) + { + return e.getFailures(); + } + return null; + } + } + + ISubMap createSubMap(string subMapName) + { + if(_subMaps == null) + { + return null; + } + ISubMapCloneFactory factory; + if(_subMaps.TryGetValue(subMapName, out factory)) + { + return factory.create(); + } + return null; + } + + public Entry getMatching(IceMX.MetricsHelper<T> helper, Entry previous) + { + // + // Check the accept and reject filters. + // + foreach(KeyValuePair<string, Regex> e in _accept) + { + if(!match(e.Key, e.Value, helper, false)) + { + return null; + } + } + + foreach(KeyValuePair<string, Regex> e in _reject) + { + if(match(e.Key, e.Value, helper, true)) + { + return null; + } + } + + // + // Compute the key from the GroupBy property. + // + string key; + try + { + if(_groupByAttributes.Count == 1) + { + key = helper.resolve(_groupByAttributes[0]); + } + else + { + StringBuilder os = new StringBuilder(); + IEnumerator<string> q = _groupBySeparators.GetEnumerator(); + foreach(string p in _groupByAttributes) + { + os.Append(helper.resolve(p)); + if(q.MoveNext()) + { + os.Append(q.Current); + } + } + key = os.ToString(); + } + } + catch(Exception) + { + return null; + } + + // + // Lookup the metrics object. + // + lock(this) + { + if(previous != null && previous.getId().Equals(key)) + { + Debug.Assert(_objects[key] == previous); + return previous; + } + + Entry e; + if(!_objects.TryGetValue(key, out e)) + { + try + { + T t = new T(); + t.id = key; + e = new Entry(this, t); + _objects.Add(key, e); + } + catch(Exception) + { + Debug.Assert(false); + } + } + e.attach(helper); + return e; + } + } + + private void detached(Entry entry) + { + if(_retain == 0) + { + return; + } + + if(_detachedQueue == null) + { + _detachedQueue = new LinkedList<Entry>(); + } + Debug.Assert(_detachedQueue.Count <= _retain); + + // Compress the queue by removing entries which are no longer detached. + LinkedListNode<Entry> p = _detachedQueue.First; + while(p != null) + { + LinkedListNode<Entry> next = p.Next; + if(p.Value == entry || !p.Value.isDetached()) + { + _detachedQueue.Remove(p); + } + p = next; + } + + // If there's still no room, remove the oldest entry (at the front). + if(_detachedQueue.Count == _retain) + { + _objects.Remove(_detachedQueue.First.Value.getId()); + _detachedQueue.RemoveFirst(); + } + + // Add the entry at the back of the queue. + _detachedQueue.AddLast(entry); + } + + private Dictionary<string, Regex> parseRule(Ice.Properties properties, string name) + { + Dictionary<string, Regex> pats = new Dictionary<string, Regex>(); + Dictionary<string, string> rules = properties.getPropertiesForPrefix(name + '.'); + foreach(KeyValuePair<string, string> e in rules) + { + pats.Add(e.Key.Substring(name.Length + 1), new Regex(e.Value)); + } + return pats; + } + + private bool match(string attribute, Regex regex, IceMX.MetricsHelper<T> helper, bool reject) + { + string value; + try + { + value = helper.resolve(attribute); + } + catch(Exception) + { + return !reject; + } + return regex.IsMatch(value); + } + + readonly private Dictionary<string, string> _properties; + readonly private List<string> _groupByAttributes; + readonly private List<string> _groupBySeparators; + readonly private int _retain; + readonly private Dictionary<string, Regex> _accept; + readonly private Dictionary<string, Regex> _reject; + + readonly private Dictionary<string, Entry> _objects = new Dictionary<string, Entry>(); + readonly private Dictionary<string, ISubMapCloneFactory> _subMaps; + private LinkedList<Entry> _detachedQueue; + }; + + internal class MetricsViewI + { + internal MetricsViewI(string name) + { + _name = name; + } + + internal bool addOrUpdateMap(Ice.Properties properties, string mapName, IMetricsMapFactory factory, + Ice.Logger logger) + { + // + // Add maps to views configured with the given map. + // + string viewPrefix = "IceMX.Metrics." + _name + "."; + string mapsPrefix = viewPrefix + "Map."; + Dictionary<string, string> mapsProps = properties.getPropertiesForPrefix(mapsPrefix); + + string mapPrefix; + Dictionary<string, string> mapProps = new Dictionary<string, string>(); + if(mapsProps.Count > 0) + { + mapPrefix = mapsPrefix + mapName + "."; + mapProps = properties.getPropertiesForPrefix(mapPrefix); + if(mapProps.Count == 0) + { + // This map isn't configured for this view. + return _maps.Remove(mapName); + } + } + else + { + mapPrefix = viewPrefix; + mapProps = properties.getPropertiesForPrefix(mapPrefix); + } + + if(properties.getPropertyAsInt(mapPrefix + "Disabled") > 0) + { + // This map is disabled for this view. + return _maps.Remove(mapName); + } + + IMetricsMap m; + if(_maps.TryGetValue(mapName, out m) && + IceUtilInternal.Collections.DictionaryEquals(m.getProperties(), mapProps)) + { + return false; // The map configuration didn't change, no need to re-create. + } + + try + { + _maps[mapName] = factory.create(mapPrefix, properties); + } + catch(Exception ex) + { + logger.warning("unexpected exception while creating metrics map:\n" + ex); + _maps.Remove(mapName); + } + return true; + } + + internal bool removeMap(string mapName) + { + return _maps.Remove(mapName); + } + + internal Dictionary<string, IceMX.Metrics[]> getMetrics() + { + Dictionary<string, IceMX.Metrics[]> metrics = new Dictionary<string, IceMX.Metrics[]>(); + foreach(KeyValuePair<string, IMetricsMap> e in _maps) + { + IceMX.Metrics[] m = e.Value.getMetrics(); + if(m != null) + { + metrics.Add(e.Key, m); + } + } + return metrics; + } + + internal IceMX.MetricsFailures[] getFailures(string mapName) + { + IMetricsMap m; + if(_maps.TryGetValue(mapName, out m)) + { + return m.getFailures(); + } + return null; + } + + internal IceMX.MetricsFailures getFailures(string mapName, string id) + { + IMetricsMap m; + if(_maps.TryGetValue(mapName, out m)) + { + return m.getFailures(id); + } + return null; + } + + internal ICollection<string> getMaps() + { + return _maps.Keys; + } + + internal MetricsMap<T> getMap<T>(string mapName) where T : IceMX.Metrics, new() + { + IMetricsMap m; + if(_maps.TryGetValue(mapName, out m)) + { + return (MetricsMap<T>)m; + } + return null; + } + + readonly private string _name; + readonly private Dictionary<string, IMetricsMap> _maps = new Dictionary<string, IMetricsMap>(); + }; + + public class MetricsAdminI : IceMX.MetricsAdminDisp_, Ice.PropertiesAdminUpdateCallback + { + readonly static private string[] suffixes = + { + "Disabled", + "GroupBy", + "Accept.*", + "Reject.*", + "RetainDetached", + "Map.*", + }; + + public static void validateProperties(string prefix, Ice.Properties properties) + { + Dictionary<string, string> props = properties.getPropertiesForPrefix(prefix); + List<string> unknownProps = new List<string>(); + foreach(string prop in props.Keys) + { + bool valid = false; + foreach(string suffix in suffixes) + { + if(IceUtilInternal.StringUtil.match(prop, prefix + suffix, false)) + { + valid = true; + break; + } + } + + if(!valid) + { + unknownProps.Add(prop); + } + } + + if(unknownProps.Count != 0 && properties.getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0) + { + StringBuilder message = new StringBuilder("found unknown IceMX properties for `"); + message.Append(prefix.Substring(0, prefix.Length - 1)); + message.Append("':"); + foreach(string p in unknownProps) + { + message.Append("\n "); + message.Append(p); + } + Ice.Util.getProcessLogger().warning(message.ToString()); + } + } + + class MetricsMapFactory<T> : IMetricsMapFactory where T : IceMX.Metrics, new() + { +#if COMPACT + public MetricsMapFactory(Ice.VoidAction updater) +#else + public MetricsMapFactory(System.Action updater) +#endif + { + _updater = updater; + } + + public void update() + { + Debug.Assert(_updater != null); + _updater(); + } + + public IMetricsMap create(string mapPrefix, Ice.Properties properties) + { + return new MetricsMap<T>(mapPrefix, properties, _subMaps); + } + + public void registerSubMap<S>(string subMap, System.Reflection.FieldInfo field) + where S : IceMX.Metrics, new() + { + _subMaps.Add(subMap, new SubMapFactory<S>(field)); + } + +#if COMPACT + readonly private Ice.VoidAction _updater; +#else + readonly private System.Action _updater; +#endif + readonly private Dictionary<string, ISubMapFactory> _subMaps = new Dictionary<string, ISubMapFactory>(); + }; + + public MetricsAdminI(Ice.Properties properties, Ice.Logger logger) + { + _logger = logger; + _properties = properties; + updateViews(); + } + + public void updateViews() + { + HashSet<IMetricsMapFactory> updatedMaps = new HashSet<IMetricsMapFactory>(); + lock(this) + { + string viewsPrefix = "IceMX.Metrics."; + Dictionary<string, string> viewsProps = _properties.getPropertiesForPrefix(viewsPrefix); + Dictionary<string, MetricsViewI> views = new Dictionary<string, MetricsViewI>(); + _disabledViews.Clear(); + foreach(KeyValuePair<string, string> e in viewsProps) + { + string viewName = e.Key.Substring(viewsPrefix.Length); + int dotPos = viewName.IndexOf('.'); + if(dotPos > 0) + { + viewName = viewName.Substring(0, dotPos); + } + + if(views.ContainsKey(viewName) || _disabledViews.Contains(viewName)) + { + continue; // View already configured. + } + + validateProperties(viewsPrefix + viewName + ".", _properties); + + if(_properties.getPropertyAsIntWithDefault(viewsPrefix + viewName + ".Disabled", 0) > 0) + { + _disabledViews.Add(viewName); + continue; // The view is disabled + } + + // + // Create the view or update it. + // + MetricsViewI v; + if(!_views.TryGetValue(viewName, out v)) + { + v = new MetricsViewI(viewName); + } + views[viewName] = v; + + foreach(KeyValuePair<string, IMetricsMapFactory> f in _factories) + { + if(v.addOrUpdateMap(_properties, f.Key, f.Value, _logger)) + { + updatedMaps.Add(f.Value); + } + } + } + + Dictionary<string, MetricsViewI> tmp = _views; + _views = views; + views = tmp; + + // + // Go through removed views to collect maps to update. + // + foreach(KeyValuePair<string, MetricsViewI> v in views) + { + if(!_views.ContainsKey(v.Key)) + { + foreach(string n in v.Value.getMaps()) + { + updatedMaps.Add(_factories[n]); + } + } + } + } + + // + // Call the updaters to update the maps. + // + foreach(IMetricsMapFactory f in updatedMaps) + { + f.update(); + } + } + + override public string[] getMetricsViewNames(out string[] disabledViews, Ice.Current current) + { + lock(this) + { + disabledViews = _disabledViews.ToArray(); + return new List<String>(_views.Keys).ToArray(); + } + } + + override public void enableMetricsView(string name, Ice.Current current) + { + lock(this) + { + getMetricsView(name); // Throws if unknown view. + _properties.setProperty("IceMX.Metrics." + name + ".Disabled", "0"); + } + updateViews(); + } + + override public void disableMetricsView(string name, Ice.Current current) + { + lock(this) + { + getMetricsView(name); // Throws if unknown view. + _properties.setProperty("IceMX.Metrics." + name + ".Disabled", "1"); + } + updateViews(); + } + + override public Dictionary<string, IceMX.Metrics[]> getMetricsView(string viewName, out long timestamp, + Ice.Current current) + { + lock(this) + { + MetricsViewI view = getMetricsView(viewName); + timestamp = IceInternal.Time.currentMonotonicTimeMillis(); + if(view != null) + { + return view.getMetrics(); + } + return new Dictionary<string, IceMX.Metrics[]>(); + } + } + + override public IceMX.MetricsFailures[] getMapMetricsFailures(string viewName, string mapName, Ice.Current c) + { + lock(this) + { + MetricsViewI view = getMetricsView(viewName); + if(view != null) + { + return view.getFailures(mapName); + } + return new IceMX.MetricsFailures[0]; + } + } + + override public IceMX.MetricsFailures getMetricsFailures(string viewName, string mapName, string id, + Ice.Current c) + { + lock(this) + { + MetricsViewI view = getMetricsView(viewName); + if(view != null) + { + return view.getFailures(mapName, id); + } + return new IceMX.MetricsFailures(); + } + } + +#if COMPACT + public void registerMap<T>(string map, Ice.VoidAction updater) +#else + public void registerMap<T>(string map, System.Action updater) +#endif + where T : IceMX.Metrics, new() + { + bool updated; + MetricsMapFactory<T> factory; + lock(this) + { + factory = new MetricsMapFactory<T>(updater); + _factories.Add(map, factory); + updated = addOrUpdateMap(map, factory); + } + if(updated) + { + factory.update(); + } + } + + public void registerSubMap<S>(string map, string subMap, System.Reflection.FieldInfo field) + where S : IceMX.Metrics, new() + { + bool updated; + IMetricsMapFactory factory; + lock(this) + { + if(!_factories.TryGetValue(map, out factory)) + { + return; + } + factory.registerSubMap<S>(subMap, field); + removeMap(map); + updated = addOrUpdateMap(map, factory); + } + if(updated) + { + factory.update(); + } + } + + public void unregisterMap(string mapName) + { + bool updated; + IMetricsMapFactory factory; + lock(this) + { + if(!_factories.TryGetValue(mapName, out factory)) + { + return; + } + _factories.Remove(mapName); + updated = removeMap(mapName); + } + if(updated) + { + factory.update(); + } + } + + public List<MetricsMap<T>> getMaps<T>(string mapName) where T : IceMX.Metrics, new() + { + List<MetricsMap<T>> maps = new List<MetricsMap<T>>(); + foreach(MetricsViewI v in _views.Values) + { + MetricsMap<T> map = v.getMap<T>(mapName); + if(map != null) + { + maps.Add(map); + } + } + return maps; + } + + public Ice.Logger getLogger() + { + return _logger; + } + + public void updated(Dictionary<string, string> props) + { + foreach(KeyValuePair<string, string> e in props) + { + if(e.Key.IndexOf("IceMX.") == 0) + { + // Udpate the metrics views using the new configuration. + try + { + updateViews(); + } + catch(Exception ex) + { + _logger.warning("unexpected exception while updating metrics view configuration:\n" + + ex.ToString()); + } + return; + } + } + } + + private MetricsViewI getMetricsView(string name) + { + MetricsViewI view; + if(!_views.TryGetValue(name, out view)) + { + if(!_disabledViews.Contains(name)) + { + throw new IceMX.UnknownMetricsView(); + } + return null; + } + return view; + } + + private bool addOrUpdateMap(string mapName, IMetricsMapFactory factory) + { + bool updated = false; + foreach(MetricsViewI v in _views.Values) + { + updated |= v.addOrUpdateMap(_properties, mapName, factory, _logger); + } + return updated; + } + + private bool removeMap(string mapName) + { + bool updated = false; + foreach(MetricsViewI v in _views.Values) + { + updated |= v.removeMap(mapName); + } + return updated; + } + + private Ice.Properties _properties; + readonly private Ice.Logger _logger; + readonly private Dictionary<string, IMetricsMapFactory> _factories = + new Dictionary<string, IMetricsMapFactory>(); + private Dictionary<string, MetricsViewI> _views = new Dictionary<string, MetricsViewI>(); + private List<string> _disabledViews = new List<string>(); + } +}
\ No newline at end of file diff --git a/csharp/src/Ice/MetricsObserverI.cs b/csharp/src/Ice/MetricsObserverI.cs new file mode 100644 index 00000000000..3681c185125 --- /dev/null +++ b/csharp/src/Ice/MetricsObserverI.cs @@ -0,0 +1,454 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceMX +{ + using IceInternal; + using System; + using System.Diagnostics; + using System.Collections.Generic; + + public class MetricsHelper<T> where T : Metrics + { + public class AttributeResolver + { + private abstract class Resolver + { + protected Resolver(string name) + { + _name = name; + } + + protected abstract object resolve(object obj); + + public string resolveImpl(object obj) + { + try + { + object result = resolve(obj); + if(result != null) + { + return result.ToString(); + } + return ""; + } + catch(ArgumentOutOfRangeException ex) + { + throw ex; + } + catch(Exception ex) + { +#if COMPACT + throw new ArgumentOutOfRangeException(_name, ex.ToString()); +#else + throw new ArgumentOutOfRangeException(_name, ex); +#endif + } + } + + readonly protected string _name; + }; + + class FieldResolverI : Resolver + { + internal FieldResolverI(string name, System.Reflection.FieldInfo field) : base(name) + { + Debug.Assert(field != null); + _field = field; + } + + override protected object resolve(object obj) + { + return _field.GetValue(obj); + } + + readonly private System.Reflection.FieldInfo _field; + }; + + class MethodResolverI : Resolver + { + internal MethodResolverI(string name, System.Reflection.MethodInfo method) : base(name) + { + Debug.Assert(method != null); + _method = method; + } + + override protected object resolve(object obj) + { + return _method.Invoke(obj, null); + } + + readonly private System.Reflection.MethodInfo _method; + }; + + class MemberFieldResolverI : Resolver + { + internal MemberFieldResolverI(string name, System.Reflection.MethodInfo method, + System.Reflection.FieldInfo field) + : base(name) + { + Debug.Assert(method != null && field != null); + _method = method; + _field = field; + } + + override protected object resolve(object obj) + { + object o = _method.Invoke(obj, null); + if(o != null) + { + return _field.GetValue(o); + } + throw new ArgumentOutOfRangeException(_name); + } + + readonly private System.Reflection.MethodInfo _method; + readonly private System.Reflection.FieldInfo _field; + }; + + class MemberMethodResolverI : Resolver + { + internal MemberMethodResolverI(string name, System.Reflection.MethodInfo method, + System.Reflection.MethodInfo subMeth) + : base(name) + { + Debug.Assert(method != null && subMeth != null); + _method = method; + _subMethod = subMeth; + } + + override protected object resolve(object obj) + { + object o = _method.Invoke(obj, null); + if(o != null) + { + return _subMethod.Invoke(o, null); + } + throw new ArgumentOutOfRangeException(_name); + } + + readonly private System.Reflection.MethodInfo _method; + readonly private System.Reflection.MethodInfo _subMethod; + }; + + protected AttributeResolver() + { + } + + public string resolve(MetricsHelper<T> helper, string attribute) + { + Resolver resolver; + if(!_attributes.TryGetValue(attribute, out resolver)) + { + if(attribute.Equals("none")) + { + return ""; + } + string v = helper.defaultResolve(attribute); + if(v != null) + { + return v; + } + throw new ArgumentOutOfRangeException(attribute); + } + return resolver.resolveImpl(helper); + } + + public void + add(string name, System.Reflection.MethodInfo method) + { + _attributes.Add(name, new MethodResolverI(name, method)); + } + + public void + add(string name, System.Reflection.FieldInfo field) + { + _attributes.Add(name, new FieldResolverI(name, field)); + } + + public void + add(string name, System.Reflection.MethodInfo method, System.Reflection.FieldInfo field) + { + _attributes.Add(name, new MemberFieldResolverI(name, method, field)); + } + + public void + add(string name, System.Reflection.MethodInfo method, System.Reflection.MethodInfo subMethod) + { + _attributes.Add(name, new MemberMethodResolverI(name, method, subMethod)); + } + + private Dictionary<string, Resolver> _attributes = new Dictionary<string, Resolver>(); + }; + + protected MetricsHelper(AttributeResolver attributes) + { + _attributes = attributes; + } + + public string resolve(string attribute) + { + return _attributes.resolve(this, attribute); + } + + virtual public void initMetrics(T metrics) + { + // Override in specialized helpers. + } + + virtual protected string defaultResolve(string attribute) + { + return null; + } + + private AttributeResolver _attributes; + }; + + public class Observer<T> : Stopwatch, Ice.Instrumentation.Observer where T : Metrics, new() + { + public delegate void MetricsUpdate(T m); + + virtual public void attach() + { + Start(); + } + + virtual public void detach() + { + Stop(); + long lifetime = _previousDelay + (long)(ElapsedTicks / (Frequency / 1000000.0)); + foreach(MetricsMap<T>.Entry e in _objects) + { + e.detach(lifetime); + } + } + + virtual public void failed(string exceptionName) + { + foreach(MetricsMap<T>.Entry e in _objects) + { + e.failed(exceptionName); + } + } + + public void forEach(MetricsUpdate u) + { + foreach(MetricsMap<T>.Entry e in _objects) + { + e.execute(u); + } + } + + public void init(MetricsHelper<T> helper, List<MetricsMap<T>.Entry> objects, Observer<T> previous) + { + _objects = objects; + + if(previous == null) + { + return; + } + + _previousDelay = previous._previousDelay + (long)(previous.ElapsedTicks / (Frequency / 1000000.0)); + foreach(MetricsMap<T>.Entry e in previous._objects) + { + if(!_objects.Contains(e)) + { + e.detach(_previousDelay); + } + } + } + + public ObserverImpl getObserver<S, ObserverImpl>(string mapName, MetricsHelper<S> helper) + where S : Metrics, new() + where ObserverImpl : Observer<S>, new() + { + List<MetricsMap<S>.Entry> metricsObjects = null; + foreach(MetricsMap<T>.Entry entry in _objects) + { + MetricsMap<S>.Entry e = entry.getMatching(mapName, helper); + if(e != null) + { + if(metricsObjects == null) + { + metricsObjects = new List<MetricsMap<S>.Entry>(_objects.Count); + } + metricsObjects.Add(e); + } + } + + if(metricsObjects == null) + { + return null; + } + + try + { + ObserverImpl obsv = new ObserverImpl(); + obsv.init(helper, metricsObjects, null); + return obsv; + } + catch(Exception) + { + Debug.Assert(false); + return null; + } + } + + public MetricsMap<T>.Entry getEntry(MetricsMap<T> map) + { + foreach(MetricsMap<T>.Entry e in _objects) + { + if(e.getMap() == map) + { + return e; + } + } + return null; + } + + private List<MetricsMap<T>.Entry> _objects; + private long _previousDelay = 0; + }; + + public class ObserverFactory<T, O> where T : Metrics, new() where O : Observer<T>, new() + { + public ObserverFactory(IceInternal.MetricsAdminI metrics, string name) + { + _metrics = metrics; + _name = name; + _metrics.registerMap<T>(name, this.update); + } + + public ObserverFactory(string name) + { + _name = name; + _metrics = null; + } + + public void destroy() + { + if(_metrics != null) + { + _metrics.unregisterMap(_name); + } + } + + public O getObserver(MetricsHelper<T> helper) + { + return getObserver(helper, null); + } + + public O getObserver(MetricsHelper<T> helper, object observer) + { + lock(this) + { + List<MetricsMap<T>.Entry> metricsObjects = null; + O old = null; + try + { + old = (O)observer; + } + catch(InvalidCastException) + { + } + foreach(MetricsMap<T> m in _maps) + { + MetricsMap<T>.Entry e = m.getMatching(helper, old != null ? old.getEntry(m) : null); + if(e != null) + { + if(metricsObjects == null) + { + metricsObjects = new List<MetricsMap<T>.Entry>(_maps.Count); + } + metricsObjects.Add(e); + } + } + + if(metricsObjects == null) + { + if(old != null) + { + old.detach(); + } + return null; + } + + O obsv; + try + { + obsv = new O(); + } + catch(Exception) + { + Debug.Assert(false); + return null; + } + obsv.init(helper, metricsObjects, old); + return obsv; + } + } + + public void registerSubMap<S>(string subMap, System.Reflection.FieldInfo field) + where S : Metrics, new() + { + _metrics.registerSubMap<S>(_name, subMap, field); + } + + public bool isEnabled() + { + return _enabled; + } + + public void update() + { +#if COMPACT + Ice.VoidAction updater; +#else + System.Action updater; +#endif + lock(this) + { + _maps.Clear(); + foreach(MetricsMap<T> m in _metrics.getMaps<T>(_name)) + { + _maps.Add(m); + } + _enabled = _maps.Count > 0; + updater = _updater; + } + + if(updater != null) + { + updater(); + } + } + +#if COMPACT + public void setUpdater(Ice.VoidAction updater) +#else + public void setUpdater(System.Action updater) +#endif + { + lock(this) + { + _updater = updater; + } + } + + private readonly IceInternal.MetricsAdminI _metrics; + private readonly string _name; + private List<MetricsMap<T>> _maps = new List<MetricsMap<T>>(); + private volatile bool _enabled; +#if COMPACT + private Ice.VoidAction _updater; +#else + private System.Action _updater; +#endif + }; +}
\ No newline at end of file diff --git a/csharp/src/Ice/Network.cs b/csharp/src/Ice/Network.cs new file mode 100644 index 00000000000..413679340a4 --- /dev/null +++ b/csharp/src/Ice/Network.cs @@ -0,0 +1,1452 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics; + using System.Net; +#if !COMPACT && !UNITY + using System.Net.NetworkInformation; +#endif + using System.Net.Sockets; + using System.Runtime.InteropServices; + using System.Threading; + using System.Globalization; + + public sealed class Network + { + // ProtocolSupport + public const int EnableIPv4 = 0; + public const int EnableIPv6 = 1; + public const int EnableBoth = 2; + +#if COMPACT + public static SocketError socketErrorCode(SocketException ex) + { + return (SocketError)ex.ErrorCode; + } +#else + public static SocketError socketErrorCode(SocketException ex) + { + return ex.SocketErrorCode; + } +#endif + +#if COMPACT + // + // SocketError enumeration isn't available with Silverlight + // + public enum SocketError + { + Interrupted = 10004, // A blocking Socket call was canceled. + //AccessDenied =10013, // An attempt was made to access a Socket in a way that is forbidden by its access permissions. + Fault = 10014, // An invalid pointer address was detected by the underlying socket provider. + InvalidArgument = 10022, // An invalid argument was supplied to a Socket member. + TooManyOpenSockets = 10024, // There are too many open sockets in the underlying socket provider. + WouldBlock = 10035, // An operation on a nonblocking socket cannot be completed immediately. + InProgress = 10036, // A blocking operation is in progress. + //AlreadyInProgress = 10037, // The nonblocking Socket already has an operation in progress. + //NotSocket = 10038, // A Socket operation was attempted on a non-socket. + //DestinationAddressRequired = 10039, // A required address was omitted from an operation on a Socket. + MessageSize = 10040, // The datagram is too long. + //ProtocolType = 10041, // The protocol type is incorrect for this Socket. + //ProtocolOption = 10042, // An unknown, invalid, or unsupported option or level was used with a Socket. + //ProtocolNotSupported = 10043, // The protocol is not implemented or has not been configured. + //SocketNotSupported = 10044, // The support for the specified socket type does not exist in this address family. + //OperationNotSupported = 10045, // The address family is not supported by the protocol family. + //ProtocolFamilyNotSupported = 10046, // The protocol family is not implemented or has not been configured. + //AddressFamilyNotSupported = 10047, // The address family specified is not supported. + //AddressAlreadyInUse = 10048, // Only one use of an address is normally permitted. + //AddressNotAvailable = 10049, // The selected IP address is not valid in this context. + NetworkDown = 10050, // The network is not available. + NetworkUnreachable = 10051, // No route to the remote host exists. + NetworkReset = 10052, // The application tried to set KeepAlive on a connection that has already timed out. + ConnectionAborted = 10053, // The connection was aborted by the .NET Framework or the underlying socket provider. + ConnectionReset = 10054, // The connection was reset by the remote peer. + NoBufferSpaceAvailable = 10055, // No free buffer space is available for a Socket operation. + //IsConnected = 10056, // The Socket is already connected. + NotConnected = 10057, // The application tried to send or receive data, and the Socket is not connected. + Shutdown = 10058, // A request to send or receive data was disallowed because the Socket has already been closed. + TimedOut = 10060, // The connection attempt timed out, or the connected host has failed to respond. + ConnectionRefused = 10061, // The remote host is actively refusing a connection. + //HostDown = 10064, // The operation failed because the remote host is down. + HostUnreachable = 10065, // There is no network route to the specified host. + //ProcessLimit = 10067, // Too many processes are using the underlying socket provider. + //SystemNotReady = 10091, // The network subsystem is unavailable. + //VersionNotSupported = 10092, // The version of the underlying socket provider is out of range. + //NotInitialized = 10093, // The underlying socket provider has not been initialized. + //Disconnecting = 10101, // A graceful shutdown is in progress. + //TypeNotFound = 10109, // The specified class was not found. + //HostNotFound = 11001, // No such host is known. The name is not an official host name or alias. + TryAgain = 11002, // The name of the host could not be resolved. Try again later. + //NoRecovery = 11003, // The error is unrecoverable or the requested database cannot be located. + //NoData = 11004, // The requested name or IP address was not found on the name server. + //IOPending = 997, // The application has initiated an overlapped operation that cannot be completed immediately. + OperationAborted =995 // The overlapped operation was aborted due to the closure of the Socket. + } +#endif + + public static bool interrupted(SocketException ex) + { + return socketErrorCode(ex) == SocketError.Interrupted; + } + + public static bool acceptInterrupted(SocketException ex) + { + if(interrupted(ex)) + { + return true; + } + SocketError error = socketErrorCode(ex); + return error == SocketError.ConnectionAborted || + error == SocketError.ConnectionReset || + error == SocketError.TimedOut; + } + + public static bool noBuffers(SocketException ex) + { + SocketError error = socketErrorCode(ex); + return error == SocketError.NoBufferSpaceAvailable || + error == SocketError.Fault; + } + + public static bool wouldBlock(SocketException ex) + { + return socketErrorCode(ex) == SocketError.WouldBlock; + } + + public static bool connectFailed(SocketException ex) + { + SocketError error = socketErrorCode(ex); + return error == SocketError.ConnectionRefused || + error == SocketError.TimedOut || + error == SocketError.NetworkUnreachable || + error == SocketError.HostUnreachable || + error == SocketError.ConnectionReset || + error == SocketError.Shutdown || + error == SocketError.ConnectionAborted || + error == SocketError.NetworkDown; + } + + public static bool connectInProgress(SocketException ex) + { + SocketError error = socketErrorCode(ex); + return error == SocketError.WouldBlock || + error == SocketError.InProgress; + } + + public static bool connectionLost(SocketException ex) + { + SocketError error = socketErrorCode(ex); + return error == SocketError.ConnectionReset || + error == SocketError.Shutdown || + error == SocketError.ConnectionAborted || + error == SocketError.NetworkDown || + error == SocketError.NetworkReset; + } + + public static bool connectionLost(System.IO.IOException ex) + { + // + // In some cases the IOException has an inner exception that we can pass directly + // to the other overloading of connectionLost(). + // + if(ex.InnerException != null && ex.InnerException is SocketException) + { + return connectionLost(ex.InnerException as SocketException); + } + + // + // In other cases the IOException has no inner exception. We could examine the + // exception's message, but that is fragile due to localization issues. We + // resort to extracting the value of the protected HResult member via reflection. + // + int hr = (int)ex.GetType().GetProperty("HResult", + System.Reflection.BindingFlags.Instance | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.Public).GetValue(ex, null); + + // + // This value corresponds to the following errors: + // + // "Authentication failed because the remote party has closed the transport stream" + // + if(hr == -2146232800) + { + return true; + } + + return false; + } + + public static bool connectionRefused(SocketException ex) + { + return socketErrorCode(ex) == SocketError.ConnectionRefused; + } + + public static bool notConnected(SocketException ex) + { + // BUGFIX: SocketError.InvalidArgument because shutdown() under OS X returns EINVAL + // if the server side is gone. + // BUGFIX: shutdown() under Vista might return SocketError.ConnectionReset + SocketError error = socketErrorCode(ex); + return error == SocketError.NotConnected || + error == SocketError.InvalidArgument || + error == SocketError.ConnectionReset; + } + + public static bool recvTruncated(SocketException ex) + { + return socketErrorCode(ex) == SocketError.MessageSize; + } + + public static bool operationAborted(SocketException ex) + { + return socketErrorCode(ex) == SocketError.OperationAborted; + } + + public static bool timeout(System.IO.IOException ex) + { + // + // TODO: Instead of testing for an English substring, we need to examine the inner + // exception (if there is one). + // + return ex.Message.IndexOf("period of time", StringComparison.Ordinal) >= 0; + } + + public static bool noMoreFds(System.Exception ex) + { + try + { + return ex != null && socketErrorCode((SocketException)ex) == SocketError.TooManyOpenSockets; + } + catch(InvalidCastException) + { + return false; + } + } + + public static bool isMulticast(IPEndPoint addr) + { +#if COMPACT + string ip = addr.Address.ToString().ToUpper(); +#else + string ip = addr.Address.ToString().ToUpperInvariant(); +#endif + if(addr.AddressFamily == AddressFamily.InterNetwork) + { + char[] splitChars = { '.' }; + string[] arr = ip.Split(splitChars); + try + { + int i = System.Int32.Parse(arr[0], CultureInfo.InvariantCulture); + if(i >= 223 && i <= 239) + { + return true; + } + } + catch(System.FormatException) + { + return false; + } + } + else // AddressFamily.InterNetworkV6 + { + if(ip.StartsWith("FF", StringComparison.Ordinal)) + { + return true; + } + } + return false; + } + + public static bool isIPv6Supported() + { + try + { + Socket socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); + closeSocketNoThrow(socket); + return true; + } + catch(SocketException) + { + return false; + } + } + + public static Socket createSocket(bool udp, AddressFamily family) + { + Socket socket; + + try + { + if(udp) + { + socket = new Socket(family, SocketType.Dgram, ProtocolType.Udp); + } + else + { + socket = new Socket(family, SocketType.Stream, ProtocolType.Tcp); + } + } + catch(SocketException ex) + { + throw new Ice.SocketException(ex); + } + catch(ArgumentException ex) + { + throw new Ice.SocketException(ex); + } + + + if(!udp) + { + try + { + setTcpNoDelay(socket); +#if !SILVERLIGHT + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1); +# if !__MonoCS__ + setTcpLoopbackFastPath(socket); +# endif +#endif + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + return socket; + } + + public static Socket createServerSocket(bool udp, AddressFamily family, int protocol) + { + Socket socket = createSocket(udp, family); +# if !COMPACT && !UNITY && !__MonoCS__ && !SILVERLIGHT && !DOTNET3_5 + // + // The IPv6Only enumerator was added in .NET 4. + // + if(family == AddressFamily.InterNetworkV6 && protocol != EnableIPv4) + { + try + { + int flag = protocol == EnableIPv6 ? 1 : 0; + socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, flag); + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } +#endif + return socket; + } + + public static void closeSocketNoThrow(Socket socket) + { + if(socket == null) + { + return; + } + try + { + socket.Close(); + } + catch(SocketException) + { + // Ignore + } + } + + public static void closeSocket(Socket socket) + { + if(socket == null) + { + return; + } + try + { + socket.Close(); + } + catch(SocketException ex) + { + throw new Ice.SocketException(ex); + } + } + + public static void setTcpNoDelay(Socket socket) + { + try + { +#if SILVERLIGHT + socket.NoDelay = true; +#else + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); +#endif + } + catch(System.Exception ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + +#if !SILVERLIGHT + public static void setTcpLoopbackFastPath(Socket socket) + { + const int SIO_LOOPBACK_FAST_PATH = (-1744830448); + + Byte[] OptionInValue = BitConverter.GetBytes(1); + try + { + socket.IOControl(SIO_LOOPBACK_FAST_PATH, OptionInValue, null); + } + catch(System.Exception) + { + // Expected on platforms that do not support TCP Loopback Fast Path + } + } + + public static void setBlock(Socket socket, bool block) + { + try + { + socket.Blocking = block; + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + + public static void setKeepAlive(Socket socket) + { + try + { + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1); + } + catch(System.Exception ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } +#endif + + public static void setSendBufferSize(Socket socket, int sz) + { + try + { +#if SILVERLIGHT + socket.SendBufferSize = sz; +#else + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, sz); +#endif + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + + public static int getSendBufferSize(Socket socket) + { + int sz; + try + { +#if SILVERLIGHT + sz = socket.SendBufferSize; +#else + sz = (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer); +#endif + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + return sz; + } + + public static void setRecvBufferSize(Socket socket, int sz) + { + try + { +#if SILVERLIGHT + socket.ReceiveBufferSize = sz; +#else + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, sz); +#endif + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + + public static int getRecvBufferSize(Socket socket) + { + int sz = 0; + try + { +#if SILVERLIGHT + sz = socket.ReceiveBufferSize; +#else + sz = (int)socket.GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer); +#endif + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + return sz; + } + +#if !SILVERLIGHT + public static void setReuseAddress(Socket socket, bool reuse) + { + try + { + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, reuse ? 1 : 0); + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + + public static void setMcastGroup(Socket s, IPAddress group, string iface) + { + try + { + int index = getInterfaceIndex(iface, group.AddressFamily); + if(group.AddressFamily == AddressFamily.InterNetwork) + { + MulticastOption option; +#if COMPACT + option = new MulticastOption(group); +#else + if(index == -1) + { + option = new MulticastOption(group); + } + + else + { + option = new MulticastOption(group, index); + } +#endif + s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, option); + } + else + { + IPv6MulticastOption option; + if(index == -1) + { + option = new IPv6MulticastOption(group); + } + else + { + option = new IPv6MulticastOption(group, index); + } + s.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, option); + } + } + catch(Exception ex) + { + closeSocketNoThrow(s); + throw new Ice.SocketException(ex); + } + } +#endif + + public static void setMcastTtl(Socket socket, int ttl, AddressFamily family) + { + try + { +#if SILVERLIGHT + socket.Ttl = (short)ttl; +#else + if(family == AddressFamily.InterNetwork) + { + socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, ttl); + } + else + { + socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastTimeToLive, ttl); + } +#endif + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + +#if !SILVERLIGHT + public static IPEndPoint doBind(Socket socket, EndPoint addr) + { + try + { + socket.Bind(addr); + return (IPEndPoint)socket.LocalEndPoint; + } + catch(SocketException ex) + { + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } + + public static void doListen(Socket socket, int backlog) + { + + repeatListen: + + try + { + socket.Listen(backlog); + } + catch(SocketException ex) + { + if(interrupted(ex)) + { + goto repeatListen; + } + + closeSocketNoThrow(socket); + throw new Ice.SocketException(ex); + } + } +#endif + +#if !SILVERLIGHT + public static bool doConnect(Socket fd, EndPoint addr, EndPoint sourceAddr) + { + EndPoint bindAddr = sourceAddr; + if(bindAddr == null) + { + // + // Even though we are on the client side, the call to Bind() + // is necessary to work around a .NET bug: if a socket is + // connected non-blocking, the LocalEndPoint and RemoteEndPoint + // properties are null. The call to Bind() fixes this. + // + IPAddress any = fd.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any; + bindAddr = new IPEndPoint(any, 0); + } + doBind(fd, bindAddr); + + repeatConnect: + try + { + IAsyncResult result = fd.BeginConnect(addr, null, null); + if(!result.CompletedSynchronously) + { + return false; + } + fd.EndConnect(result); + } + catch(SocketException ex) + { + if(interrupted(ex)) + { + goto repeatConnect; + } + + closeSocketNoThrow(fd); + + if(connectionRefused(ex)) + { + throw new Ice.ConnectionRefusedException(ex); + } + else + { + throw new Ice.ConnectFailedException(ex); + } + } + + // + // On Windows, we need to set the socket's blocking status again + // after the asynchronous connect. Seems like a bug in .NET. + // + setBlock(fd, fd.Blocking); + + if(AssemblyUtil.platform_ == AssemblyUtil.Platform.NonWindows) + { + // + // Prevent self connect (self connect happens on Linux when a client tries to connect to + // a server which was just deactivated if the client socket re-uses the same ephemeral + // port as the server). + // + if(addr.Equals(getLocalAddress(fd))) + { + throw new Ice.ConnectionRefusedException(); + } + } + return true; + } + + public static IAsyncResult doConnectAsync(Socket fd, EndPoint addr, EndPoint sourceAddr, AsyncCallback callback, + object state) + { + // + // NOTE: It's the caller's responsability to close the socket upon + // failure to connect. The socket isn't closed by this method. + // + EndPoint bindAddr = sourceAddr; + if(bindAddr == null) + { + // + // Even though we are on the client side, the call to Bind() + // is necessary to work around a .NET bug: if a socket is + // connected non-blocking, the LocalEndPoint and RemoteEndPoint + // properties are null. The call to Bind() fixes this. + // + IPAddress any = fd.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any; + bindAddr = new IPEndPoint(any, 0); + } + fd.Bind(bindAddr); + + repeatConnect: + try + { + return fd.BeginConnect(addr, + delegate(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + callback(result.AsyncState); + } + }, state); + } + catch(SocketException ex) + { + if(interrupted(ex)) + { + goto repeatConnect; + } + + if(connectionRefused(ex)) + { + throw new Ice.ConnectionRefusedException(ex); + } + else + { + throw new Ice.ConnectFailedException(ex); + } + } + } + + public static void doFinishConnectAsync(Socket fd, IAsyncResult result) + { + // + // NOTE: It's the caller's responsability to close the socket upon + // failure to connect. The socket isn't closed by this method. + // + try + { + fd.EndConnect(result); + } + catch(SocketException ex) + { + if(connectionRefused(ex)) + { + throw new Ice.ConnectionRefusedException(ex); + } + else + { + throw new Ice.ConnectFailedException(ex); + } + } + + // + // On Windows, we need to set the socket's blocking status again + // after the asynchronous connect. Seems like a bug in .NET. + // + setBlock(fd, fd.Blocking); + + if(AssemblyUtil.platform_ == AssemblyUtil.Platform.NonWindows) + { + // + // Prevent self connect (self connect happens on Linux when a client tries to connect to + // a server which was just deactivated if the client socket re-uses the same ephemeral + // port as the server). + // + EndPoint remoteAddr = getRemoteAddress(fd); + if(remoteAddr != null && remoteAddr.Equals(getLocalAddress(fd))) + { + throw new Ice.ConnectionRefusedException(); + } + } + } +#endif + + public static EndPoint getAddressForServer(string host, int port, int protocol, bool preferIPv6) + { + if(host.Length == 0) + { + if(protocol != EnableIPv4) + { + return new IPEndPoint(IPAddress.IPv6Any, port); + } + else + { + return new IPEndPoint(IPAddress.Any, port); + } + } + return getAddresses(host, port, protocol, Ice.EndpointSelectionType.Ordered, preferIPv6, true)[0]; + } + + public static List<EndPoint> getAddresses(string host, int port, int protocol, + Ice.EndpointSelectionType selType, bool preferIPv6, bool blocking) + { + List<EndPoint> addresses = new List<EndPoint>(); + if(host.Length == 0) + { + if(protocol != EnableIPv4) + { + addresses.Add(new IPEndPoint(IPAddress.IPv6Loopback, port)); + } + + if(protocol != EnableIPv6) + { + addresses.Add(new IPEndPoint(IPAddress.Loopback, port)); + } + return addresses; + } + + + int retry = 5; + + repeatGetHostByName: + try + { + // + // No need for lookup if host is ip address. + // + try + { + IPAddress addr = IPAddress.Parse(host); + if((addr.AddressFamily == AddressFamily.InterNetwork && protocol != EnableIPv6) || + (addr.AddressFamily == AddressFamily.InterNetworkV6 && protocol != EnableIPv4)) + { + addresses.Add(new IPEndPoint(addr, port)); + return addresses; + } + else + { + Ice.DNSException e = new Ice.DNSException(); + e.host = host; + throw e; + } + } + catch(FormatException) + { +#if !SILVERLIGHT + if(!blocking) + { + return addresses; + } +#endif + } + +#if SILVERLIGHT + if(protocol != EnableIPv6) + { + addresses.Add(new DnsEndPoint(host, port, AddressFamily.InterNetwork)); + } + if(protocol != EnableIPv4) + { + addresses.Add(new DnsEndPoint(host, port, AddressFamily.InterNetworkV6)); + } +#else +# if COMPACT + foreach(IPAddress a in Dns.GetHostEntry(host).AddressList) +# else + foreach(IPAddress a in Dns.GetHostAddresses(host)) +# endif + { + if((a.AddressFamily == AddressFamily.InterNetwork && protocol != EnableIPv6) || + (a.AddressFamily == AddressFamily.InterNetworkV6 && protocol != EnableIPv4)) + { + addresses.Add(new IPEndPoint(a, port)); + } + } +#endif + + if(selType == Ice.EndpointSelectionType.Random) + { + IceUtilInternal.Collections.Shuffle(ref addresses); + } + + if(protocol == EnableBoth) + { + if(preferIPv6) + { + IceUtilInternal.Collections.Sort(ref addresses, _preferIPv6Comparator); + } + else + { + IceUtilInternal.Collections.Sort(ref addresses, _preferIPv4Comparator); + } + } + } + catch(SocketException ex) + { + if(socketErrorCode(ex) == SocketError.TryAgain && --retry >= 0) + { + goto repeatGetHostByName; + } + Ice.DNSException e = new Ice.DNSException(ex); + e.host = host; + throw e; + } + catch(System.Exception ex) + { + Ice.DNSException e = new Ice.DNSException(ex); + e.host = host; + throw e; + } + + // + // No InterNetwork/InterNetworkV6 available. + // + if(addresses.Count == 0) + { + Ice.DNSException e = new Ice.DNSException(); + e.host = host; + throw e; + } + return addresses; + } + + public static IPAddress[] getLocalAddresses(int protocol, bool includeLoopback) + { +#if SILVERLIGHT + return new List<IPAddress>().ToArray(); +#else + List<IPAddress> addresses; + int retry = 5; + + repeatGetHostByName: + try + { + addresses = new List<IPAddress>(); +# if !COMPACT && !UNITY + NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + foreach(NetworkInterface ni in nics) + { + IPInterfaceProperties ipProps = ni.GetIPProperties(); + UnicastIPAddressInformationCollection uniColl = ipProps.UnicastAddresses; + foreach(UnicastIPAddressInformation uni in uniColl) + { + if((uni.Address.AddressFamily == AddressFamily.InterNetwork && protocol != EnableIPv6) || + (uni.Address.AddressFamily == AddressFamily.InterNetworkV6 && protocol != EnableIPv4)) + { + if(includeLoopback || !IPAddress.IsLoopback(uni.Address)) + { + addresses.Add(uni.Address); + } + } + } + } +# else +# if COMPACT + foreach(IPAddress a in Dns.GetHostEntry(Dns.GetHostName()).AddressList) +# else + foreach(IPAddress a in Dns.GetHostAddresses(Dns.GetHostName())) +# endif + { + if((a.AddressFamily == AddressFamily.InterNetwork && protocol != EnableIPv6) || + (a.AddressFamily == AddressFamily.InterNetworkV6 && protocol != EnableIPv4)) + { + if(includeLoopback || !IPAddress.IsLoopback(a)) + { + addresses.Add(a); + } + } + } +# endif + } + catch(SocketException ex) + { + if(socketErrorCode(ex) == SocketError.TryAgain && --retry >= 0) + { + goto repeatGetHostByName; + } + Ice.DNSException e = new Ice.DNSException(ex); + e.host = "0.0.0.0"; + throw e; + } + catch(System.Exception ex) + { + Ice.DNSException e = new Ice.DNSException(ex); + e.host = "0.0.0.0"; + throw e; + } + + return addresses.ToArray(); +#endif + } + + public static bool + isLinklocal(IPAddress addr) + { + if (addr.IsIPv6LinkLocal) + { + return true; + } + else if (addr.AddressFamily == AddressFamily.InterNetwork) + { + Byte[] bytes = addr.GetAddressBytes(); + return bytes[0] == 169 && bytes[1] == 254; + } + return false; + } + + public static void + setTcpBufSize(Socket socket, ProtocolInstance instance) + { + // + // By default, on Windows we use a 128KB buffer size. On Unix + // platforms, we use the system defaults. + // + int dfltBufSize = 0; + if(AssemblyUtil.platform_ == AssemblyUtil.Platform.Windows) + { + dfltBufSize = 128 * 1024; + } + + int rcvSize = instance.properties().getPropertyAsIntWithDefault("Ice.TCP.RcvSize", dfltBufSize); + int sndSize = instance.properties().getPropertyAsIntWithDefault("Ice.TCP.SndSize", dfltBufSize); + + setTcpBufSize(socket, rcvSize, sndSize, instance); + } + + public static void + setTcpBufSize(Socket socket, int rcvSize, int sndSize, ProtocolInstance instance) + { + if(rcvSize > 0) + { + // + // Try to set the buffer size. The kernel will silently adjust + // the size to an acceptable value. Then read the size back to + // get the size that was actually set. + // + setRecvBufferSize(socket, rcvSize); + int size = getRecvBufferSize(socket); + if(size < rcvSize) + { + // Warn if the size that was set is less than the requested size and + // we have not already warned. + BufSizeWarnInfo winfo = instance.getBufSizeWarn(Ice.TCPEndpointType.value); + if(!winfo.rcvWarn || rcvSize != winfo.rcvSize) + { + instance.logger().warning("TCP receive buffer size: requested size of " + rcvSize + + " adjusted to " + size); + instance.setRcvBufSizeWarn(Ice.TCPEndpointType.value, rcvSize); + } + } + } + + if(sndSize > 0) + { + // + // Try to set the buffer size. The kernel will silently adjust + // the size to an acceptable value. Then read the size back to + // get the size that was actually set. + // + setSendBufferSize(socket, sndSize); + int size = getSendBufferSize(socket); + if(size < sndSize) // Warn if the size that was set is less than the requested size. + { + // Warn if the size that was set is less than the requested size and + // we have not already warned. + BufSizeWarnInfo winfo = instance.getBufSizeWarn(Ice.TCPEndpointType.value); + if(!winfo.sndWarn || sndSize != winfo.sndSize) + { + instance.logger().warning("TCP send buffer size: requested size of " + sndSize + + " adjusted to " + size); + instance.setSndBufSizeWarn(Ice.TCPEndpointType.value, sndSize); + } + } + } + } + + public static List<string> getHostsForEndpointExpand(string host, int protocol, bool includeLoopback) + { + bool wildcard = host.Length == 0; + bool ipv4Wildcard = false; + if(!wildcard) + { + try + { + IPAddress addr = IPAddress.Parse(host); + ipv4Wildcard = addr.Equals(IPAddress.Any); + wildcard = ipv4Wildcard || addr.Equals(IPAddress.IPv6Any); + } + catch(Exception) + { + } + } + + List<string> hosts = new List<string>(); + if(wildcard) + { + IPAddress[] addrs = + getLocalAddresses(ipv4Wildcard ? Network.EnableIPv4 : protocol, includeLoopback); + foreach(IPAddress a in addrs) + { +#if COMPACT + if(!IPAddress.IsLoopback(a)) +#else + if(!isLinklocal(a)) +#endif + { + hosts.Add(a.ToString()); + } + } + } + return hosts; + } + + public static string fdToString(Socket socket, NetworkProxy proxy, EndPoint target) + { + try + { + if(socket == null) + { + return "<closed>"; + } + + EndPoint remote = getRemoteAddress(socket); + + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("local address = " + localAddrToString(getLocalAddress(socket))); + if(proxy != null) + { + if(remote == null) + { + remote = proxy.getAddress(); + } + s.Append("\n" + proxy.getName() + " proxy address = " + remoteAddrToString(remote)); + s.Append("\nremote address = " + remoteAddrToString(target)); + } + else + { + if(remote == null) + { + remote = target; + } + s.Append("\nremote address = " + remoteAddrToString(remote)); + } + return s.ToString(); + } + catch(ObjectDisposedException) + { + return "<closed>"; + } + } + + public static string fdToString(Socket socket) + { + try + { + if(socket == null) + { + return "<closed>"; + } + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("local address = " + localAddrToString(getLocalAddress(socket))); + s.Append("\nremote address = " + remoteAddrToString(getRemoteAddress(socket))); + return s.ToString(); + } + catch(ObjectDisposedException) + { + return "<closed>"; + } + } + + public static string fdLocalAddressToString(Socket socket) + { + return "local address = " + localAddrToString(getLocalAddress(socket)); + } + + public static string + addrToString(EndPoint addr) + { + return endpointAddressToString(addr) + ":" + endpointPort(addr); + } + + public static string + localAddrToString(EndPoint endpoint) + { + if(endpoint == null) + { +#if SILVERLIGHT + return "<not available>"; +#else + return "<not bound>"; +#endif + } + return endpointAddressToString(endpoint) + ":" + endpointPort(endpoint); + } + + public static string + remoteAddrToString(EndPoint endpoint) + { + if(endpoint == null) + { + return "<not connected>"; + } + return endpointAddressToString(endpoint) + ":" + endpointPort(endpoint); + } + + public static EndPoint + getLocalAddress(Socket socket) + { + // Silverlight socket doesn't exposes a local endpoint +#if !SILVERLIGHT + try + { + return socket.LocalEndPoint; + } + catch(SocketException ex) + { + throw new Ice.SocketException(ex); + } +#else + return null; +#endif + } + + public static EndPoint + getRemoteAddress(Socket socket) + { + try + { + return (EndPoint)socket.RemoteEndPoint; + } + catch(SocketException) + { + } + return null; + } + + private static int + getInterfaceIndex(string iface, AddressFamily family) + { + if(iface.Length == 0) + { + return -1; + } + + // + // The iface parameter must either be an IP address, an + // index or the name of an interface. If it's an index we + // just return it. If it's an IP addess we search for an + // interface which has this IP address. If it's a name we + // search an interface with this name. + // + try + { + return System.Int32.Parse(iface, CultureInfo.InvariantCulture); + } + catch(System.FormatException) + { + } + +#if !COMPACT && !SILVERLIGHT && !UNITY + NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + try + { + IPAddress addr = IPAddress.Parse(iface); + foreach(NetworkInterface ni in nics) + { + IPInterfaceProperties ipProps = ni.GetIPProperties(); + foreach(UnicastIPAddressInformation uni in ipProps.UnicastAddresses) + { + if(uni.Address.Equals(addr)) + { + if(addr.AddressFamily == AddressFamily.InterNetwork) + { + IPv4InterfaceProperties ipv4Props = ipProps.GetIPv4Properties(); + if(ipv4Props != null) + { + return ipv4Props.Index; + } + } + else + { + IPv6InterfaceProperties ipv6Props = ipProps.GetIPv6Properties(); + if(ipv6Props != null) + { + return ipv6Props.Index; + } + } + } + } + } + } + catch(FormatException) + { + } + + foreach(NetworkInterface ni in nics) + { + if(ni.Name == iface) + { + IPInterfaceProperties ipProps = ni.GetIPProperties(); + if(family == AddressFamily.InterNetwork) + { + IPv4InterfaceProperties ipv4Props = ipProps.GetIPv4Properties(); + if(ipv4Props != null) + { + return ipv4Props.Index; + } + } + else + { + IPv6InterfaceProperties ipv6Props = ipProps.GetIPv6Properties(); + if(ipv6Props != null) + { + return ipv6Props.Index; + } + } + } + } +#endif + return -1; + } + + public static EndPoint + getNumericAddress(string sourceAddress) + { + EndPoint addr = null; + if(!String.IsNullOrEmpty(sourceAddress)) + { + List<EndPoint> addrs = getAddresses(sourceAddress, 0, Network.EnableBoth, + Ice.EndpointSelectionType.Ordered, false, false); + if(addrs.Count != 0) + { + return addrs[0]; + } + } + return addr; + } + + public static bool + addressEquals(EndPoint addr1, EndPoint addr2) + { + if(addr1 == null) + { + if(addr2 == null) + { + return true; + } + else + { + return false; + } + } + else if(addr2 == null) + { + return false; + } + + return addr1.Equals(addr2); + } + + public static string + endpointAddressToString(EndPoint endpoint) + { + if(endpoint != null) + { +#if SILVERLIGHT + if(endpoint is DnsEndPoint) + { + DnsEndPoint dnsEndpoint = (DnsEndPoint)endpoint; + return dnsEndpoint.Host; + } +#endif + if(endpoint is IPEndPoint) + { + IPEndPoint ipEndpoint = (IPEndPoint) endpoint; + return ipEndpoint.Address.ToString(); + } + } + return ""; + } + + public static int + endpointPort(EndPoint endpoint) + { + if(endpoint != null) + { +#if SILVERLIGHT + if(endpoint is DnsEndPoint) + { + DnsEndPoint dnsEndpoint = (DnsEndPoint)endpoint; + return dnsEndpoint.Port; + } +#endif + if(endpoint is IPEndPoint) + { + IPEndPoint ipEndpoint = (IPEndPoint) endpoint; + return ipEndpoint.Port; + } + } + return -1; + } + + private class EndPointComparator : IComparer<EndPoint> + { + public EndPointComparator(bool ipv6) + { + _ipv6 = ipv6; + } + + public int Compare(EndPoint lhs, EndPoint rhs) + { + if(lhs.AddressFamily == AddressFamily.InterNetwork && + rhs.AddressFamily == AddressFamily.InterNetworkV6) + { + return _ipv6 ? 1 : -1; + } + else if(lhs.AddressFamily == AddressFamily.InterNetworkV6 && + rhs.AddressFamily == AddressFamily.InterNetwork) + { + return _ipv6 ? -1 : 1; + } + else + { + return 0; + } + } + + private bool _ipv6; + } + + private readonly static EndPointComparator _preferIPv4Comparator = new EndPointComparator(false); + private readonly static EndPointComparator _preferIPv6Comparator = new EndPointComparator(true); + } +} diff --git a/csharp/src/Ice/NetworkProxy.cs b/csharp/src/Ice/NetworkProxy.cs new file mode 100644 index 00000000000..6e4a3ed9ffa --- /dev/null +++ b/csharp/src/Ice/NetworkProxy.cs @@ -0,0 +1,311 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Net; + using System.Net.Sockets; + using System.Diagnostics; + using System.Text; + + public interface NetworkProxy + { + // + // Write the connection request on the connection established + // with the network proxy server. This is called right after + // the connection establishment succeeds. + // + void beginWrite(EndPoint endpoint, Buffer buf); + int endWrite(Buffer buf); + + // + // Once the connection request has been sent, this is called + // to prepare and read the response from the proxy server. + // + void beginRead(Buffer buf); + int endRead(Buffer buf); + + // + // This is called when the response from the proxy has been + // read. The proxy should copy the extra read data (if any) in the + // given byte vector. + // + void finish(Buffer readBuffer, Buffer writeBuffer); + +#if !SILVERLIGHT + // + // If the proxy host needs to be resolved, this should return + // a new NetworkProxy containing the IP address of the proxy. + // This is called from the endpoint host resolver thread, so + // it's safe if this this method blocks. + // + NetworkProxy resolveHost(int protocolSupport); +#endif + // + // Returns the IP address of the network proxy. This method + // must not block. It's only called on a network proxy object + // returned by resolveHost(). + // + EndPoint getAddress(); + + // + // Returns the name of the proxy, used for tracing purposes. + // + string getName(); + + // + // Returns the protocols supported by the proxy. + // + int getProtocolSupport(); + } + + public sealed class SOCKSNetworkProxy : NetworkProxy + { + public SOCKSNetworkProxy(string host, int port) + { +#if SILVERLIGHT + _address = new DnsEndPoint(host, port, AddressFamily.InterNetwork); +#else + _host = host; + _port = port; +#endif + } + + private SOCKSNetworkProxy(EndPoint address) + { + _address = address; + } + + public void beginWrite(EndPoint endpoint, Buffer buf) + { + if(!(endpoint is IPEndPoint)) + { + throw new Ice.FeatureNotSupportedException("SOCKS4 does not support domain names"); + } + else if(endpoint.AddressFamily != AddressFamily.InterNetwork) + { + throw new Ice.FeatureNotSupportedException("SOCKS4 only supports IPv4 addresses"); + } + + // + // SOCKS connect request + // + IPEndPoint addr = (IPEndPoint)endpoint; + buf.resize(9, false); + ByteBuffer.ByteOrder order = buf.b.order(); + buf.b.order(ByteBuffer.ByteOrder.BIG_ENDIAN); // Network byte order. + buf.b.position(0); + buf.b.put(0x04); // SOCKS version 4. + buf.b.put(0x01); // Command, establish a TCP/IP stream connection + buf.b.putShort((short)addr.Port); // Port + buf.b.put(addr.Address.GetAddressBytes()); // IPv4 address + buf.b.put(0x00); // User ID. + buf.b.position(0); + buf.b.limit(buf.size()); + buf.b.order(order); + } + + public int endWrite(Buffer buf) + { + // Once the request is sent, read the response + return buf.b.hasRemaining() ? SocketOperation.Write : SocketOperation.Read; + } + + public void beginRead(Buffer buf) + { + // + // Read the SOCKS4 response whose size is 8 bytes. + // + buf.resize(8, true); + buf.b.position(0); + } + + public int endRead(Buffer buf) + { + // We're done once we read the response + return buf.b.hasRemaining() ? SocketOperation.Read : SocketOperation.None; + } + + public void finish(Buffer readBuffer, Buffer writeBuffer) + { + readBuffer.b.position(0); + byte b1 = readBuffer.b.get(); + byte b2 = readBuffer.b.get(); + if(b1 != 0x00 || b2 != 0x5a) + { + throw new Ice.ConnectFailedException(); + } + } + +#if !SILVERLIGHT + public NetworkProxy resolveHost(int protocolSupport) + { + Debug.Assert(_host != null); + return new SOCKSNetworkProxy(Network.getAddresses(_host, + _port, + protocolSupport, + Ice.EndpointSelectionType.Random, + false, + true)[0]); + } +#endif + + public EndPoint getAddress() + { + Debug.Assert(_address != null); // Host must be resolved. + return _address; + } + + public string getName() + { + return "SOCKS"; + } + + public int getProtocolSupport() + { + return Network.EnableIPv4; + } + +#if !SILVERLIGHT + private readonly string _host; + private readonly int _port; +#endif + private readonly EndPoint _address; + } + + public sealed class HTTPNetworkProxy : NetworkProxy + { + public HTTPNetworkProxy(string host, int port) + { +#if SILVERLIGHT + _address = new DnsEndPoint(host, port, AddressFamily.InterNetwork); + _protocolSupport = Network.EnableIPv4; +#else + _host = host; + _port = port; + _protocolSupport = Network.EnableBoth; +#endif + } + + private HTTPNetworkProxy(EndPoint address, int protocolSupport) + { + _address = address; + _protocolSupport = protocolSupport; + } + + public void beginWrite(EndPoint endpoint, Buffer buf) + { + string addr = Network.addrToString(endpoint); + StringBuilder str = new StringBuilder(); + str.Append("CONNECT "); + str.Append(addr); + str.Append(" HTTP/1.1\r\nHost: "); + str.Append(addr); + str.Append("\r\n\r\n"); + +#if SILVERLIGHT + byte[] b = System.Text.Encoding.UTF8.GetBytes(str.ToString()); +#else + byte[] b = System.Text.Encoding.ASCII.GetBytes(str.ToString()); +#endif + + // + // HTTP connect request + // + buf.resize(b.Length, false); + buf.b.position(0); + buf.b.put(b); + buf.b.position(0); + buf.b.limit(buf.size()); + } + + public int endWrite(Buffer buf) + { + // Once the request is sent, read the response + return buf.b.hasRemaining() ? SocketOperation.Write : SocketOperation.Read; + } + + public void beginRead(Buffer buf) + { + // + // Read the HTTP response + // + buf.resize(7, true); // Enough space for reading at least HTTP1.1 + buf.b.position(0); + } + + public int endRead(Buffer buf) + { + // + // Check if we received the full HTTP response, if not, continue + // reading otherwise we're done. + // + int end = new HttpParser().isCompleteMessage(buf.b, 0, buf.b.position()); + if(end < 0 && !buf.b.hasRemaining()) + { + // + // Read one more byte, we can't easily read bytes in advance + // since the transport implenentation might be be able to read + // the data from the memory instead of the socket. + // + buf.resize(buf.size() + 1, true); + return SocketOperation.Read; + } + return SocketOperation.None; + } + + public void finish(Buffer readBuffer, Buffer writeBuffer) + { + HttpParser parser = new HttpParser(); + parser.parse(readBuffer.b, 0, readBuffer.b.position()); + if(parser.status() != 200) + { + throw new Ice.ConnectFailedException(); + } + } + +#if !SILVERLIGHT + public NetworkProxy resolveHost(int protocolSupport) + { + Debug.Assert(_host != null); + return new HTTPNetworkProxy(Network.getAddresses(_host, + _port, + protocolSupport, + Ice.EndpointSelectionType.Random, + false, + true)[0], + protocolSupport); + } +#endif + + public EndPoint getAddress() + { + Debug.Assert(_address != null); // Host must be resolved. + return _address; + } + + public string getName() + { + return "HTTP"; + } + + public int getProtocolSupport() + { + return _protocolSupport; + } + +#if !SILVERLIGHT + private readonly string _host; + private readonly int _port; +#endif + private readonly EndPoint _address; + private readonly int _protocolSupport; + } +} diff --git a/csharp/src/Ice/Object.cs b/csharp/src/Ice/Object.cs new file mode 100644 index 00000000000..c2089e81f14 --- /dev/null +++ b/csharp/src/Ice/Object.cs @@ -0,0 +1,548 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Diagnostics; + +namespace Ice +{ + /// <summary> + /// Indicates the status of operation dispatch. + /// </summary> + public enum DispatchStatus + { + /// <summary> + /// Indicates that an operation was dispatched synchronously and successfully. + /// </summary> + DispatchOK, + + /// <summary> + /// Indicates that an operation was dispatched synchronously and raised a user exception. + /// </summary> + DispatchUserException, + + /// <summary> + /// Indicates that an operation was dispatched asynchronously. + /// </summary> + DispatchAsync + } + + public interface DispatchInterceptorAsyncCallback + { + bool response(bool ok); + bool exception(System.Exception ex); + } + + /// <summary> + /// Interface for incoming requests. + /// </summary> + public interface Request + { + /// <summary> + /// Returns the {@link Current} object for this the request. + /// </summary> + /// <returns>The Current object for this request.</returns> + Current getCurrent(); + } + + /// <summary> + /// the base interface for servants. + /// </summary> + public interface Object : System.ICloneable + { + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// + /// <param name="s">The type ID of the Slice interface to test against.</param> + /// <returns>True if this object has the interface + /// specified by s or derives from the interface specified by s.</returns> + bool ice_isA(string s); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// + /// <param name="s">The type ID of the Slice interface to test against.</param> + /// <param name="current">The Current object for the invocation.</param> + /// <returns>True if this object has the interface + /// specified by s or derives from the interface specified by s.</returns> + bool ice_isA(string s, Current current); + + /// <summary> + /// Tests whether this object can be reached. + /// </summary> + void ice_ping(); + + /// <summary> + /// Tests whether this object can be reached. + /// </summary> + /// <param name="current">The Current object for the invocation.</param> + void ice_ping(Current current); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by this object. + /// </summary> + /// <returns>The Slice type IDs of the interfaces supported by this object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + string[] ice_ids(); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by this object. + /// </summary> + /// <param name="current">The Current object for the invocation.</param> + /// <returns>The Slice type IDs of the interfaces supported by this object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + string[] ice_ids(Current current); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by this object. + /// </summary> + /// <returns>The Slice type ID of the most-derived interface.</returns> + string ice_id(); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by this object. + /// </summary> + /// <param name="current">The Current object for the invocation.</param> + /// <returns>The Slice type ID of the most-derived interface.</returns> + string ice_id(Current current); + + /// <summary> + /// The Ice run time invokes this method prior to marshaling an object's data members. This allows a subclass + /// to override this method in order to validate its data members. + /// </summary> + void ice_preMarshal(); + + /// <summary> + /// This Ice run time invokes this method vafter unmarshaling an object's data members. This allows a + /// subclass to override this method in order to perform additional initialization. + /// </summary> + void ice_postUnmarshal(); + + /// <summary> + /// Dispatches an invocation to a servant. This method is used by dispatch interceptors to forward an invocation + /// to a servant (or to another interceptor). + /// </summary> + /// <param name="request">The details of the invocation.</param> + /// <param name="cb">The callback object for asynchchronous dispatch. For synchronous dispatch, + /// the callback object must be null.</param> + /// <returns>The dispatch status for the operation.</returns> + DispatchStatus ice_dispatch(Request request, DispatchInterceptorAsyncCallback cb); + + /// <summary> + /// Dispatches an invocation to a servant. This method is used by dispatch interceptors to forward an invocation + /// to a servant (or to another interceptor). + /// </summary> + /// <param name="request">The details of the invocation.</param> + /// <returns>The dispatch status for the operation.</returns> + DispatchStatus ice_dispatch(Request request); + + DispatchStatus dispatch__(IceInternal.Incoming inc, Current current); + + void write__(IceInternal.BasicStream os__); + void read__(IceInternal.BasicStream is__); + + void write__(OutputStream outS__); + void read__(InputStream inS__); + } + + /// <summary> + /// Base class for all Slice classes. + /// </summary> + public abstract class ObjectImpl : Object + { + /// <summary> + /// Instantiates an Ice object. + /// </summary> + public ObjectImpl() + { + } + + /// <summary> + /// Returns a copy of the object. The cloned object contains field-for-field copies + /// of the state. + /// </summary> + /// <returns>The cloned object.</returns> + public object Clone() + { + return MemberwiseClone(); + } + + public static readonly string[] ids__ = + { + "::Ice::Object" + }; + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="s">The type ID of the Slice interface to test against.</param> + /// <returns>The return value is true if s is ::Ice::Object.</returns> + public virtual bool ice_isA(string s) + { + return s.Equals(ids__[0]); + } + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="s">The type ID of the Slice interface to test against.</param> + /// <param name="current">The Current object for the invocation.</param> + /// <returns>The return value is true if s is ::Ice::Object.</returns> + public virtual bool ice_isA(string s, Current current) + { + return s.Equals(ids__[0]); + } + + public static DispatchStatus ice_isA___(Ice.Object __obj, IceInternal.Incoming inS__, Current __current) + { + IceInternal.BasicStream is__ = inS__.startReadParams(); + string __id = is__.readString(); + inS__.endReadParams(); + bool __ret = __obj.ice_isA(__id, __current); + IceInternal.BasicStream os__ = inS__.startWriteParams__(FormatType.DefaultFormat); + os__.writeBool(__ret); + inS__.endWriteParams__(true); + return DispatchStatus.DispatchOK; + } + + /// <summary> + /// Tests whether this object can be reached. + /// </summary> + public virtual void ice_ping() + { + // Nothing to do. + } + + /// <summary> + /// Tests whether this object can be reached. + /// <param name="current">The Current object for the invocation.</param> + /// </summary> + public virtual void ice_ping(Current current) + { + // Nothing to do. + } + + public static DispatchStatus ice_ping___(Ice.Object __obj, IceInternal.Incoming inS__, Current __current) + { + inS__.readEmptyParams(); + __obj.ice_ping(__current); + inS__.writeEmptyParams__(); + return DispatchStatus.DispatchOK; + } + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by this object. + /// </summary> + /// <returns>An array whose only element is ::Ice::Object.</returns> + public virtual string[] ice_ids() + { + return ids__; + } + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by this object. + /// </summary> + /// <param name="current">The Current object for the invocation.</param> + /// <returns>An array whose only element is ::Ice::Object.</returns> + public virtual string[] ice_ids(Current current) + { + return ids__; + } + + public static DispatchStatus ice_ids___(Ice.Object __obj, IceInternal.Incoming inS__, Current __current) + { + inS__.readEmptyParams(); + string[] ret__ = __obj.ice_ids(__current); + IceInternal.BasicStream os__ = inS__.startWriteParams__(FormatType.DefaultFormat); + os__.writeStringSeq(ret__); + inS__.endWriteParams__(true); + return DispatchStatus.DispatchOK; + } + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by this object. + /// </summary> + /// <returns>The return value is always ::Ice::Object.</returns> + public virtual string ice_id() + { + return ids__[0]; + } + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by this object. + /// </summary> + /// <param name="current">The Current object for the invocation.</param> + /// <returns>The return value is always ::Ice::Object.</returns> + public virtual string ice_id(Current current) + { + return ids__[0]; + } + + public static DispatchStatus ice_id___(Ice.Object __obj, IceInternal.Incoming inS__, Current __current) + { + inS__.readEmptyParams(); + string __ret = __obj.ice_id(__current); + IceInternal.BasicStream os__ = inS__.startWriteParams__(FormatType.DefaultFormat); + os__.writeString(__ret); + inS__.endWriteParams__(true); + return DispatchStatus.DispatchOK; + } + + /// <summary> + /// Returns the Slice type ID of the interface supported by this object. + /// </summary> + /// <returns>The return value is always ::Ice::Object.</returns> + public static string ice_staticId() + { + return ids__[0]; + } + + /// <summary> + /// The Ice run time invokes this method prior to marshaling an object's data members. This allows a subclass + /// to override this method in order to validate its data members. + /// </summary> + public virtual void ice_preMarshal() + { + } + + /// <summary> + /// This Ice run time invokes this method vafter unmarshaling an object's data members. This allows a + /// subclass to override this method in order to perform additional initialization. + /// </summary> + public virtual void ice_postUnmarshal() + { + } + + private static readonly string[] all__ = new string[] + { + "ice_id", "ice_ids", "ice_isA", "ice_ping" + }; + + /// <summary> + /// Dispatches an invocation to a servant. This method is used by dispatch interceptors to forward an invocation + /// to a servant (or to another interceptor). + /// </summary> + /// <param name="request">The details of the invocation.</param> + /// <param name="cb">The callback object for asynchchronous dispatch. For synchronous dispatch, the + /// callback object must be null.</param> + /// <returns>The dispatch status for the operation.</returns> + public virtual DispatchStatus ice_dispatch(Request request, DispatchInterceptorAsyncCallback cb) + { + IceInternal.Incoming inc = (IceInternal.Incoming)request; + if(cb != null) + { + inc.push(cb); + } + try + { + inc.startOver(); // may raise ResponseSentException + return dispatch__(inc, inc.getCurrent()); + } + finally + { + if(cb != null) + { + inc.pop(); + } + } + } + + /// <summary> + /// Dispatches an invocation to a servant. This method is used by dispatch interceptors to forward an invocation + /// to a servant (or to another interceptor). + /// </summary> + /// <param name="request">The details of the invocation.</param> + /// <returns>The dispatch status for the operation.</returns> + public virtual DispatchStatus ice_dispatch(Request request) + { + return ice_dispatch(request, null); + } + + public virtual DispatchStatus dispatch__(IceInternal.Incoming inc, Current current) + { + int pos = System.Array.BinarySearch(all__, current.operation); + if(pos < 0) + { + throw new Ice.OperationNotExistException(current.id, current.facet, current.operation); + } + + switch(pos) + { + case 0: + { + return ice_id___(this, inc, current); + } + case 1: + { + return ice_ids___(this, inc, current); + } + case 2: + { + return ice_isA___(this, inc, current); + } + case 3: + { + return ice_ping___(this, inc, current); + } + } + + Debug.Assert(false); + throw new Ice.OperationNotExistException(current.id, current.facet, current.operation); + } + + public virtual void write__(IceInternal.BasicStream os__) + { + os__.startWriteObject(null); + writeImpl__(os__); + os__.endWriteObject(); + } + + public virtual void read__(IceInternal.BasicStream is__) + { + is__.startReadObject(); + readImpl__(is__); + is__.endReadObject(false); + } + + public virtual void write__(OutputStream os__) + { + os__.startObject(null); + writeImpl__(os__); + os__.endObject(); + } + + public virtual void read__(InputStream is__) + { + is__.startObject(); + readImpl__(is__); + is__.endObject(false); + } + + protected virtual void writeImpl__(IceInternal.BasicStream os__) + { + } + + protected virtual void readImpl__(IceInternal.BasicStream is__) + { + } + + protected virtual void writeImpl__(OutputStream os__) + { + throw new MarshalException("class was not generated with stream support"); + } + + protected virtual void readImpl__(InputStream is__) + { + throw new MarshalException("class was not generated with stream support"); + } + + private static string operationModeToString(OperationMode mode) + { + if(mode == Ice.OperationMode.Normal) + { + return "::Ice::Normal"; + } + if(mode == Ice.OperationMode.Nonmutating) + { + return "::Ice::Nonmutating"; + } + + if(mode == Ice.OperationMode.Idempotent) + { + return "::Ice::Idempotent"; + } + + return "???"; + } + + public static void checkMode__(OperationMode expected, OperationMode received) + { + if(expected != received) + { + if(expected == OperationMode.Idempotent && received == OperationMode.Nonmutating) + { + // + // Fine: typically an old client still using the + // deprecated nonmutating keyword + // + } + else + { + Ice.MarshalException ex = new Ice.MarshalException(); + ex.reason = "unexpected operation mode. expected = " + operationModeToString(expected) + + " received = " + operationModeToString(received); + throw ex; + } + } + } + + public static Ice.Current defaultCurrent = new Ice.Current(); + } + + /// <summary> + /// Base class for dynamic dispatch servants. A server application + /// derives a concrete servant class from Blobject that + /// implements the Blobject.ice_invoke method. + /// </summary> + public abstract class Blobject : Ice.ObjectImpl + { + /// <summary> + /// Dispatch an incoming request. + /// </summary> + /// <param name="inParams">The encoded in-parameters for the operation.</param> + /// <param name="outParams">The encoded out-paramaters and return value + /// for the operation. The return value follows any out-parameters.</param> + /// <param name="current">The Current object to pass to the operation.</param> + /// <returns>If the operation completed successfully, the return value + /// is true. If the operation raises a user exception, + /// the return value is false; in this case, outParams + /// must contain the encoded user exception. If the operation raises an + /// Ice run-time exception, it must throw it directly.</returns> + public abstract bool ice_invoke(byte[] inParams, out byte[] outParams, Current current); + + public override DispatchStatus dispatch__(IceInternal.Incoming inS__, Current current) + { + byte[] inEncaps = inS__.readParamEncaps(); + byte[] outEncaps; + bool ok = ice_invoke(inEncaps, out outEncaps, current); + inS__.writeParamEncaps__(outEncaps, ok); + if(ok) + { + return DispatchStatus.DispatchOK; + } + else + { + return DispatchStatus.DispatchUserException; + } + } + } + + public abstract class BlobjectAsync : Ice.ObjectImpl + { + public abstract void ice_invoke_async(AMD_Object_ice_invoke cb, byte[] inEncaps, Current current); + + public override DispatchStatus dispatch__(IceInternal.Incoming inS__, Current current) + { + byte[] inEncaps = inS__.readParamEncaps(); + AMD_Object_ice_invoke cb = new _AMD_Object_ice_invoke(inS__); + try + { + ice_invoke_async(cb, inEncaps, current); + } + catch(System.Exception ex) + { + cb.ice_exception(ex); + } + return DispatchStatus.DispatchAsync; + } + } +} diff --git a/csharp/src/Ice/ObjectAdapterFactory.cs b/csharp/src/Ice/ObjectAdapterFactory.cs new file mode 100644 index 00000000000..f9cfe278209 --- /dev/null +++ b/csharp/src/Ice/ObjectAdapterFactory.cs @@ -0,0 +1,246 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + + public sealed class ObjectAdapterFactory + { + public void shutdown() + { + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + // + // Ignore shutdown requests if the object adapter factory has + // already been shut down. + // + if(instance_ == null) + { + return; + } + + adapters = new List<Ice.ObjectAdapterI>(_adapters); + + instance_ = null; + _communicator = null; + + System.Threading.Monitor.PulseAll(this); + } + + // + // Deactivate outside the thread synchronization, to avoid + // deadlocks. + // + foreach(Ice.ObjectAdapter adapter in adapters) + { + adapter.deactivate(); + } + } + + public void waitForShutdown() + { + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + // + // First we wait for the shutdown of the factory itself. + // + while(instance_ != null) + { + System.Threading.Monitor.Wait(this); + } + + adapters = new List<Ice.ObjectAdapterI>(_adapters); + } + + // + // Now we wait for deactivation of each object adapter. + // + foreach(Ice.ObjectAdapter adapter in adapters) + { + adapter.waitForDeactivate(); + } + } + + public bool isShutdown() + { + lock(this) + { + return instance_ == null; + } + } + + public void destroy() + { + // + // First wait for shutdown to finish. + // + waitForShutdown(); + + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + adapters = new List<Ice.ObjectAdapterI>(_adapters); + } + + foreach(Ice.ObjectAdapter adapter in adapters) + { + adapter.destroy(); + } + + lock(this) + { + _adapters.Clear(); + } + } + + public void + updateConnectionObservers() + { + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + adapters = new List<Ice.ObjectAdapterI>(_adapters); + } + + foreach(Ice.ObjectAdapterI adapter in adapters) + { + adapter.updateConnectionObservers(); + } + } + + public void + updateThreadObservers() + { + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + adapters = new List<Ice.ObjectAdapterI>(_adapters); + } + + foreach(Ice.ObjectAdapterI adapter in adapters) + { + adapter.updateThreadObservers(); + } + } + + public Ice.ObjectAdapter createObjectAdapter(string name, Ice.RouterPrx router) + { + lock(this) + { + if(instance_ == null) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Ice.ObjectAdapterI adapter = null; + if(name.Length == 0) + { + string uuid = System.Guid.NewGuid().ToString(); + adapter = new Ice.ObjectAdapterI(instance_, _communicator, this, uuid, null, true); + } + else + { + if(_adapterNamesInUse.Contains(name)) + { + Ice.AlreadyRegisteredException ex = new Ice.AlreadyRegisteredException(); + ex.kindOfObject = "object adapter"; + ex.id = name; + throw ex; + } + adapter = new Ice.ObjectAdapterI(instance_, _communicator, this, name, router, false); + _adapterNamesInUse.Add(name); + } + _adapters.Add(adapter); + return adapter; + } + } + + public Ice.ObjectAdapter findObjectAdapter(Ice.ObjectPrx proxy) + { + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + if(instance_ == null) + { + return null; + } + + adapters = new List<Ice.ObjectAdapterI>(_adapters); + } + + foreach(Ice.ObjectAdapterI adapter in adapters) + { + try + { + if(adapter.isLocal(proxy)) + { + return adapter; + } + } + catch(Ice.ObjectAdapterDeactivatedException) + { + // Ignore. + } + } + + return null; + } + + public void removeObjectAdapter(Ice.ObjectAdapterI adapter) + { + lock(this) + { + if(instance_ == null) + { + return; + } + + _adapters.Remove(adapter); + _adapterNamesInUse.Remove(adapter.getName()); + } + } + + public void flushAsyncBatchRequests(CommunicatorFlushBatch outAsync) + { + List<Ice.ObjectAdapterI> adapters; + lock(this) + { + adapters = new List<Ice.ObjectAdapterI>(_adapters); + } + + foreach(Ice.ObjectAdapterI adapter in adapters) + { + adapter.flushAsyncBatchRequests(outAsync); + } + } + + // + // Only for use by Instance. + // + internal ObjectAdapterFactory(Instance instance, Ice.Communicator communicator) + { + instance_ = instance; + _communicator = communicator; + _adapterNamesInUse = new HashSet<string>(); + _adapters = new List<Ice.ObjectAdapterI>(); + } + + private Instance instance_; + private Ice.Communicator _communicator; + private HashSet<string> _adapterNamesInUse; + private List<Ice.ObjectAdapterI> _adapters; + } + +} diff --git a/csharp/src/Ice/ObjectAdapterI.cs b/csharp/src/Ice/ObjectAdapterI.cs new file mode 100644 index 00000000000..c6794a1a4db --- /dev/null +++ b/csharp/src/Ice/ObjectAdapterI.cs @@ -0,0 +1,1579 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Text; + + using IceInternal; + + public sealed class ObjectAdapterI : ObjectAdapter + { + public string getName() + { + // + // No mutex lock necessary, _name is immutable. + // + return _noConfig ? "" : _name; + } + + public Communicator getCommunicator() + { + return _communicator; + } + + public void activate() + { + LocatorInfo locatorInfo = null; + bool registerProcess = false; + bool printAdapterReady = false; + + lock(this) + { + checkForDeactivation(); + + // + // If we've previously been initialized we just need to activate the + // incoming connection factories and we're done. + // + if(state_ != StateUninitialized) + { + foreach(IncomingConnectionFactory icf in _incomingConnectionFactories) + { + icf.activate(); + } + return; + } + + // + // One off initializations of the adapter: update the + // locator registry and print the "adapter ready" + // message. We set set state to StateActivating to prevent + // deactivation from other threads while these one off + // initializations are done. + // + state_ = StateActivating; + + locatorInfo = _locatorInfo; + if(!_noConfig) + { + Properties properties = instance_.initializationData().properties; + registerProcess = properties.getPropertyAsInt(_name + ".RegisterProcess") > 0; + printAdapterReady = properties.getPropertyAsInt("Ice.PrintAdapterReady") > 0; + } + } + + try + { + Ice.Identity dummy = new Ice.Identity(); + dummy.name = "dummy"; + updateLocatorRegistry(locatorInfo, createDirectProxy(dummy), registerProcess); + } + catch(Ice.LocalException) + { + // + // If we couldn't update the locator registry, we let the + // exception go through and don't activate the adapter to + // allow to user code to retry activating the adapter + // later. + // + lock(this) + { + state_ = StateUninitialized; + System.Threading.Monitor.PulseAll(this); + } + throw; + } + + if(printAdapterReady) + { + System.Console.Out.WriteLine(_name + " ready"); + } + + lock(this) + { + Debug.Assert(state_ == StateActivating); + + foreach(IncomingConnectionFactory icf in _incomingConnectionFactories) + { + icf.activate(); + } + + state_ = StateActive; + System.Threading.Monitor.PulseAll(this); + } + } + + public void hold() + { + lock(this) + { + checkForDeactivation(); + state_ = StateHeld; + foreach(IncomingConnectionFactory factory in _incomingConnectionFactories) + { + factory.hold(); + } + } + } + + public void waitForHold() + { + List<IncomingConnectionFactory> incomingConnectionFactories; + lock(this) + { + checkForDeactivation(); + + incomingConnectionFactories = new List<IncomingConnectionFactory>(_incomingConnectionFactories); + } + + foreach(IncomingConnectionFactory factory in incomingConnectionFactories) + { + factory.waitUntilHolding(); + } + } + + public void deactivate() + { + lock(this) + { + // + // + // Wait for activation to complete. This is necessary to not + // get out of order locator updates. + // + while(state_ == StateActivating || state_ == StateDeactivating) + { + System.Threading.Monitor.Wait(this); + } + if(state_ > StateDeactivating) + { + return; + } + state_ = StateDeactivating; + } + + // + // NOTE: the router/locator infos and incoming connection + // facatory list are immutable at this point. + // + + if(_routerInfo != null) + { + // + // Remove entry from the router manager. + // + instance_.routerManager().erase(_routerInfo.getRouter()); + + // + // Clear this object adapter with the router. + // + _routerInfo.setAdapter(null); + } + + try + { + updateLocatorRegistry(_locatorInfo, null, false); + } + catch(Ice.LocalException) + { + // + // We can't throw exceptions in deactivate so we ignore + // failures to update the locator registry. + // + } + + // + // Must be called outside the thread synchronization, because + // Connection::destroy() might block when sending a CloseConnection + // message. + // + foreach(IncomingConnectionFactory factory in _incomingConnectionFactories) + { + factory.destroy(); + } + + // + // Must be called outside the thread synchronization, because + // changing the object adapter might block if there are still + // requests being dispatched. + // + instance_.outgoingConnectionFactory().removeAdapter(this); + + lock(this) + { + Debug.Assert(state_ == StateDeactivating); + state_ = StateDeactivated; + System.Threading.Monitor.PulseAll(this); + } + } + + public void waitForDeactivate() + { + IncomingConnectionFactory[] incomingConnectionFactories = null; + lock(this) + { + // + // Wait for deactivation of the adapter itself, and + // for the return of all direct method calls using this + // adapter. + // + while((state_ < StateDeactivated) || _directCount > 0) + { + System.Threading.Monitor.Wait(this); + } + if(state_ > StateDeactivated) + { + return; + } + + incomingConnectionFactories = _incomingConnectionFactories.ToArray(); + } + + // + // Now we wait for until all incoming connection factories are + // finished. + // + foreach(IncomingConnectionFactory factory in incomingConnectionFactories) + { + factory.waitUntilFinished(); + } + } + + public bool isDeactivated() + { + lock(this) + { + return state_ >= StateDeactivated; + } + } + + public void destroy() + { + // + // Deactivate and wait for completion. + // + deactivate(); + waitForDeactivate(); + + lock(this) + { + // + // Only a single thread is allowed to destroy the object + // adapter. Other threads wait for the destruction to be + // completed. + // + while(state_ == StateDestroying) + { + System.Threading.Monitor.Wait(this); + } + if(state_ == StateDestroyed) + { + return; + } + state_ = StateDestroying; + } + + // + // Now it's also time to clean up our servants and servant + // locators. + // + _servantManager.destroy(); + + // + // Destroy the thread pool. + // + if(_threadPool != null) + { + _threadPool.destroy(); + _threadPool.joinWithAllThreads(); + } + + if(_objectAdapterFactory != null) + { + _objectAdapterFactory.removeObjectAdapter(this); + } + + lock(this) + { + // + // We're done, now we can throw away all incoming connection + // factories. + // + _incomingConnectionFactories.Clear(); + + // + // Remove object references (some of them cyclic). + // + instance_ = null; + _threadPool = null; + _routerEndpoints = null; + _routerInfo = null; + _publishedEndpoints = null; + _locatorInfo = null; + _reference = null; + _objectAdapterFactory = null; + + state_ = StateDestroyed; + System.Threading.Monitor.PulseAll(this); + } + } + + public ObjectPrx add(Ice.Object obj, Identity ident) + { + return addFacet(obj, ident, ""); + } + + public ObjectPrx addFacet(Ice.Object obj, Identity ident, string facet) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + checkServant(obj); + + // + // Create a copy of the Identity argument, in case the caller + // reuses it. + // + Identity id = new Identity(); + id.category = ident.category; + id.name = ident.name; + + _servantManager.addServant(obj, id, facet); + + return newProxy(id, facet); + } + } + + public ObjectPrx addWithUUID(Ice.Object obj) + { + return addFacetWithUUID(obj, ""); + } + + public ObjectPrx addFacetWithUUID(Ice.Object obj, string facet) + { + Identity ident = new Identity(); + ident.category = ""; + ident.name = Guid.NewGuid().ToString(); + + return addFacet(obj, ident, facet); + } + + public void addDefaultServant(Ice.Object servant, string category) + { + checkServant(servant); + + lock(this) + { + checkForDeactivation(); + + _servantManager.addDefaultServant(servant, category); + } + } + + public Ice.Object remove(Identity ident) + { + return removeFacet(ident, ""); + } + + public Ice.Object removeFacet(Identity ident, string facet) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return _servantManager.removeServant(ident, facet); + } + } + + public Dictionary<string, Ice.Object> removeAllFacets(Identity ident) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return _servantManager.removeAllFacets(ident); + } + } + + public Ice.Object removeDefaultServant(string category) + { + lock(this) + { + checkForDeactivation(); + + return _servantManager.removeDefaultServant(category); + } + } + + public Ice.Object find(Identity ident) + { + return findFacet(ident, ""); + } + + public Ice.Object findFacet(Identity ident, string facet) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return _servantManager.findServant(ident, facet); + } + } + + public Dictionary<string, Ice.Object> findAllFacets(Identity ident) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return _servantManager.findAllFacets(ident); + } + } + + public Ice.Object findByProxy(ObjectPrx proxy) + { + lock(this) + { + checkForDeactivation(); + + Reference @ref = ((ObjectPrxHelperBase)proxy).reference__(); + return findFacet(@ref.getIdentity(), @ref.getFacet()); + } + } + + public Ice.Object findDefaultServant(string category) + { + lock(this) + { + checkForDeactivation(); + + return _servantManager.findDefaultServant(category); + } + } + + public void addServantLocator(ServantLocator locator, string prefix) + { + lock(this) + { + checkForDeactivation(); + + _servantManager.addServantLocator(locator, prefix); + } + } + + public ServantLocator removeServantLocator(string prefix) + { + lock(this) + { + checkForDeactivation(); + + return _servantManager.removeServantLocator(prefix); + } + } + + public ServantLocator findServantLocator(string prefix) + { + lock(this) + { + checkForDeactivation(); + + return _servantManager.findServantLocator(prefix); + } + } + + public ObjectPrx createProxy(Identity ident) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return newProxy(ident, ""); + } + } + + public ObjectPrx createDirectProxy(Identity ident) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return newDirectProxy(ident, ""); + } + } + + public ObjectPrx createIndirectProxy(Identity ident) + { + lock(this) + { + checkForDeactivation(); + checkIdentity(ident); + + return newIndirectProxy(ident, "", _id); + } + } + + public void setLocator(LocatorPrx locator) + { + lock(this) + { + checkForDeactivation(); + + _locatorInfo = instance_.locatorManager().get(locator); + } + } + + public LocatorPrx getLocator() + { + lock(this) + { + checkForDeactivation(); + + if(_locatorInfo == null) + { + return null; + } + else + { + return _locatorInfo.getLocator(); + } + } + } + + public void refreshPublishedEndpoints() + { + LocatorInfo locatorInfo = null; + bool registerProcess = false; + List<EndpointI> oldPublishedEndpoints; + + lock(this) + { + checkForDeactivation(); + + oldPublishedEndpoints = _publishedEndpoints; + _publishedEndpoints = parsePublishedEndpoints(); + + locatorInfo = _locatorInfo; + if(!_noConfig) + { + registerProcess = + instance_.initializationData().properties.getPropertyAsInt(_name + ".RegisterProcess") > 0; + } + } + + try + { + Ice.Identity dummy = new Ice.Identity(); + dummy.name = "dummy"; + updateLocatorRegistry(locatorInfo, createDirectProxy(dummy), registerProcess); + } + catch(Ice.LocalException) + { + lock(this) + { + // + // Restore the old published endpoints. + // + _publishedEndpoints = oldPublishedEndpoints; + throw; + } + } + } + + public Endpoint[] getEndpoints() + { + lock(this) + { + List<Endpoint> endpoints = new List<Endpoint>(); + foreach(IncomingConnectionFactory factory in _incomingConnectionFactories) + { + endpoints.Add(factory.endpoint()); + } + return endpoints.ToArray(); + } + } + + public Endpoint[] getPublishedEndpoints() + { + lock(this) + { + return _publishedEndpoints.ToArray(); + } + } + + public bool isLocal(ObjectPrx proxy) + { + // + // NOTE: it's important that isLocal() doesn't perform any blocking operations as + // it can be called for AMI invocations if the proxy has no delegate set yet. + // + + Reference r = ((ObjectPrxHelperBase)proxy).reference__(); + if(r.isWellKnown()) + { + // + // Check the active servant map to see if the well-known + // proxy is for a local object. + // + return _servantManager.hasServant(r.getIdentity()); + } + else if(r.isIndirect()) + { + // + // Proxy is local if the reference adapter id matches this + // adapter id or replica group id. + // + return r.getAdapterId().Equals(_id) || r.getAdapterId().Equals(_replicaGroupId); + } + else + { + EndpointI[] endpoints = r.getEndpoints(); + + lock(this) + { + checkForDeactivation(); + + // + // Proxies which have at least one endpoint in common with the + // endpoints used by this object adapter's incoming connection + // factories are considered local. + // + for(int i = 0; i < endpoints.Length; ++i) + { + foreach(EndpointI endpoint in _publishedEndpoints) + { + if(endpoints[i].equivalent(endpoint)) + { + return true; + } + } + foreach(IncomingConnectionFactory factory in _incomingConnectionFactories) + { + if(endpoints[i].equivalent(factory.endpoint())) + { + return true; + } + } + } + + // + // Proxies which have at least one endpoint in common with the + // router's server proxy endpoints (if any), are also considered + // local. + // + if(_routerInfo != null && _routerInfo.getRouter().Equals(proxy.ice_getRouter())) + { + for(int i = 0; i < endpoints.Length; ++i) + { + foreach(EndpointI endpoint in _routerEndpoints) + { + if(endpoints[i].equivalent(endpoint)) + { + return true; + } + } + } + } + + return false; + } + } + } + + public void flushAsyncBatchRequests(CommunicatorFlushBatch outAsync) + { + List<IncomingConnectionFactory> f; + lock(this) + { + f = new List<IncomingConnectionFactory>(_incomingConnectionFactories); + } + + foreach(IncomingConnectionFactory factory in f) + { + factory.flushAsyncBatchRequests(outAsync); + } + } + + public void updateConnectionObservers() + { + List<IncomingConnectionFactory> f; + lock(this) + { + f = new List<IncomingConnectionFactory>(_incomingConnectionFactories); + } + + foreach(IncomingConnectionFactory p in f) + { + p.updateConnectionObservers(); + } + } + + public void updateThreadObservers() + { + ThreadPool threadPool = null; + lock(this) + { + threadPool = _threadPool; + } + + if(threadPool != null) + { + threadPool.updateObservers(); + } + } + + public void incDirectCount() + { + lock(this) + { + checkForDeactivation(); + + Debug.Assert(_directCount >= 0); + ++_directCount; + } + } + + public void decDirectCount() + { + lock(this) + { + // Not check for deactivation here! + + Debug.Assert(instance_ != null); // Must not be called after destroy(). + + Debug.Assert(_directCount > 0); + if(--_directCount == 0) + { + System.Threading.Monitor.PulseAll(this); + } + } + } + + public ThreadPool getThreadPool() + { + // No mutex lock necessary, _threadPool and instance_ are + // immutable after creation until they are removed in + // destroy(). + + // Not check for deactivation here! + + Debug.Assert(instance_ != null); // Must not be called after destroy(). + + if(_threadPool != null) + { + return _threadPool; + } + else + { + return instance_.serverThreadPool(); + } + + } + + public ServantManager getServantManager() + { + // + // No mutex lock necessary, _servantManager is immutable. + // + return _servantManager; + } + + public ACMConfig getACM() + { + // Not check for deactivation here! + + Debug.Assert(instance_ != null); // Must not be called after destroy(). + return _acm; + } + + public int messageSizeMax() + { + // No mutex lock, immutable. + return _messageSizeMax; + } + + // + // Only for use by ObjectAdapterFactory + // + public ObjectAdapterI(Instance instance, Communicator communicator, + ObjectAdapterFactory objectAdapterFactory, string name, + RouterPrx router, bool noConfig) + { + instance_ = instance; + _communicator = communicator; + _objectAdapterFactory = objectAdapterFactory; + _servantManager = new ServantManager(instance, name); + _name = name; + _incomingConnectionFactories = new List<IncomingConnectionFactory>(); + _publishedEndpoints = new List<EndpointI>(); + _routerEndpoints = new List<EndpointI>(); + _routerInfo = null; + _directCount = 0; + _noConfig = noConfig; + _processId = null; + + if(_noConfig) + { + _id = ""; + _replicaGroupId = ""; + _reference = instance_.referenceFactory().create("dummy -t", ""); + _acm = instance_.serverACM(); + return; + } + + Properties properties = instance_.initializationData().properties; + List<string> unknownProps = new List<string>(); + bool noProps = filterProperties(unknownProps); + + // + // Warn about unknown object adapter properties. + // + if(unknownProps.Count != 0 && properties.getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0) + { + StringBuilder message = new StringBuilder("found unknown properties for object adapter `"); + message.Append(_name); + message.Append("':"); + foreach(string s in unknownProps) + { + message.Append("\n "); + message.Append(s); + } + instance_.initializationData().logger.warning(message.ToString()); + } + + // + // Make sure named adapter has configuration. + // + if(router == null && noProps) + { + // + // These need to be set to prevent warnings/asserts in the destructor. + // + state_ = StateDestroyed; + instance_ = null; + _incomingConnectionFactories = null; + + InitializationException ex = new InitializationException(); + ex.reason = "object adapter `" + _name + "' requires configuration"; + throw ex; + } + + _id = properties.getProperty(_name + ".AdapterId"); + _replicaGroupId = properties.getProperty(_name + ".ReplicaGroupId"); + + // + // Setup a reference to be used to get the default proxy options + // when creating new proxies. By default, create twoway proxies. + // + string proxyOptions = properties.getPropertyWithDefault(_name + ".ProxyOptions", "-t"); + try + { + _reference = instance_.referenceFactory().create("dummy " + proxyOptions, ""); + } + catch(ProxyParseException) + { + InitializationException ex = new InitializationException(); + ex.reason = "invalid proxy options `" + proxyOptions + "' for object adapter `" + _name + "'"; + throw ex; + } + + _acm = new ACMConfig(properties, communicator.getLogger(), _name + ".ACM", instance_.serverACM()); + + { + int defaultMessageSizeMax = instance.messageSizeMax() / 1024; + int num = properties.getPropertyAsIntWithDefault(_name + ".MessageSizeMax", defaultMessageSizeMax); + if(num < 1 || num > 0x7fffffff / 1024) + { + _messageSizeMax = 0x7fffffff; + } + else + { + _messageSizeMax = num * 1024; // Property is in kilobytes, _messageSizeMax in bytes + } + } + + try + { + int threadPoolSize = properties.getPropertyAsInt(_name + ".ThreadPool.Size"); + int threadPoolSizeMax = properties.getPropertyAsInt(_name + ".ThreadPool.SizeMax"); + if(threadPoolSize > 0 || threadPoolSizeMax > 0) + { + _threadPool = new ThreadPool(instance_, _name + ".ThreadPool", 0); + } + + if(router == null) + { + router = RouterPrxHelper.uncheckedCast( + instance_.proxyFactory().propertyToProxy(_name + ".Router")); + } + if(router != null) + { + _routerInfo = instance_.routerManager().get(router); + if(_routerInfo != null) + { + // + // Make sure this router is not already registered with another adapter. + // + if(_routerInfo.getAdapter() != null) + { + Ice.AlreadyRegisteredException ex = new Ice.AlreadyRegisteredException(); + ex.kindOfObject = "object adapter with router"; + ex.id = instance_.identityToString(router.ice_getIdentity()); + throw ex; + } + + // + // Add the router's server proxy endpoints to this object + // adapter. + // + EndpointI[] endpoints = _routerInfo.getServerEndpoints(); + for(int i = 0; i < endpoints.Length; ++i) + { + _routerEndpoints.Add(endpoints[i]); + } + _routerEndpoints.Sort(); // Must be sorted. + + // + // Remove duplicate endpoints, so we have a list of unique endpoints. + // + for(int i = 0; i < _routerEndpoints.Count-1;) + { + EndpointI e1 = _routerEndpoints[i]; + EndpointI e2 = _routerEndpoints[i + 1]; + if(e1.Equals(e2)) + { + _routerEndpoints.RemoveAt(i); + } + else + { + ++i; + } + } + + // + // Associate this object adapter with the router. This way, + // new outgoing connections to the router's client proxy will + // use this object adapter for callbacks. + // + _routerInfo.setAdapter(this); + + // + // Also modify all existing outgoing connections to the + // router's client proxy to use this object adapter for + // callbacks. + // + instance_.outgoingConnectionFactory().setRouterInfo(_routerInfo); + } + } + else + { + // + // Parse the endpoints, but don't store them in the adapter. The connection + // factory might change it, for example, to fill in the real port number. + // + List<EndpointI> endpoints = parseEndpoints(properties.getProperty(_name + ".Endpoints"), true); + foreach(EndpointI endp in endpoints) + { + IncomingConnectionFactory factory = new IncomingConnectionFactory(instance, endp, this); + _incomingConnectionFactories.Add(factory); + } + if(endpoints.Count == 0) + { + TraceLevels tl = instance_.traceLevels(); + if(tl.network >= 2) + { + instance_.initializationData().logger.trace(tl.networkCat, "created adapter `" + _name + + "' without endpoints"); + } + } + + // + // Parse published endpoints. + // + _publishedEndpoints = parsePublishedEndpoints(); + } + + if(properties.getProperty(_name + ".Locator").Length > 0) + { + setLocator(LocatorPrxHelper.uncheckedCast( + instance_.proxyFactory().propertyToProxy(_name + ".Locator"))); + } + else + { + setLocator(instance_.referenceFactory().getDefaultLocator()); + } + } + catch(LocalException) + { + destroy(); + throw; + } + } + + /* + ~ObjectAdapterI() + { + if(!_deactivated) + { + string msg = "object adapter `" + getName() + "' has not been deactivated"; + if(!Environment.HasShutdownStarted) + { + instance_.initializationData().logger.warning(msg); + } + else + { + Console.Error.WriteLine(msg); + } + } + else if(!_destroyed) + { + string msg = "object adapter `" + getName() + "' has not been destroyed"; + if(!Environment.HasShutdownStarted) + { + instance_.initializationData().logger.warning(msg); + } + else + { + Console.Error.WriteLine(msg); + } + } + } + */ + + private ObjectPrx newProxy(Identity ident, string facet) + { + if(_id.Length == 0) + { + return newDirectProxy(ident, facet); + } + else if(_replicaGroupId.Length == 0) + { + return newIndirectProxy(ident, facet, _id); + } + else + { + return newIndirectProxy(ident, facet, _replicaGroupId); + } + } + + private ObjectPrx newDirectProxy(Identity ident, string facet) + { + EndpointI[] endpoints; + + // + // Use the published endpoints, otherwise use the endpoints from all + // incoming connection factories. + // + int sz = _publishedEndpoints.Count; + endpoints = new EndpointI[sz + _routerEndpoints.Count]; + for(int i = 0; i < sz; ++i) + { + endpoints[i] = _publishedEndpoints[i]; + } + + // + // Now we also add the endpoints of the router's server proxy, if + // any. This way, object references created by this object adapter + // will also point to the router's server proxy endpoints. + // + for(int i = 0; i < _routerEndpoints.Count; ++i) + { + endpoints[sz + i] = _routerEndpoints[i]; + } + + // + // Create a reference and return a proxy for this reference. + // + Reference reference = instance_.referenceFactory().create(ident, facet, _reference, endpoints); + return instance_.proxyFactory().referenceToProxy(reference); + } + + private ObjectPrx newIndirectProxy(Identity ident, string facet, string id) + { + // + // Create a reference with the adapter id and return a + // proxy for the reference. + // + Reference reference = instance_.referenceFactory().create(ident, facet, _reference, id); + return instance_.proxyFactory().referenceToProxy(reference); + } + + private void checkForDeactivation() + { + if(state_ >= StateDeactivating) + { + ObjectAdapterDeactivatedException ex = new ObjectAdapterDeactivatedException(); + ex.name = getName(); + throw ex; + } + } + + private static void checkIdentity(Identity ident) + { + if(ident.name == null || ident.name.Length == 0) + { + throw new IllegalIdentityException(ident); + } + if(ident.category == null) + { + ident.category = ""; + } + } + + private static void checkServant(Ice.Object servant) + { + if(servant == null) + { + throw new IllegalServantException("cannot add null servant to Object Adapter"); + } + } + + private List<EndpointI> parseEndpoints(string endpts, bool oaEndpoints) + { + int beg; + int end = 0; + + string delim = " \t\n\r"; + + List<EndpointI> endpoints = new List<EndpointI>(); + while(end < endpts.Length) + { + beg = IceUtilInternal.StringUtil.findFirstNotOf(endpts, delim, end); + if(beg == -1) + { + break; + } + + end = beg; + while(true) + { + end = endpts.IndexOf((System.Char) ':', end); + if(end == -1) + { + end = endpts.Length; + break; + } + else + { + bool quoted = false; + int quote = beg; + while(true) + { + quote = endpts.IndexOf((System.Char) '\"', quote); + if(quote == -1 || end < quote) + { + break; + } + else + { + quote = endpts.IndexOf((System.Char) '\"', ++quote); + if(quote == -1) + { + break; + } + else if(end < quote) + { + quoted = true; + break; + } + ++quote; + } + } + if(!quoted) + { + break; + } + ++end; + } + } + + if(end == beg) + { + ++end; + continue; + } + + string s = endpts.Substring(beg, (end) - (beg)); + EndpointI endp = instance_.endpointFactoryManager().create(s, oaEndpoints); + if(endp == null) + { +#if COMPACT + if(s.StartsWith("ssl", StringComparison.Ordinal) || s.StartsWith("wss", StringComparison.Ordinal)) + { + instance_.initializationData().logger.warning( + "ignoring endpoint `" + s + + "': IceSSL is not supported with the .NET Compact Framework"); + ++end; + continue; + } +#else + if(AssemblyUtil.runtime_ == AssemblyUtil.Runtime.Mono && + (s.StartsWith("ssl", StringComparison.Ordinal) || s.StartsWith("wss", StringComparison.Ordinal))) + { + instance_.initializationData().logger.warning( + "ignoring endpoint `" + s + "': IceSSL is not supported with Mono"); + ++end; + continue; + } +#endif + Ice.EndpointParseException e2 = new Ice.EndpointParseException(); + e2.str = "invalid object adapter endpoint `" + s + "'"; + throw e2; + } + endpoints.Add(endp); + + ++end; + } + + return endpoints; + } + + private List<EndpointI> parsePublishedEndpoints() + { + // + // Parse published endpoints. If set, these are used in proxies + // instead of the connection factory endpoints. + // + string endpts = instance_.initializationData().properties.getProperty(_name + ".PublishedEndpoints"); + List<EndpointI> endpoints = parseEndpoints(endpts, false); + if(endpoints.Count == 0) + { + // + // If the PublishedEndpoints property isn't set, we compute the published enpdoints + // from the OA endpoints, expanding any endpoints that may be listening on INADDR_ANY + // to include actual addresses in the published endpoints. + // + foreach(IncomingConnectionFactory factory in _incomingConnectionFactories) + { + endpoints.AddRange(factory.endpoint().expand()); + } + } + + if(instance_.traceLevels().network >= 1 && endpoints.Count > 0) + { + StringBuilder s = new StringBuilder("published endpoints for object adapter `"); + s.Append(_name); + s.Append("':\n"); + bool first = true; + foreach(EndpointI endpoint in endpoints) + { + if(!first) + { + s.Append(":"); + } + s.Append(endpoint.ToString()); + first = false; + } + instance_.initializationData().logger.trace(instance_.traceLevels().networkCat, s.ToString()); + } + return endpoints; + } + + private void updateLocatorRegistry(LocatorInfo locatorInfo, ObjectPrx proxy, bool registerProcess) + { + if(!registerProcess && _id.Length == 0) + { + return; // Nothing to update. + } + + // + // Call on the locator registry outside the synchronization to + // blocking other threads that need to lock this OA. + // + LocatorRegistryPrx locatorRegistry = locatorInfo != null ? locatorInfo.getLocatorRegistry() : null; + string serverId = ""; + if(registerProcess) + { + Debug.Assert(instance_ != null); + serverId = instance_.initializationData().properties.getProperty("Ice.ServerId"); + + if(locatorRegistry == null) + { + instance_.initializationData().logger.warning( + "object adapter `" + getName() + "' cannot register the process without a locator registry"); + } + else if(serverId.Length == 0) + { + instance_.initializationData().logger.warning( + "object adapter `" + getName() + + "' cannot register the process without a value for Ice.ServerId"); + } + } + + if(locatorRegistry == null) + { + return; + } + + if(_id.Length > 0) + { + try + { + if(_replicaGroupId.Length == 0) + { + locatorRegistry.setAdapterDirectProxy(_id, proxy); + } + else + { + locatorRegistry.setReplicatedAdapterDirectProxy(_id, _replicaGroupId, proxy); + } + } + catch(AdapterNotFoundException) + { + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n"); + s.Append("the object adapter is not known to the locator registry"); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + + NotRegisteredException ex1 = new NotRegisteredException(); + ex1.kindOfObject = "object adapter"; + ex1.id = _id; + throw ex1; + } + catch(InvalidReplicaGroupIdException) + { + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n"); + s.Append("the replica group `" + _replicaGroupId + "' is not known to the locator registry"); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + + NotRegisteredException ex1 = new NotRegisteredException(); + ex1.kindOfObject = "replica group"; + ex1.id = _replicaGroupId; + throw ex1; + } + catch(AdapterAlreadyActiveException) + { + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n"); + s.Append("the object adapter endpoints are already set"); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + + ObjectAdapterIdInUseException ex1 = new ObjectAdapterIdInUseException(); + ex1.id = _id; + throw; + } + catch(LocalException e) + { + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't update object adapter `" + _id + "' endpoints with the locator registry:\n"); + s.Append(e.ToString()); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + throw; // TODO: Shall we raise a special exception instead of a non obvious local exception? + } + + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("updated object adapter `" + _id + "' endpoints with the locator registry\n"); + s.Append("endpoints = "); + if(proxy != null) + { + Ice.Endpoint[] endpoints = proxy.ice_getEndpoints(); + for(int i = 0; i < endpoints.Length; i++) + { + s.Append(endpoints[i].ToString()); + if(i + 1 < endpoints.Length) + { + s.Append(":"); + } + } + } + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + } + + if(registerProcess && serverId.Length > 0) + { + lock(this) + { + if(_processId == null) + { + Process servant = new ProcessI(_communicator); + _processId = addWithUUID(servant).ice_getIdentity(); + } + } + + try + { + locatorRegistry.setServerProcessProxy(serverId, + ProcessPrxHelper.uncheckedCast(createDirectProxy(_processId))); + } + catch(ServerNotFoundException) + { + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't register server `" + serverId + "' with the locator registry:\n"); + s.Append("the server is not known to the locator registry"); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + + NotRegisteredException ex1 = new NotRegisteredException(); + ex1.id = serverId; + ex1.kindOfObject = "server"; + throw ex1; + } + catch(LocalException ex) + { + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("couldn't register server `" + serverId + "' with the locator registry:\n" + ex); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + throw; // TODO: Shall we raise a special exception instead of a non obvious local exception? + } + + if(instance_.traceLevels().location >= 1) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("registered server `" + serverId + "' with the locator registry"); + instance_.initializationData().logger.trace(instance_.traceLevels().locationCat, s.ToString()); + } + } + } + + static private readonly string[] _suffixes = + { + "ACM", + "ACM.Timeout", + "ACM.Heartbeat", + "ACM.Close", + "AdapterId", + "Endpoints", + "Locator", + "Locator.EncodingVersion", + "Locator.EndpointSelection", + "Locator.ConnectionCached", + "Locator.PreferSecure", + "Locator.CollocationOptimized", + "Locator.Router", + "MessageSizeMax", + "PublishedEndpoints", + "RegisterProcess", + "ReplicaGroupId", + "Router", + "Router.EncodingVersion", + "Router.EndpointSelection", + "Router.ConnectionCached", + "Router.PreferSecure", + "Router.CollocationOptimized", + "Router.Locator", + "Router.Locator.EndpointSelection", + "Router.Locator.ConnectionCached", + "Router.Locator.PreferSecure", + "Router.Locator.CollocationOptimized", + "Router.Locator.LocatorCacheTimeout", + "Router.Locator.InvocationTimeout", + "Router.LocatorCacheTimeout", + "Router.InvocationTimeout", + "ProxyOptions", + "ThreadPool.Size", + "ThreadPool.SizeMax", + "ThreadPool.SizeWarn", + "ThreadPool.StackSize", + "ThreadPool.Serialize" + }; + + private bool filterProperties(List<string> unknownProps) + { + // + // Do not create unknown properties list if Ice prefix, ie Ice, Glacier2, etc + // + bool addUnknown = true; + String prefix = _name + "."; + for(int i = 0; PropertyNames.clPropNames[i] != null; ++i) + { + if(prefix.StartsWith(PropertyNames.clPropNames[i] + ".", StringComparison.Ordinal)) + { + addUnknown = false; + break; + } + } + + bool noProps = true; + Dictionary<string, string> props = + instance_.initializationData().properties.getPropertiesForPrefix(prefix); + foreach(String prop in props.Keys) + { + bool valid = false; + for(int i = 0; i < _suffixes.Length; ++i) + { + if(prop.Equals(prefix + _suffixes[i])) + { + noProps = false; + valid = true; + break; + } + } + + if(!valid && addUnknown) + { + unknownProps.Add(prop); + } + } + + return noProps; + } + + private const int StateUninitialized = 0; // Just constructed. + private const int StateHeld = 1; + private const int StateActivating = 2; + private const int StateActive = 3; + private const int StateDeactivating = 4; + private const int StateDeactivated = 5; + private const int StateDestroying = 6; + private const int StateDestroyed = 7; + + private int state_ = StateUninitialized; + private Instance instance_; + private Communicator _communicator; + private ObjectAdapterFactory _objectAdapterFactory; + private ThreadPool _threadPool; + private ACMConfig _acm; + private ServantManager _servantManager; + private readonly string _name; + private readonly string _id; + private readonly string _replicaGroupId; + private Reference _reference; + private List<IncomingConnectionFactory> _incomingConnectionFactories; + private List<EndpointI> _routerEndpoints; + private RouterInfo _routerInfo; + private List<EndpointI> _publishedEndpoints; + private LocatorInfo _locatorInfo; + private int _directCount; // The number of direct proxies dispatching on this object adapter. + private bool _noConfig; + private Identity _processId; + private int _messageSizeMax; + } +} diff --git a/csharp/src/Ice/ObjectFactoryManager.cs b/csharp/src/Ice/ObjectFactoryManager.cs new file mode 100644 index 00000000000..e211713ed55 --- /dev/null +++ b/csharp/src/Ice/ObjectFactoryManager.cs @@ -0,0 +1,86 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections.Generic; + + public sealed class ObjectFactoryManager + { + public void add(Ice.ObjectFactory factory, string id) + { + lock(this) + { + if(_factoryMap.ContainsKey(id)) + { + Ice.AlreadyRegisteredException ex = new Ice.AlreadyRegisteredException(); + ex.id = id; + ex.kindOfObject = "object factory"; + throw ex; + } + _factoryMap[id] = factory; + } + } + + public void remove(string id) + { + object o = null; + lock(this) + { + if(!_factoryMap.ContainsKey(id)) + { + Ice.NotRegisteredException ex = new Ice.NotRegisteredException(); + ex.id = id; + ex.kindOfObject = "object factory"; + throw ex; + } + _factoryMap.Remove(id); + } + ((Ice.ObjectFactory)o).destroy(); + } + + public Ice.ObjectFactory find(string id) + { + lock(this) + { + Ice.ObjectFactory factory = null; + _factoryMap.TryGetValue(id, out factory); + return factory; + } + } + + // + // Only for use by Instance + // + internal ObjectFactoryManager() + { + _factoryMap = new Dictionary<string, Ice.ObjectFactory>(); + } + + internal void destroy() + { + Dictionary<string, Ice.ObjectFactory> oldMap = null; + + lock(this) + { + oldMap = _factoryMap; + _factoryMap = new Dictionary<string, Ice.ObjectFactory>(); + } + + foreach(Ice.ObjectFactory factory in oldMap.Values) + { + factory.destroy(); + } + } + + private Dictionary<string, Ice.ObjectFactory> _factoryMap; + } + +} diff --git a/csharp/src/Ice/ObserverHelper.cs b/csharp/src/Ice/ObserverHelper.cs new file mode 100644 index 00000000000..82067242da7 --- /dev/null +++ b/csharp/src/Ice/ObserverHelper.cs @@ -0,0 +1,63 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Collections.Generic; + using Ice.Instrumentation; + + public sealed class ObserverHelper + { + static public InvocationObserver get(Instance instance, string op) + { + CommunicatorObserver obsv = instance.initializationData().observer; + if(obsv != null) + { + InvocationObserver observer = obsv.getInvocationObserver(null, op, _emptyContext); + if(observer != null) + { + observer.attach(); + } + return observer; + } + return null; + } + + static public InvocationObserver get(Ice.ObjectPrx proxy, string op) + { + return get(proxy, op, null); + } + + static public InvocationObserver get(Ice.ObjectPrx proxy, string op, Dictionary<string, string> context) + { + CommunicatorObserver obsv = + ((Ice.ObjectPrxHelperBase)proxy).reference__().getInstance().initializationData().observer; + if(obsv != null) + { + InvocationObserver observer; + if(context == null) + { + observer = obsv.getInvocationObserver(proxy, op, _emptyContext); + } + else + { + observer = obsv.getInvocationObserver(proxy, op, context); + } + if(observer != null) + { + observer.attach(); + } + return observer; + } + return null; + } + + private static Dictionary<string, string> _emptyContext = new Dictionary<string, string>(); + } +}
\ No newline at end of file diff --git a/csharp/src/Ice/OpaqueEndpointI.cs b/csharp/src/Ice/OpaqueEndpointI.cs new file mode 100644 index 00000000000..2bf92073e34 --- /dev/null +++ b/csharp/src/Ice/OpaqueEndpointI.cs @@ -0,0 +1,428 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + + sealed class OpaqueEndpointI : EndpointI + { + public OpaqueEndpointI(List<string> args) + { + _type = -1; + _rawEncoding = Ice.Util.Encoding_1_0; + _rawBytes = new byte[0]; + + initWithOptions(args); + + if(_type < 0) + { + throw new Ice.EndpointParseException("no -t option in endpoint " + ToString()); + } + if(_rawBytes.Length == 0) + { + throw new Ice.EndpointParseException("no -v option in endpoint " + ToString()); + } + + calcHashValue(); + } + + public OpaqueEndpointI(short type, BasicStream s) + { + _type = type; + _rawEncoding = s.getReadEncoding(); + int sz = s.getReadEncapsSize(); + _rawBytes = new byte[sz]; + s.readBlob(_rawBytes); + + calcHashValue(); + } + + // + // Marshal the endpoint + // + public override void streamWrite(BasicStream s) + { + s.startWriteEncaps(_rawEncoding, Ice.FormatType.DefaultFormat); + s.writeBlob(_rawBytes); + s.endWriteEncaps(); + } + + // + // Convert the endpoint to its string form + // + public override string ice_toString_() + { + string val = IceUtilInternal.Base64.encode(_rawBytes); + return "opaque -t " + _type + " -e " + Ice.Util.encodingVersionToString(_rawEncoding) + " -v " + val; + } + + private sealed class InfoI : Ice.OpaqueEndpointInfo + { + public InfoI(short type, Ice.EncodingVersion rawEncoding, byte[] rawBytes) : + base(-1, false, rawEncoding, rawBytes) + { + _type = type; + } + + override public short type() + { + return _type; + } + + override public bool datagram() + { + return false; + } + + override public bool secure() + { + return false; + } + + private readonly short _type; + } + + // + // Return the endpoint information. + // + public override Ice.EndpointInfo getInfo() + { + return new InfoI(_type, _rawEncoding, _rawBytes); + } + + // + // Return the endpoint type + // + public override short type() + { + return _type; + } + + // + // Return the protocol name; + // + public override string protocol() + { + return "opaque"; + } + + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + public override int timeout() + { + return -1; + } + + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + public override EndpointI timeout(int t) + { + return this; + } + + public override string connectionId() + { + return ""; + } + + // + // Return a new endpoint with a different connection id. + // + public override EndpointI connectionId(string id) + { + return this; + } + + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + public override bool compress() + { + return false; + } + + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + public override EndpointI compress(bool compress) + { + return this; + } + + // + // Return true if the endpoint is datagram-based. + // + public override bool datagram() + { + return false; + } + + // + // Return true if the endpoint is secure. + // + public override bool secure() + { + return false; + } + + // + // Get the encoded endpoint. + // + public byte[] rawBytes() + { + return _rawBytes; + } + + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. + // + public override Transceiver transceiver() + { + return null; + } + + // + // Return connectors for this endpoint, or empty list if no connector + // is available. + // + public override void connectors_async(Ice.EndpointSelectionType endSel, EndpointI_connectors callback) + { + callback.connectors(new List<Connector>()); + } + + // + // Return an acceptor for this endpoint, or null if no acceptors + // is available. + // + public override Acceptor acceptor(string adapterName) + { + return null; + } + + // + // Expand endpoint out in to separate endpoints for each local + // host if listening on INADDR_ANY on server side or if no host + // was specified on client side. + // + public override List<EndpointI> expand() + { + List<EndpointI> endps = new List<EndpointI>(); + endps.Add(this); + return endps; + } + + // + // Check whether the endpoint is equivalent to another one. + // + public override bool equivalent(EndpointI endpoint) + { + return false; + } + + public override int GetHashCode() + { + return _hashCode; + } + + public override string options() + { + string s = ""; + if(_type > -1) + { + s += " -t " + _type; + } + s += " -e " + Ice.Util.encodingVersionToString(_rawEncoding); + if(_rawBytes.Length > 0) + { + s += " -v " + IceUtilInternal.Base64.encode(_rawBytes); + } + return s; + } + + // + // Compare endpoints for sorting purposes + // + public override int CompareTo(EndpointI obj) + { + if(!(obj is OpaqueEndpointI)) + { + return type() < obj.type() ? -1 : 1; + } + + OpaqueEndpointI p = (OpaqueEndpointI)obj; + if(this == p) + { + return 0; + } + + if(_type < p._type) + { + return -1; + } + else if(p._type < _type) + { + return 1; + } + + if(_rawEncoding.major < p._rawEncoding.major) + { + return -1; + } + else if(p._rawEncoding.major < _rawEncoding.major) + { + return 1; + } + + if(_rawEncoding.minor < p._rawEncoding.minor) + { + return -1; + } + else if(p._rawEncoding.minor < _rawEncoding.minor) + { + return 1; + } + + if(_rawBytes.Length < p._rawBytes.Length) + { + return -1; + } + else if(p._rawBytes.Length < _rawBytes.Length) + { + return 1; + } + for(int i = 0; i < _rawBytes.Length; i++) + { + if(_rawBytes[i] < p._rawBytes[i]) + { + return -1; + } + else if(p._rawBytes[i] < _rawBytes[i]) + { + return 1; + } + } + + return 0; + } + + protected override bool checkOption(string option, string argument, string endpoint) + { + switch(option[1]) + { + case 't': + { + if(_type > -1) + { + throw new Ice.EndpointParseException("multiple -t options in endpoint " + endpoint); + } + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for -t option in endpoint " + endpoint); + } + + int t; + try + { + t = System.Int32.Parse(argument, CultureInfo.InvariantCulture); + } + catch(System.FormatException) + { + throw new Ice.EndpointParseException("invalid type value `" + argument + "' in endpoint " + + endpoint); + } + + if(t < 0 || t > 65535) + { + throw new Ice.EndpointParseException("type value `" + argument + "' out of range in endpoint " + + endpoint); + } + + _type = (short)t; + return true; + } + + case 'v': + { + if(_rawBytes.Length > 0) + { + throw new Ice.EndpointParseException("multiple -v options in endpoint " + endpoint); + } + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for -v option in endpoint " + endpoint); + } + + for(int j = 0; j < argument.Length; ++j) + { + if(!IceUtilInternal.Base64.isBase64(argument[j])) + { + throw new Ice.EndpointParseException("invalid base64 character `" + argument[j] + + "' (ordinal " + ((int)argument[j]) + + ") in endpoint " + endpoint); + } + } + _rawBytes = IceUtilInternal.Base64.decode(argument); + return true; + } + + case 'e': + { + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for -e option in endpoint " + endpoint); + } + + try + { + _rawEncoding = Ice.Util.stringToEncodingVersion(argument); + } + catch(Ice.VersionParseException e) + { + throw new Ice.EndpointParseException("invalid encoding version `" + argument + + "' in endpoint " + endpoint + ":\n" + e.str); + } + return true; + } + + default: + { + return false; + } + } + } + + private void calcHashValue() + { + int h = 5381; + IceInternal.HashUtil.hashAdd(ref h, _type); + IceInternal.HashUtil.hashAdd(ref h, _rawEncoding); + IceInternal.HashUtil.hashAdd(ref h, _rawBytes); + _hashCode = h; + } + + private short _type; + private Ice.EncodingVersion _rawEncoding; + private byte[] _rawBytes; + private int _hashCode; + } + +} diff --git a/csharp/src/Ice/Optional.cs b/csharp/src/Ice/Optional.cs new file mode 100644 index 00000000000..8527f87ccf9 --- /dev/null +++ b/csharp/src/Ice/Optional.cs @@ -0,0 +1,262 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + using System; + using System.Collections.Generic; + using System.Runtime.Serialization; + + public struct NoneType + { + } + + /// <summary> + /// Encapsulates an optional value. Instances of this type are immutable. + /// </summary> +#if SILVERLIGHT + public struct Optional<T> +#else + [Serializable] + public struct Optional<T> : ISerializable +#endif + { + /// <summary> + /// Creates an optional value whose state is unset. + /// </summary> + public Optional(NoneType none) + { + _value = default(T); + _isSet = false; + } + + /// <summary> + /// Creates an optional value and sets its value to the given argument. + /// </summary> + public Optional(T v) + { + _value = v; + _isSet = true; + } + + /// <summary> + /// Creates an optional value whose state is copied from the given argument. + /// </summary> + public Optional(Optional<T> v) + { + _value = v._value; + _isSet = v._isSet; + } + +#if !SILVERLIGHT + /// <summary> + /// Initializes a new instance of the exception with serialized data. + /// </summary> + /// <param name="info">Holds the serialized object data about the exception being thrown.</param> + /// <param name="context">Contains contextual information about the source or destination.</param> + public Optional(SerializationInfo info, StreamingContext context) + { + _isSet = info.GetBoolean("isSet"); + if(_isSet) + { + _value = (T)info.GetValue("value", typeof(T)); + } + else + { + _value = default(T); + } + } +#endif + + /// <summary> + /// Conversion operator to the underlying type; a cast is required. An exception + /// is raised if no value is set. + /// </summary> + /// <returns>The encapsulated value.</returns> + /// <exception cref="System.InvalidOperationException">Thrown if no value is set.</exception> + public static explicit operator T(Optional<T> v) + { + return v.Value; + } + + /// <summary> + /// Conversion operator from a value of the underlying type; no cast is required. + /// </summary> + public static implicit operator Optional<T>(T v) + { + return new Optional<T>(v); + } + + /// <summary> + /// Conversion operator from a None value; no cast is required. + /// </summary> + public static implicit operator Optional<T>(NoneType v) + { + return new Optional<T>(); + } + + /// <summary> + /// Reads and writes the encapsulated value. + /// </summary> + /// <exception cref="System.InvalidOperationException">Thrown if the property is read and no value is + /// set.</exception> + public T Value + { + get + { + if(!_isSet) + { + throw new System.InvalidOperationException(); + } + return _value; + } + } + + /// <summary> + /// Determines whether a value is set. + /// </summary> + /// <returns>True if a value is set, false otherwise.</returns> + public bool HasValue + { + get + { + return _isSet; + } + } + + public override bool Equals(object other) + { + if(object.ReferenceEquals(this, other)) + { + return true; + } + if(other == null) + { + return false; + } + + try + { + Optional<T> o2 = (Optional<T>)other; + + if(_isSet != o2._isSet) + { + return false; + } + else if(_isSet) + { + EqualityComparer<T> comparer = EqualityComparer<T>.Default; + return comparer.Equals(_value, o2._value); + } + + return true; + } + catch(System.Exception) + { + return false; + } + } + + public override int GetHashCode() + { + if(!_isSet) + { + return base.GetHashCode(); + } + else + { + return _value.GetHashCode(); + } + } + +#if !SILVERLIGHT + /// <summary> + /// Serializes an optional value. + /// </summary> + /// <param name="info">Holds the serialized object data about the exception being thrown.</param> + /// <param name="context">Contains contextual information about the source or destination.</param> + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("isSet", _isSet); + if(_isSet) + { + info.AddValue("value", _value, typeof(T)); + } + } +#endif + + private T _value; + private bool _isSet; + } + + /// <summary> + /// Handles callbacks for an optional object parameter. + /// </summary> + public class OptionalPatcher<T> : IceInternal.Patcher + where T : Ice.Object + { + /// <summary> + /// Instantiates the class with the given optional. + /// </summary> + /// <param name="type">The Slice type ID corresponding to the formal type.</param> + public OptionalPatcher(string type) : + base(type) + { + } + + /// <summary> + /// Sets the Ice object of the optional to the passed instance. + /// </summary> + /// <param name="v">The new object for the optional.</param> + public override void patch(Ice.Object v) + { + if(v == null || typeof(T).IsAssignableFrom(v.GetType())) + { + // + // The line below must assign to the Value property. We could also + // have written it this way: + // + // this.opt = (T)v; + // + // However, when v is null, the optional might be cleared, which + // is not the result we want. + // + this.value = new Optional<T>((T)v); + } + else + { + IceInternal.Ex.throwUOE(type(), v.ice_id()); + } + } + + /// <summary> + /// The target optional. + /// </summary> + public Optional<T> value = new Optional<T>(); + } + + /// <summary> + /// The optional format. + /// + /// An optional value is encoded with a specific optional format. This optional + /// format describes how the data is encoded and how it can be skipped by the + /// unmarshaling code if the optional is not known to the receiver. + /// </summary> + public enum OptionalFormat + { + F1 = 0, + F2 = 1, + F4 = 2, + F8 = 3, + Size = 4, + VSize = 5, + FSize = 6, + Class = 7 + } +} diff --git a/csharp/src/Ice/Options.cs b/csharp/src/Ice/Options.cs new file mode 100644 index 00000000000..8303758c928 --- /dev/null +++ b/csharp/src/Ice/Options.cs @@ -0,0 +1,406 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; + +namespace IceUtilInternal +{ + public sealed class Options + { + public sealed class BadQuote : System.Exception + { + public BadQuote(string message) : base(message) + { + } + } + + enum State { Normal, DoubleQuote, SingleQuote, ANSIQuote }; + + static public string[] + split(string line) + { + string IFS = " \t\n"; + + string l = line.Trim(); + if(l.Length == 0) + { + return new string[0]; + } + + State state = State.Normal; + + string arg = ""; + List<string> vec = new List<string>(); + + for(int i = 0; i < l.Length; ++i) + { + char c = l[i]; + switch(state) + { + case State.Normal: + { + switch(c) + { + case '\\': + { + // + // Ignore a backslash at the end of the string, + // and strip backslash-newline pairs. If a + // backslash is followed by a space, single quote, + // double quote, or dollar sign, we drop the backslash + // and write the space, single quote, double quote, + // or dollar sign. This is necessary to allow quotes + // to be escaped. Dropping the backslash preceding a + // space deviates from bash quoting rules, but is + // necessary so we don't drop backslashes from Windows + // path names.) + // + if(i < l.Length - 1 && l[++i] != '\n') + { + switch(l[i]) + { + case ' ': + case '$': + case '\'': + case '"': + { + arg += l[i]; + break; + } + default: + { + arg += '\\'; + arg += l[i]; + break; + } + } + } + break; + } + case '\'': + { + state = State.SingleQuote; + break; + } + case '"': + { + state = State.DoubleQuote; + break; + } + case '$': + { + if(i < l.Length - 1 && l[i + 1] == '\'') + { + state = State.ANSIQuote; // Bash uses $'<text>' to allow ANSI escape sequences + // within <text>. + ++i; + } + else + { + arg += '$'; + } + break; + } + default: + { + if(IFS.IndexOf(l[i]) != -1) + { + vec.Add(arg); + arg = ""; + + // + // Move to start of next argument. + // + while(++i < l.Length && IFS.IndexOf(l[i]) != -1) + { + ; + } + --i; + } + else + { + arg += l[i]; + } + break; + } + } + break; + } + case State.DoubleQuote: + { + // + // Within double quotes, only backslash retains its special + // meaning, and only if followed by double quote, backslash, + // or newline. If not followed by one of these characters, + // both the backslash and the character are preserved. + // + if(c == '\\' && i < l.Length - 1) + { + switch(c = l[++i]) + { + case '"': + case '\\': + case '\n': + { + arg += c; + break; + } + default: + { + arg += '\\'; + arg += c; + break; + } + } + } + else if(c == '"') // End of double-quote mode. + { + state = State.Normal; + } + else + { + arg += c; // Everything else is taken literally. + } + break; + } + case State.SingleQuote: + { + if(c == '\'') // End of single-quote mode. + { + state = State.Normal; + } + else + { + arg += c; // Everything else is taken literally. + } + break; + } + case State.ANSIQuote: + { + switch(c) + { + case '\\': + { + if(i == l.Length - 1) + { + break; + } + switch(c = l[++i]) + { + // + // Single-letter escape sequences. + // + case 'a': + { + arg += '\a'; + break; + } + case 'b': + { + arg += '\b'; + break; + } + case 'f': + { + arg += '\f'; + break; + } + case 'n': + { + arg += '\n'; + break; + } + case 'r': + { + arg += '\r'; + break; + } + case 't': + { + arg += '\t'; + break; + } + case 'v': + { + arg += '\v'; + break; + } + case '\\': + { + arg += '\\'; + break; + } + case '\'': + { + arg += '\''; + break; + } + case 'e': // Not ANSI-C, but used by bash. + { + arg += '\u001B'; + break; + } + + // + // Process up to three octal digits. + // + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + const string octalDigits = "01234567"; + short s = 0; + int j; + for(j = i; j < i + 3 && j < l.Length && octalDigits.IndexOf(c = l[j]) != -1; ++j) + { + s = (short)(s * 8 + c - '0'); + } + i = j - 1; + arg += (char)s; + break; + } + + // + // Process up to two hex digits. + // + case 'x': + { + const string hexDigits = "0123456789abcdefABCDEF"; + if(i < l.Length - 1 && hexDigits.IndexOf(l[i + 1]) == -1) + { + arg += '\\'; + arg += 'x'; + break; + } + + short s = 0; + int j; + for(j = i + 1; + j < i + 3 && j < l.Length && hexDigits.IndexOf(c = l[j]) != -1; + ++j) + { + s *= 16; + if(char.IsDigit(c)) + { + s += (short)(c - '0'); + } + else if(char.IsLower(c)) + { + s += (short)(c - 'a' + 10); + } + else + { + s += (short)(c - 'A' + 10); + } + } + i = j - 1; + arg += (char)s; + break; + } + + // + // Process control-chars. + // + case 'c': + { + c = l[++i]; + if((char.ToUpper(c, CultureInfo.InvariantCulture) >= 'A' && char.ToUpper(c, CultureInfo.InvariantCulture) <= 'Z') || + c == '@' || + (c >= '[' && c <= '_')) + { + arg += (char)(char.ToUpper(c, CultureInfo.InvariantCulture) - '@'); + } + else + { + // + // Bash does not define what should happen if a \c + // is not followed by a recognized control character. + // We simply treat this case like other unrecognized + // escape sequences, that is, we preserve the escape + // sequence unchanged. + // + arg += '\\'; + arg += 'c'; + arg += c; + } + break; + } + + // + // If inside an ANSI-quoted string, a backslash isn't followed by + // one of the recognized characters, both the backslash and the + // character are preserved. + // + default: + { + arg += '\\'; + arg += c; + break; + } + } + break; + } + case '\'': // End of ANSI-quote mode. + { + state = State.Normal; + break; + } + default: + { + arg += c; // Everything else is taken literally. + break; + } + } + break; + } + default: + { + Debug.Assert(false); + break; + } + } + } + + switch(state) + { + case State.Normal: + { + vec.Add(arg); + break; + } + case State.SingleQuote: + { + throw new BadQuote("missing closing single quote"); + } + case State.DoubleQuote: + { + throw new BadQuote("missing closing double quote"); + } + case State.ANSIQuote: + { + throw new BadQuote("unterminated $' quote"); + } + default: + { + Debug.Assert(false); + break; + } + } + + return (string[])vec.ToArray(); + } + } +} diff --git a/csharp/src/Ice/OutgoingAsync.cs b/csharp/src/Ice/OutgoingAsync.cs new file mode 100644 index 00000000000..8b71e65c81f --- /dev/null +++ b/csharp/src/Ice/OutgoingAsync.cs @@ -0,0 +1,1267 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + + public class OutgoingAsyncBase : AsyncResultI + { + public virtual Ice.AsyncCallback sent() + { + return sent(true); + } + + public virtual Ice.AsyncCallback completed(Ice.Exception ex) + { + return finished(ex); + } + + public virtual Ice.AsyncCallback completed() + { + Debug.Assert(false); // Must be implemented by classes that handle responses + return null; + } + + public void attachRemoteObserver(Ice.ConnectionInfo info, Ice.Endpoint endpt, int requestId) + { + if(observer_ != null) + { + int size = os_.size() - Protocol.headerSize - 4; + childObserver_ = getObserver().getRemoteObserver(info, endpt, requestId, size); + if(childObserver_ != null) + { + childObserver_.attach(); + } + } + } + + public void attachCollocatedObserver(Ice.ObjectAdapter adapter, int requestId) + { + if(observer_ != null) + { + int size = os_.size() - Protocol.headerSize - 4; + childObserver_ = getObserver().getCollocatedObserver(adapter, requestId, size); + if(childObserver_ != null) + { + childObserver_.attach(); + } + } + } + + public IceInternal.BasicStream getOs() + { + return os_; + } + + public virtual IceInternal.BasicStream getIs() + { + return null; // Must be implemented by classes that handle responses + } + + protected OutgoingAsyncBase(Ice.Communicator com, Instance instance, string op, object cookie) : + base(com, instance, op, cookie) + { + os_ = new BasicStream(instance, Ice.Util.currentProtocolEncoding); + } + + protected OutgoingAsyncBase(Ice.Communicator com, Instance instance, string op, object cookie, BasicStream os) : + base(com, instance, op, cookie) + { + os_ = os; + } + + protected new Ice.AsyncCallback sent(bool done) + { + if(done) + { + if(childObserver_ != null) + { + childObserver_.detach(); + childObserver_ = null; + } + } + return base.sent(done); + } + + protected new Ice.AsyncCallback finished(Ice.Exception ex) + { + if(childObserver_ != null) + { + childObserver_.failed(ex.ice_name()); + childObserver_.detach(); + childObserver_ = null; + } + return base.finished(ex); + } + + protected BasicStream os_; + protected Ice.Instrumentation.ChildInvocationObserver childObserver_; + } + + // + // Base class for proxy based invocations. This class handles the + // retry for proxy invocations. It also ensures the child observer is + // correct notified of failures and make sure the retry task is + // correctly canceled when the invocation completes. + // + public abstract class ProxyOutgoingAsyncBase : OutgoingAsyncBase, TimerTask + { + public static ProxyOutgoingAsyncBase check(Ice.AsyncResult r, Ice.ObjectPrx prx, string operation) + { + return ProxyOutgoingAsyncBase.check<ProxyOutgoingAsyncBase>(r, prx, operation); + } + + public abstract bool invokeRemote(Ice.ConnectionI con, bool compress, bool resp, out Ice.AsyncCallback cb); + + public abstract bool invokeCollocated(CollocatedRequestHandler handler, out Ice.AsyncCallback cb); + + public override Ice.ObjectPrx getProxy() + { + return proxy_; + } + + public override Ice.AsyncCallback completed(Ice.Exception exc) + { + if(childObserver_ != null) + { + childObserver_.failed(exc.ice_name()); + childObserver_.detach(); + childObserver_ = null; + } + + cachedConnection_ = null; + if(proxy_.reference__().getInvocationTimeout() == -2) + { + instance_.timer().cancel(this); + } + + // + // NOTE: at this point, synchronization isn't needed, no other threads should be + // calling on the callback. + // + try + { + // + // It's important to let the retry queue do the retry even if + // the retry interval is 0. This method can be called with the + // connection locked so we can't just retry here. + // + instance_.retryQueue().add(this, handleException(exc)); + return null; + } + catch(Ice.Exception ex) + { + return finished(ex); // No retries, we're done + } + } + + public void retryException(Ice.Exception ex) + { + try + { + // + // It's important to let the retry queue do the retry. This is + // called from the connect request handler and the retry might + // require could end up waiting for the flush of the + // connection to be done. + // + proxy_.updateRequestHandler__(handler_, null); // Clear request handler and always retry. + instance_.retryQueue().add(this, 0); + } + catch(Ice.Exception exc) + { + Ice.AsyncCallback cb = completed(exc); + if(cb != null) + { + invokeCompletedAsync(cb); + } + } + } + + public override void cancelable(CancellationHandler handler) + { + if(proxy_.reference__().getInvocationTimeout() == -2 && cachedConnection_ != null) + { + int timeout = cachedConnection_.timeout(); + if(timeout > 0) + { + instance_.timer().schedule(this, timeout); + } + } + base.cancelable(handler); + } + + public void retry() + { + invokeImpl(false); + } + + public virtual void abort(Ice.Exception ex) + { + Debug.Assert(childObserver_ == null); + Ice.AsyncCallback cb = finished(ex); + if(cb != null) + { + invokeCompletedAsync(cb); + } + else if(ex is Ice.CommunicatorDestroyedException) + { + // + // If it's a communicator destroyed exception, don't swallow + // it but instead notify the user thread. Even if no callback + // was provided. + // + throw ex; + } + } + + public void runTimerTask() + { + if(proxy_.reference__().getInvocationTimeout() == -2) + { + cancel(new Ice.ConnectionTimeoutException()); + } + else + { + cancel(new Ice.InvocationTimeoutException()); + } + } + + protected ProxyOutgoingAsyncBase(Ice.ObjectPrxHelperBase prx, string op, object cookie) : + base(prx.ice_getCommunicator(), prx.reference__().getInstance(), op, cookie) + { + proxy_ = prx; + mode_ = Ice.OperationMode.Normal; + _cnt = 0; + _sent = false; + } + + protected ProxyOutgoingAsyncBase(Ice.ObjectPrxHelperBase prx, string op, object cookie, BasicStream os) : + base(prx.ice_getCommunicator(), prx.reference__().getInstance(), op, cookie, os) + { + proxy_ = prx; + mode_ = Ice.OperationMode.Normal; + _cnt = 0; + _sent = false; + } + + protected static T check<T>(Ice.AsyncResult r, Ice.ObjectPrx prx, string operation) + { + if(r != null && r.getProxy() != prx) + { + throw new System.ArgumentException("Proxy for call to end_" + operation + + " does not match proxy that was used to call corresponding begin_" + + operation + " method"); + } + return check<T>(r, operation); + } + + protected void invokeImpl(bool userThread) + { + try + { + if(userThread) + { + int invocationTimeout = proxy_.reference__().getInvocationTimeout(); + if(invocationTimeout > 0) + { + instance_.timer().schedule(this, invocationTimeout); + } + } + else // If not called from the user thread, it's called from the retry queue + { + if(observer_ != null) + { + observer_.retried(); + } + } + + while(true) + { + try + { + _sent = false; + handler_ = proxy_.getRequestHandler__(); + Ice.AsyncCallback sentCallback; + if(handler_.sendAsyncRequest(this, out sentCallback)) + { + if(userThread) + { + sentSynchronously_ = true; + if(sentCallback != null) + { + invokeSent(sentCallback); // Call from the user thread. + } + } + else + { + if(sentCallback != null) + { + invokeSentAsync(sentCallback); // Call from a client thread pool thread. + } + } + } + return; // We're done! + } + catch(RetryException) + { + proxy_.updateRequestHandler__(handler_, null); // Clear request handler and always retry. + } + catch(Ice.Exception ex) + { + if(childObserver_ != null) + { + childObserver_.failed(ex.ice_name()); + childObserver_.detach(); + childObserver_ = null; + } + int interval = handleException(ex); + if(interval > 0) + { + instance_.retryQueue().add(this, interval); + return; + } + else if(observer_ != null) + { + observer_.retried(); + } + } + } + } + catch(Ice.Exception ex) + { + // + // If called from the user thread we re-throw, the exception + // will be catch by the caller and abort() will be called. + // + if(userThread) + { + throw ex; + } + Ice.AsyncCallback cb = finished(ex); // No retries, we're done + if(cb != null) + { + invokeCompletedAsync(cb); + } + } + } + + protected new Ice.AsyncCallback sent(bool done) + { + _sent = true; + if(done) + { + if(proxy_.reference__().getInvocationTimeout() != -1) + { + instance_.timer().cancel(this); + } + } + return base.sent(done); + } + + protected new Ice.AsyncCallback finished(Ice.Exception ex) + { + if(proxy_.reference__().getInvocationTimeout() != -1) + { + instance_.timer().cancel(this); + } + return base.finished(ex); + } + + protected new Ice.AsyncCallback finished(bool ok) + { + if(proxy_.reference__().getInvocationTimeout() != -1) + { + instance_.timer().cancel(this); + } + return base.finished(ok); + } + + protected virtual int handleException(Ice.Exception exc) + { + return proxy_.handleException__(exc, handler_, mode_, _sent, ref _cnt); + } + + protected Ice.ObjectPrxHelperBase proxy_; + protected RequestHandler handler_; + protected Ice.OperationMode mode_; + + private int _cnt; + private bool _sent; + } + + public class OutgoingAsync : ProxyOutgoingAsyncBase + { + public new static OutgoingAsync check(Ice.AsyncResult r, Ice.ObjectPrx prx, string operation) + { + return ProxyOutgoingAsyncBase.check<OutgoingAsync>(r, prx, operation); + } + + public OutgoingAsync(Ice.ObjectPrx prx, string operation, object cookie) : + base((Ice.ObjectPrxHelperBase)prx, operation, cookie) + { + _encoding = Protocol.getCompatibleEncoding(proxy_.reference__().getEncoding()); + _is = null; + } + + public OutgoingAsync(Ice.ObjectPrx prx, string operation, object cookie, BasicStream istr, BasicStream ostr) : + base((Ice.ObjectPrxHelperBase)prx, operation, cookie, ostr) + { + _encoding = Protocol.getCompatibleEncoding(proxy_.reference__().getEncoding()); + _is = istr; + } + + public void prepare(string operation, Ice.OperationMode mode, Dictionary<string, string> ctx, + bool explicitCtx, bool synchronous) + { + Protocol.checkSupportedProtocol(Protocol.getCompatibleProtocol(proxy_.reference__().getProtocol())); + + mode_ = mode; + _synchronous = synchronous; + + if(explicitCtx && ctx == null) + { + ctx = _emptyContext; + } + observer_ = ObserverHelper.get(proxy_, operation, ctx); + + switch(proxy_.reference__().getMode()) + { + case Reference.Mode.ModeTwoway: + case Reference.Mode.ModeOneway: + case Reference.Mode.ModeDatagram: + { + os_.writeBlob(Protocol.requestHdr); + break; + } + + case Reference.Mode.ModeBatchOneway: + case Reference.Mode.ModeBatchDatagram: + { + proxy_.getBatchRequestQueue__().prepareBatchRequest(os_); + break; + } + } + + Reference rf = proxy_.reference__(); + + rf.getIdentity().write__(os_); + + // + // For compatibility with the old FacetPath. + // + string facet = rf.getFacet(); + if(facet == null || facet.Length == 0) + { + os_.writeStringSeq(null); + } + else + { + string[] facetPath = { facet }; + os_.writeStringSeq(facetPath); + } + + os_.writeString(operation); + + os_.writeByte((byte)mode); + + if(ctx != null) + { + // + // Explicit context + // + Ice.ContextHelper.write(os_, ctx); + } + else + { + // + // Implicit context + // + Ice.ImplicitContextI implicitContext = rf.getInstance().getImplicitContext(); + Dictionary<string, string> prxContext = rf.getContext(); + + if(implicitContext == null) + { + Ice.ContextHelper.write(os_, prxContext); + } + else + { + implicitContext.write(prxContext, os_); + } + } + } + + public override Ice.AsyncCallback sent() + { + return sent(!proxy_.ice_isTwoway()); // done = true if not a two-way proxy (no response expected) + } + + public override bool invokeRemote(Ice.ConnectionI con, bool compress, bool resp, out Ice.AsyncCallback sentCB) + { + cachedConnection_ = con; + return con.sendAsyncRequest(this, compress, resp, 0, out sentCB); + } + + public override bool invokeCollocated(CollocatedRequestHandler handler, out Ice.AsyncCallback sentCB) + { + // The BasicStream cannot be cached if the proxy is not a twoway or there is an invocation timeout set. + if(!proxy_.ice_isTwoway() || proxy_.reference__().getInvocationTimeout() != -1) + { + // Disable caching by marking the streams as cached! + state_ |= StateCachedBuffers; + } + return handler.invokeAsyncRequest(this, 0, _synchronous, out sentCB); + } + + public override void abort(Ice.Exception ex) + { + Reference.Mode mode = proxy_.reference__().getMode(); + if(mode == Reference.Mode.ModeBatchOneway || mode == Reference.Mode.ModeBatchDatagram) + { + proxy_.getBatchRequestQueue__().abortBatchRequest(os_); + } + + base.abort(ex); + } + + public void invoke() + { + Reference.Mode mode = proxy_.reference__().getMode(); + if(mode == Reference.Mode.ModeBatchOneway || mode == Reference.Mode.ModeBatchDatagram) + { + sentSynchronously_ = true; + proxy_.getBatchRequestQueue__().finishBatchRequest(os_, proxy_, getOperation()); + finished(true); + return; // Don't call sent/completed callback for batch AMI requests + } + + // + // NOTE: invokeImpl doesn't throw so this can be called from the + // try block with the catch block calling abort() in case of an + // exception. + // + invokeImpl(true); // userThread = true + } + + override public Ice.AsyncCallback completed() + { + Debug.Assert(_is != null); // _is has been initialized prior to this call + + // + // NOTE: this method is called from ConnectionI.parseMessage + // with the connection locked. Therefore, it must not invoke + // any user callbacks. + // + + Debug.Assert(proxy_.ice_isTwoway()); // Can only be called for twoways. + + if(childObserver_ != null) + { + childObserver_.reply(_is.size() - Protocol.headerSize - 4); + childObserver_.detach(); + childObserver_ = null; + } + + byte replyStatus; + try + { + replyStatus = _is.readByte(); + + switch(replyStatus) + { + case ReplyStatus.replyOK: + { + break; + } + + case ReplyStatus.replyUserException: + { + if(observer_ != null) + { + observer_.userException(); + } + break; + } + + case ReplyStatus.replyObjectNotExist: + case ReplyStatus.replyFacetNotExist: + case ReplyStatus.replyOperationNotExist: + { + Ice.Identity id = new Ice.Identity(); + id.read__(_is); + + // + // For compatibility with the old FacetPath. + // + string[] facetPath = _is.readStringSeq(); + string facet; + if(facetPath.Length > 0) + { + if(facetPath.Length > 1) + { + throw new Ice.MarshalException(); + } + facet = facetPath[0]; + } + else + { + facet = ""; + } + + string operation = _is.readString(); + + Ice.RequestFailedException ex = null; + switch(replyStatus) + { + case ReplyStatus.replyObjectNotExist: + { + ex = new Ice.ObjectNotExistException(); + break; + } + + case ReplyStatus.replyFacetNotExist: + { + ex = new Ice.FacetNotExistException(); + break; + } + + case ReplyStatus.replyOperationNotExist: + { + ex = new Ice.OperationNotExistException(); + break; + } + + default: + { + Debug.Assert(false); + break; + } + } + + ex.id = id; + ex.facet = facet; + ex.operation = operation; + throw ex; + } + + case ReplyStatus.replyUnknownException: + case ReplyStatus.replyUnknownLocalException: + case ReplyStatus.replyUnknownUserException: + { + string unknown = _is.readString(); + + Ice.UnknownException ex = null; + switch(replyStatus) + { + case ReplyStatus.replyUnknownException: + { + ex = new Ice.UnknownException(); + break; + } + + case ReplyStatus.replyUnknownLocalException: + { + ex = new Ice.UnknownLocalException(); + break; + } + + case ReplyStatus.replyUnknownUserException: + { + ex = new Ice.UnknownUserException(); + break; + } + + default: + { + Debug.Assert(false); + break; + } + } + + ex.unknown = unknown; + throw ex; + } + + default: + { + throw new Ice.UnknownReplyStatusException(); + } + } + + return finished(replyStatus == ReplyStatus.replyOK); + } + catch(Ice.Exception ex) + { + return completed(ex); + } + } + + public BasicStream startWriteParams(Ice.FormatType format) + { + os_.startWriteEncaps(_encoding, format); + return os_; + } + + public void endWriteParams() + { + os_.endWriteEncaps(); + } + + public void writeEmptyParams() + { + os_.writeEmptyEncaps(_encoding); + } + + public void writeParamEncaps(byte[] encaps) + { + if(encaps == null || encaps.Length == 0) + { + os_.writeEmptyEncaps(_encoding); + } + else + { + os_.writeEncaps(encaps); + } + } + + public IceInternal.BasicStream startReadParams() + { + _is.startReadEncaps(); + return _is; + } + + public void endReadParams() + { + _is.endReadEncaps(); + } + + public void readEmptyParams() + { + _is.skipEmptyEncaps(); + } + + public byte[] readParamEncaps() + { + Ice.EncodingVersion encoding; + return _is.readEncaps(out encoding); + } + + override public BasicStream getIs() + { + // _is can already be initialized if the invocation is retried + if(_is == null) + { + _is = new IceInternal.BasicStream(instance_, Ice.Util.currentProtocolEncoding); + } + return _is; + } + + public void throwUserException() + { + try + { + _is.startReadEncaps(); + _is.throwException(null); + } + catch(Ice.UserException ex) + { + _is.endReadEncaps(); + throw ex; + } + } + + public override void cacheMessageBuffers() + { + if(proxy_.reference__().getInstance().cacheMessageBuffers() > 0) + { + lock(this) + { + if((state_ & StateCachedBuffers) > 0) + { + return; + } + state_ |= StateCachedBuffers; + } + + if(_is != null) + { + _is.reset(); + } + os_.reset(); + + proxy_.cacheMessageBuffers(_is, os_); + + _is = null; + os_ = null; + } + } + + private Ice.EncodingVersion _encoding; + private BasicStream _is; + + // + // If true this AMI request is being used for a generated synchronous invocation. + // + private bool _synchronous; + + private static Dictionary<string, string> _emptyContext = new Dictionary<string, string>(); + } + + public class CommunicatorFlushBatch : IceInternal.AsyncResultI + { + public static CommunicatorFlushBatch check(Ice.AsyncResult r, Ice.Communicator com, string operation) + { + if(r != null && r.getCommunicator() != com) + { + throw new System.ArgumentException("Communicator for call to end_" + operation + + " does not match communicator that was used to call " + + "corresponding begin_" + operation + " method"); + } + return AsyncResultI.check<CommunicatorFlushBatch>(r, operation); + } + + public CommunicatorFlushBatch(Ice.Communicator communicator, Instance instance, string op, object cookie) : + base(communicator, instance, op, cookie) + { + + observer_ = ObserverHelper.get(instance, op); + + // + // _useCount is initialized to 1 to prevent premature callbacks. + // The caller must invoke ready() after all flush requests have + // been initiated. + // + _useCount = 1; + } + + public void flushConnection(Ice.ConnectionI con) + { + lock(this) + { + ++_useCount; + } + + try + { + Ice.AsyncCallback sentCB = null; + FlushBatch flush = new FlushBatch(this); + int batchRequestNum = con.getBatchRequestQueue().swap(flush.getOs()); + if(batchRequestNum == 0) + { + flush.sent(); + } + else + { + con.sendAsyncRequest(flush, false, false, batchRequestNum, out sentCB); + } + Debug.Assert(sentCB == null); + } + catch(Ice.LocalException ex) + { + doCheck(false); + throw ex; + } + } + + public void ready() + { + doCheck(true); + } + + private void doCheck(bool userThread) + { + lock(this) + { + Debug.Assert(_useCount > 0); + if(--_useCount > 0) + { + return; + } + } + + Ice.AsyncCallback sentCB = sent(true); + if(userThread) + { + sentSynchronously_ = true; + if(sentCB != null) + { + invokeSent(sentCB); + } + } + else + { + if(sentCB != null) + { + invokeSentAsync(sentCB); + } + } + } + + class FlushBatch : OutgoingAsyncBase + { + public FlushBatch(CommunicatorFlushBatch outAsync) : + base(outAsync.getCommunicator(), outAsync.instance_, outAsync.getOperation(), null) + { + _outAsync = outAsync; + } + + public override Ice.AsyncCallback sent() + { + if(childObserver_ != null) + { + childObserver_.detach(); + childObserver_ = null; + } + _outAsync.doCheck(false); + return null; + } + + public override Ice.AsyncCallback completed(Ice.Exception ex) + { + if(childObserver_ != null) + { + childObserver_.failed(ex.ice_name()); + childObserver_.detach(); + childObserver_ = null; + } + _outAsync.doCheck(false); + return null; + } + + protected override Ice.Instrumentation.InvocationObserver getObserver() + { + return _outAsync.getObserver(); + } + + private CommunicatorFlushBatch _outAsync; + }; + private int _useCount; + } + + + public class ConnectionFlushBatch : OutgoingAsyncBase + { + public static ConnectionFlushBatch check(Ice.AsyncResult r, Ice.Connection con, string operation) + { + if(r != null && r.getConnection() != con) + { + throw new System.ArgumentException("Connection for call to end_" + operation + + " does not match connection that was used to call " + + "corresponding begin_" + operation + " method"); + } + return AsyncResultI.check<ConnectionFlushBatch>(r, operation); + } + + public ConnectionFlushBatch(Ice.ConnectionI con, Ice.Communicator communicator, Instance instance, string op, + object cookie) : + base(communicator, instance, op, cookie) + { + _connection = con; + } + + public override Ice.Connection getConnection() + { + return _connection; + } + + public void invoke() + { + try + { + int batchRequestNum = _connection.getBatchRequestQueue().swap(os_); + + bool isSent = false; + Ice.AsyncCallback sentCB; + if(batchRequestNum == 0) + { + isSent = true; + sentCB = sent(); + } + else + { + isSent = _connection.sendAsyncRequest(this, false, false, batchRequestNum, out sentCB); + } + + if(isSent) + { + sentSynchronously_ = true; + if(sentCB != null) + { + invokeSent(sentCB); + } + } + } + catch(RetryException ex) + { + Ice.AsyncCallback cb = completed(ex.get()); + if(cb != null) + { + invokeCompletedAsync(cb); + } + } + catch(Ice.Exception ex) + { + Ice.AsyncCallback cb = completed(ex); + if(cb != null) + { + invokeCompletedAsync(cb); + } + } + } + + private Ice.ConnectionI _connection; + } + + public class ProxyFlushBatch : ProxyOutgoingAsyncBase + { + public new static ProxyFlushBatch check(Ice.AsyncResult r, Ice.ObjectPrx prx, string operation) + { + return ProxyOutgoingAsyncBase.check<ProxyFlushBatch>(r, prx, operation); + } + + public ProxyFlushBatch(Ice.ObjectPrxHelperBase prx, string operation, object cookie) : + base(prx, operation, cookie) + { + observer_ = ObserverHelper.get(prx, operation); + _batchRequestNum = prx.getBatchRequestQueue__().swap(os_); + } + + public override bool invokeRemote(Ice.ConnectionI con, bool compress, bool resp, out Ice.AsyncCallback sentCB) + { + if(_batchRequestNum == 0) + { + sentCB = sent(); + return true; + } + cachedConnection_ = con; + return con.sendAsyncRequest(this, compress, false, _batchRequestNum, out sentCB); + } + + public override bool invokeCollocated(CollocatedRequestHandler handler, out Ice.AsyncCallback sentCB) + { + if(_batchRequestNum == 0) + { + sentCB = sent(); + return true; + } + return handler.invokeAsyncRequest(this, _batchRequestNum, false, out sentCB); + } + + public void invoke() + { + Protocol.checkSupportedProtocol(Protocol.getCompatibleProtocol(proxy_.reference__().getProtocol())); + invokeImpl(true); // userThread = true + } + + private int _batchRequestNum; + } + + public class ProxyGetConnection : ProxyOutgoingAsyncBase, Ice.AsyncResult<Ice.Callback_Object_ice_getConnection> + { + public new static ProxyGetConnection check(Ice.AsyncResult r, Ice.ObjectPrx prx, string operation) + { + return ProxyOutgoingAsyncBase.check<ProxyGetConnection>(r, prx, operation); + } + + public ProxyGetConnection(Ice.ObjectPrxHelperBase prx, string operation, + ProxyTwowayCallback<Ice.Callback_Object_ice_getConnection> cb, object cookie) : + base(prx, operation, cookie) + { + observer_ = ObserverHelper.get(prx, operation); + _completed = cb; + } + + public override bool invokeRemote(Ice.ConnectionI con, bool compress, bool resp, out Ice.AsyncCallback sentCB) + { + sentCB = null; + cachedConnection_ = con; + Ice.AsyncCallback cb = finished(true); + if(cb != null) + { + invokeCompletedAsync(cb); + } + return true; + } + + public override bool invokeCollocated(CollocatedRequestHandler handler, out Ice.AsyncCallback sentCB) + { + sentCB = null; + Ice.AsyncCallback cb = finished(true); + if(cb != null) + { + invokeCompletedAsync(cb); + } + return true; + } + + public void invoke() + { + invokeImpl(true); // userThread = true + } + + new public Ice.AsyncResult<Ice.Callback_Object_ice_getConnection> whenCompleted(Ice.ExceptionCallback excb) + { + base.whenCompleted(excb); + return this; + } + + virtual public Ice.AsyncResult<Ice.Callback_Object_ice_getConnection> + whenCompleted(Ice.Callback_Object_ice_getConnection cb, Ice.ExceptionCallback excb) + { + if(cb == null && excb == null) + { + throw new System.ArgumentException("callback is null"); + } + lock(this) + { + if(_responseCallback != null || exceptionCallback_ != null) + { + throw new System.ArgumentException("callback already set"); + } + _responseCallback = cb; + exceptionCallback_ = excb; + } + setCompletedCallback(getCompletedCallback()); + return this; + } + + new public Ice.AsyncResult<Ice.Callback_Object_ice_getConnection> whenSent(Ice.SentCallback cb) + { + base.whenSent(cb); + return this; + } + + protected override Ice.AsyncCallback getCompletedCallback() + { + return (Ice.AsyncResult result) => { _completed(this, _responseCallback, exceptionCallback_); }; + } + + private ProxyTwowayCallback<Ice.Callback_Object_ice_getConnection> _completed; + private Ice.Callback_Object_ice_getConnection _responseCallback = null; + } + + public abstract class OutgoingAsync<T> : OutgoingAsync, Ice.AsyncResult<T> + { + public OutgoingAsync(Ice.ObjectPrxHelperBase prx, string operation, object cookie) : + base(prx, operation, cookie) + { + } + + public OutgoingAsync(Ice.ObjectPrxHelperBase prx, string operation, object cookie, BasicStream iss, + BasicStream os) : + base(prx, operation, cookie, iss, os) + { + } + + new public Ice.AsyncResult<T> whenCompleted(Ice.ExceptionCallback excb) + { + base.whenCompleted(excb); + return this; + } + + virtual public Ice.AsyncResult<T> whenCompleted(T cb, Ice.ExceptionCallback excb) + { + if(cb == null && excb == null) + { + throw new System.ArgumentException("callback is null"); + } + lock(this) + { + if(responseCallback_ != null || exceptionCallback_ != null) + { + throw new System.ArgumentException("callback already set"); + } + responseCallback_ = cb; + exceptionCallback_ = excb; + } + setCompletedCallback(getCompletedCallback()); + return this; + } + + new public Ice.AsyncResult<T> whenSent(Ice.SentCallback cb) + { + base.whenSent(cb); + return this; + } + + protected T responseCallback_; + } + + public class TwowayOutgoingAsync<T> : OutgoingAsync<T> + { + public TwowayOutgoingAsync(Ice.ObjectPrxHelperBase prx, string operation, ProxyTwowayCallback<T> cb, + object cookie) : + base(prx, operation, cookie) + { + Debug.Assert(cb != null); + _completed = cb; + } + + public TwowayOutgoingAsync(Ice.ObjectPrxHelperBase prx, string operation, ProxyTwowayCallback<T> cb, + object cookie, BasicStream iss, BasicStream os) : + base(prx, operation, cookie, iss, os) + { + Debug.Assert(cb != null); + _completed = cb; + } + + override protected Ice.AsyncCallback getCompletedCallback() + { + return (Ice.AsyncResult result) => { _completed(this, responseCallback_, exceptionCallback_); }; + } + + private ProxyTwowayCallback<T> _completed; + } + + public class OnewayOutgoingAsync<T> : OutgoingAsync<T> + { + public OnewayOutgoingAsync(Ice.ObjectPrxHelperBase prx, string operation, ProxyOnewayCallback<T> cb, + object cookie) : + base(prx, operation, cookie) + { + Debug.Assert(cb != null); + _completed = cb; + } + + public OnewayOutgoingAsync(Ice.ObjectPrxHelperBase prx, string operation, ProxyOnewayCallback<T> cb, + object cookie, BasicStream iss, BasicStream os) : + base(prx, operation, cookie, iss, os) + { + Debug.Assert(cb != null); + _completed = cb; + } + + override protected Ice.AsyncCallback getCompletedCallback() + { + return (Ice.AsyncResult result) => + { + try + { + IceInternal.OutgoingAsync outAsync__ = (IceInternal.OutgoingAsync)result; + ((Ice.ObjectPrxHelperBase)(outAsync__.getProxy())).end__(outAsync__, outAsync__.getOperation()); + } + catch(Ice.Exception ex__) + { + if(exceptionCallback_ != null) + { + exceptionCallback_(ex__); + } + return; + } + _completed(responseCallback_); + }; + } + + private ProxyOnewayCallback<T> _completed; + } +} diff --git a/csharp/src/Ice/OutputBase.cs b/csharp/src/Ice/OutputBase.cs new file mode 100644 index 00000000000..a7b74985cd4 --- /dev/null +++ b/csharp/src/Ice/OutputBase.cs @@ -0,0 +1,193 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceUtilInternal +{ + +using System.Collections.Generic; +using System.IO; +using System.Diagnostics; + +public class OutputBase +{ + public + OutputBase() + { + out_ = null; + pos_ = 0; + indent_ = 0; + indentSize_ = 4; + useTab_ = true; + indentSave_ = new Stack<int>(); + separator_ = true; + } + + public + OutputBase(TextWriter writer) + { + out_ = writer; + pos_ = 0; + indent_ = 0; + indentSize_ = 4; + useTab_ = true; + indentSave_ = new Stack<int>(); + separator_ = true; + } + + public + OutputBase(string s) + { + out_ = new StreamWriter(s); + pos_ = 0; + indent_ = 0; + indentSize_ = 4; + useTab_ = true; + indentSave_ = new Stack<int>(); + separator_ = true; + } + + virtual public void + setIndent(int indentSize) + { + indentSize_ = indentSize; + } + + virtual public void + setUseTab(bool useTab) + { + useTab_ = useTab; + } + + public virtual void + open(string s) + { + try + { + out_ = new StreamWriter(s); + } + catch(IOException) + { + } + } + + public virtual void + print(string s) + { + char[] arr = s.ToCharArray(); + for(int i = 0; i < arr.Length; i++) + { + if(arr[i] == '\n') + { + pos_ = 0; + } + else + { + } + } + + out_.Write(s); + } + + public virtual void + inc() + { + indent_ += indentSize_; + } + + public virtual void + dec() + { + Debug.Assert(indent_ >= indentSize_); + indent_ -= indentSize_; + } + + public virtual void + useCurrentPosAsIndent() + { + indentSave_.Push(indent_); + indent_ = pos_; + } + + public virtual void + zeroIndent() + { + indentSave_.Push(indent_); + indent_ = 0; + } + + public virtual void + restoreIndent() + { + Debug.Assert(indentSave_.Count != 0); + indent_ = (int)indentSave_.Pop(); + } + + public virtual void + nl() + { + out_.WriteLine(); + pos_ = 0; + separator_ = true; + + int indent = indent_; + + if(useTab_) + { + while(indent >= 8) + { + indent -= 8; + out_.Write('\t'); + pos_ += 8; + } + } + else + { + while(indent >= indentSize_) + { + indent -= indentSize_; + out_.Write(" "); + pos_ += indentSize_; + } + } + + while(indent > 0) + { + --indent; + out_.Write(" "); + ++pos_; + } + + out_.Flush(); + } + + public virtual void + sp() + { + if(separator_) + { + out_.WriteLine(); + } + } + + public virtual bool + valid() + { + return out_ != null; + } + + protected internal TextWriter out_; + protected internal int pos_; + protected internal int indent_; + protected internal int indentSize_; + protected internal Stack<int> indentSave_; + protected internal bool useTab_; + protected internal bool separator_; +} + +} diff --git a/csharp/src/Ice/Patcher.cs b/csharp/src/Ice/Patcher.cs new file mode 100644 index 00000000000..ae53b6a4692 --- /dev/null +++ b/csharp/src/Ice/Patcher.cs @@ -0,0 +1,302 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Diagnostics; + +namespace IceInternal +{ + public interface IPatcher + { + void patch(Ice.Object v); + string type(); + } + + public abstract class Patcher : IPatcher, Ice.ReadObjectCallback + { + public Patcher(string type) + { + _type = type; + } + + public abstract void patch(Ice.Object v); + + public virtual string type() + { + return _type; + } + + public virtual void invoke(Ice.Object v) + { + patch(v); + } + + private string _type; + } + + public sealed class ParamPatcher<T> : Patcher + { + public ParamPatcher(string type) : base(type) + { + } + + public override void patch(Ice.Object v) + { + if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) + { + IceInternal.Ex.throwUOE(type(), v.ice_id()); + } + value = (T)v; + } + + public T value; + } + + public sealed class CustomSeqPatcher<T> : Patcher + { + public CustomSeqPatcher(string type, IEnumerable<T> seq, int index) : base(type) + { + _seq = seq; + _seqType = seq.GetType(); + _index = index; + + setInvokeInfo(_seqType); + } + + public override void patch(Ice.Object v) + { + if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) + { + IceInternal.Ex.throwUOE(type(), v.ice_id()); + } + + InvokeInfo info = getInvokeInfo(_seqType); + int count = info.getCount(_seq); + if(_index >= count) // Need to grow the sequence. + { + for(int i = count; i < _index; i++) + { + info.invokeAdd(_seq, default(T)); + } + info.invokeAdd(_seq, (T)v); + } + else + { + info.invokeSet(_seq, _index, (T)v); + } + } + + private static InvokeInfo getInvokeInfo(Type t) + { + lock(_methodTable) + { + try + { + return _methodTable[t]; + } + catch(KeyNotFoundException) + { + throw new Ice.MarshalException("No invoke record for type " + t.ToString()); + } + } + } + + private static void setInvokeInfo(Type t) + { + lock(_methodTable) + { + if(_methodTable.ContainsKey(t)) + { + return; + } + + MethodInfo am = t.GetMethod("Add", _params); + if(am == null) + { + throw new Ice.MarshalException("Cannot patch a collection without an Add() method"); + } + + PropertyInfo pi = t.GetProperty("Item"); + if(pi == null) + { + throw new Ice.MarshalException("Cannot patch a collection without an indexer"); + } + MethodInfo sm = pi.GetSetMethod(); + if(sm == null) + { + throw new Ice.MarshalException("Cannot patch a collection without an indexer to set a value"); + } + + pi = t.GetProperty("Count"); + if(pi == null) + { + throw new Ice.MarshalException("Cannot patch a collection without a Count property"); + } + MethodInfo cm = pi.GetGetMethod(); + if(cm == null) + { + throw new Ice.MarshalException("Cannot patch a collection without a readable Count property"); + } + + _methodTable.Add(t, new InvokeInfo(am, sm, cm)); + } + } + + private class InvokeInfo + { + public InvokeInfo(MethodInfo am, MethodInfo sm, MethodInfo cm) + { + _addMethod = am; + _setMethod = sm; + _countMethod = cm; + } + + internal int getCount(System.Collections.IEnumerable seq) + { + try + { + return (int)_countMethod.Invoke(seq, null); + } + catch(Exception ex) + { + throw new Ice.MarshalException("Could not read Count property during patching", ex); + } + } + + internal void invokeAdd(System.Collections.IEnumerable seq, T v) + { + try + { + object[] arg = new object[] { v }; + _addMethod.Invoke(seq, arg); + } + catch(Exception ex) + { + throw new Ice.MarshalException("Could not invoke Add method during patching", ex); + } + } + + internal void invokeSet(System.Collections.IEnumerable seq, int index, T v) + { + try + { + object[] args = new object[] { index, v }; + _setMethod.Invoke(seq, args); + } + catch(Exception ex) + { + throw new Ice.MarshalException("Could not call indexer during patching", ex); + } + } + + private MethodInfo _addMethod; + private MethodInfo _setMethod; + private MethodInfo _countMethod; + } + + private static Type[] _params = new Type[] { typeof(T) }; + private static Dictionary<Type, InvokeInfo> _methodTable = new Dictionary<Type, InvokeInfo>(); + + private IEnumerable<T> _seq; + private Type _seqType; + private int _index; // The index at which to patch the sequence. + } + + public sealed class ArrayPatcher<T> : Patcher + { + public ArrayPatcher(string type, T[] seq, int index) : base(type) + { + _seq = seq; + _index = index; + } + + public override void patch(Ice.Object v) + { + if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) + { + IceInternal.Ex.throwUOE(type(), v.ice_id()); + } + + _seq[_index] = (T)v; + } + + private T[] _seq; + private int _index; // The index at which to patch the array. + } + + public sealed class SequencePatcher<T> : Patcher + { + public SequencePatcher(string type, IceInternal.CollectionBase<T> seq, int index) : base(type) + { + _seq = seq; + _index = index; + } + + public override void patch(Ice.Object v) + { + if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) + { + IceInternal.Ex.throwUOE(type(), v.ice_id()); + } + + int count = _seq.Count; + if(_index >= count) // Need to grow the sequence. + { + for(int i = count; i < _index; i++) + { + _seq.Add(default(T)); + } + _seq.Add((T)v); + } + else + { + _seq[_index] = (T)v; + } + } + + private IceInternal.CollectionBase<T> _seq; + private int _index; // The index at which to patch the sequence. + } + + public sealed class ListPatcher<T> : Patcher + { + public ListPatcher(string type, List<T> seq, int index) : base(type) + { + _seq = seq; + _index = index; + } + + public override void patch(Ice.Object v) + { + if(v != null && !typeof(T).IsAssignableFrom(v.GetType())) + { + IceInternal.Ex.throwUOE(type(), v.ice_id()); + } + + int count = _seq.Count; + if(_index >= count) // Need to grow the sequence. + { + for(int i = count; i < _index; i++) + { + _seq.Add(default(T)); + } + _seq.Add((T)v); + } + else + { + _seq[_index] = (T)v; + } + } + + private List<T> _seq; + private int _index; // The index at which to patch the sequence. + } +} diff --git a/csharp/src/Ice/PluginManagerI.cs b/csharp/src/Ice/PluginManagerI.cs new file mode 100644 index 00000000000..3069c1e1488 --- /dev/null +++ b/csharp/src/Ice/PluginManagerI.cs @@ -0,0 +1,530 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#if !SILVERLIGHT +namespace Ice +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + + /// <summary> + /// Applications implement this interface to provide a plug-in factory + /// to the Ice run time. + /// </summary> + public interface PluginFactory + { + /// <summary> + /// Called by the Ice run time to create a new plug-in. + /// </summary> + /// + /// <param name="communicator">The communicator that is in the process of being initialized.</param> + /// <param name="name">The name of the plug-in.</param> + /// <param name="args">The arguments that are specified in the plug-ins configuration.</param> + /// <returns>The plug-in that was created by this method.</returns> + Plugin create(Communicator communicator, string name, string[] args); + } + + public sealed class PluginManagerI : PluginManager + { + private static string _kindOfObject = "plugin"; + + public void initializePlugins() + { + if(_initialized) + { + InitializationException ex = new InitializationException(); + ex.reason = "plug-ins already initialized"; + throw ex; + } + + // + // Invoke initialize() on the plug-ins, in the order they were loaded. + // + ArrayList initializedPlugins = new ArrayList(); + try + { + foreach(PluginInfo p in _plugins) + { + p.plugin.initialize(); + initializedPlugins.Add(p.plugin); + } + } + catch(System.Exception) + { + // + // Destroy the plug-ins that have been successfully initialized, in the + // reverse order. + // + initializedPlugins.Reverse(); + foreach(Plugin p in initializedPlugins) + { + try + { + p.destroy(); + } + catch(System.Exception) + { + // Ignore. + } + } + throw; + } + + _initialized = true; + } + + public string[] getPlugins() + { + lock(this) + { + ArrayList names = new ArrayList(); + foreach(PluginInfo p in _plugins) + { + names.Add(p.name); + } + return (string[])names.ToArray(typeof(string)); + } + } + + public Plugin getPlugin(string name) + { + lock(this) + { + if(_communicator == null) + { + throw new CommunicatorDestroyedException(); + } + + Plugin p = findPlugin(name); + if(p != null) + { + return p; + } + + NotRegisteredException ex = new NotRegisteredException(); + ex.id = name; + ex.kindOfObject = _kindOfObject; + throw ex; + } + } + + public void addPlugin(string name, Plugin plugin) + { + lock(this) + { + if(_communicator == null) + { + throw new CommunicatorDestroyedException(); + } + + if(findPlugin(name) != null) + { + AlreadyRegisteredException ex = new AlreadyRegisteredException(); + ex.id = name; + ex.kindOfObject = _kindOfObject; + throw ex; + } + + PluginInfo info = new PluginInfo(); + info.name = name; + info.plugin = plugin; + _plugins.Add(info); + } + } + + public void destroy() + { + lock(this) + { + if(_communicator != null) + { + if(_initialized) + { + ArrayList plugins = (ArrayList)_plugins.Clone(); + plugins.Reverse(); + foreach(PluginInfo p in plugins) + { + try + { + p.plugin.destroy(); + } + catch(System.Exception ex) + { + Ice.Util.getProcessLogger().warning("unexpected exception raised by plug-in `" + + p.name + "' destruction:\n" + ex.ToString()); + } + } + } + + _communicator = null; + } + } + } + + public PluginManagerI(Communicator communicator) + { + _communicator = communicator; + _plugins = new ArrayList(); + _initialized = false; + } + + public void loadPlugins(ref string[] cmdArgs) + { + Debug.Assert(_communicator != null); + + // + // Load and initialize the plug-ins defined in the property set + // with the prefix "Ice.Plugin.". These properties should + // have the following format: + // + // Ice.Plugin.name[.<language>]=entry_point [args] + // + // The code below is different from the Java/C++ algorithm + // because C# must support full assembly names such as: + // + // Ice.Plugin.Logger=logger, Version=0.0.0.0, Culture=neutral:LoginPluginFactory + // + // If the Ice.PluginLoadOrder property is defined, load the + // specified plug-ins in the specified order, then load any + // remaining plug-ins. + // + string prefix = "Ice.Plugin."; + Properties properties = _communicator.getProperties(); + Dictionary<string, string> plugins = properties.getPropertiesForPrefix(prefix); + + string[] loadOrder = properties.getPropertyAsList("Ice.PluginLoadOrder"); + for(int i = 0; i < loadOrder.Length; ++i) + { + if(loadOrder[i].Length == 0) + { + continue; + } + + if(findPlugin(loadOrder[i]) != null) + { + PluginInitializationException e = new PluginInitializationException(); + e.reason = "plug-in `" + loadOrder[i] + "' already loaded"; + throw e; + } + + string key = "Ice.Plugin." + loadOrder[i] + ".clr"; + bool hasKey = plugins.ContainsKey(key); + if(hasKey) + { + plugins.Remove("Ice.Plugin." + loadOrder[i]); + } + else + { + key = "Ice.Plugin." + loadOrder[i]; + hasKey = plugins.ContainsKey(key); + } + + if(hasKey) + { + string value = plugins[key]; + loadPlugin(loadOrder[i], value, ref cmdArgs); + plugins.Remove(key); + } + else + { + PluginInitializationException e = new PluginInitializationException(); + e.reason = "plug-in `" + loadOrder[i] + "' not defined"; + throw e; + } + } + + // + // Load any remaining plug-ins that weren't specified in PluginLoadOrder. + // + while(plugins.Count > 0) + { + IEnumerator<KeyValuePair<string, string>> p = plugins.GetEnumerator(); + p.MoveNext(); + string key = p.Current.Key; + string val = p.Current.Value; + string name = key.Substring(prefix.Length); + + int dotPos = name.LastIndexOf('.'); + if(dotPos != -1) + { + string suffix = name.Substring(dotPos + 1); + if(suffix.Equals("cpp") || suffix.Equals("java")) + { + // + // Ignored + // + plugins.Remove(key); + } + else if(suffix.Equals("clr")) + { + name = name.Substring(0, dotPos); + loadPlugin(name, val, ref cmdArgs); + plugins.Remove(key); + plugins.Remove("Ice.Plugin." + name); + + } + else + { + // + // Name is just a regular name that happens to contain a dot + // + dotPos = -1; + } + } + + if(dotPos == -1) + { + plugins.Remove(key); + + // + // Is there a .clr entry? + // + string clrKey = "Ice.Plugin." + name + ".clr"; + if(plugins.ContainsKey(clrKey)) + { + val = plugins[clrKey]; + plugins.Remove(clrKey); + } + loadPlugin(name, val, ref cmdArgs); + } + } + } + + private void loadPlugin(string name, string pluginSpec, ref string[] cmdArgs) + { + Debug.Assert(_communicator != null); + + // + // Split the entire property value into arguments. An entry point containing spaces + // must be enclosed in quotes. + // + string[] args = null; + try + { + args = IceUtilInternal.Options.split(pluginSpec); + } + catch(IceUtilInternal.Options.BadQuote ex) + { + PluginInitializationException e = new PluginInitializationException(); + e.reason = "invalid arguments for plug-in `" + name + "':\n" + ex.Message; + throw e; + } + + Debug.Assert(args.Length > 0); + + string entryPoint = args[0]; + + // + // Shift the arguments. + // + string[] tmp = new string[args.Length - 1]; + Array.Copy(args, 1, tmp, 0, args.Length - 1); + args = tmp; + + // + // Convert command-line options into properties. First + // we convert the options from the plug-in + // configuration, then we convert the options from the + // application command-line. + // + Properties properties = _communicator.getProperties(); + args = properties.parseCommandLineOptions(name, args); + cmdArgs = properties.parseCommandLineOptions(name, cmdArgs); + + // + // Extract the assembly name and the class name. + // + string err = "unable to load plug-in `" + entryPoint + "': "; + int sepPos = entryPoint.IndexOf(':'); + if(sepPos != -1 && IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows) + { + const string driveLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if(entryPoint.Length > 3 && + sepPos == 1 && + driveLetters.IndexOf(entryPoint[0]) != -1 && + (entryPoint[2] == '\\' || entryPoint[2] == '/')) + { + sepPos = entryPoint.IndexOf(':', 3); + } + } + if(sepPos == -1) + { + PluginInitializationException e = new PluginInitializationException(); + e.reason = err + "invalid entry point format"; + throw e; + } + + System.Reflection.Assembly pluginAssembly = null; + string assemblyName = entryPoint.Substring(0, sepPos); + string className = entryPoint.Substring(sepPos + 1); + + try + { + // + // First try to load the assembly using Assembly.Load, which will succeed + // if a fully-qualified name is provided or if a partial name has been qualified + // in configuration. If that fails, try Assembly.LoadFrom(), which will succeed + // if a file name is configured or a partial name is configured and DEVPATH is used. + // + try + { + pluginAssembly = System.Reflection.Assembly.Load(assemblyName); + } + catch(System.IO.IOException ex) + { + try + { + pluginAssembly = System.Reflection.Assembly.LoadFrom(assemblyName); + } + catch(System.IO.IOException) + { + throw ex; + } + } + } + catch(System.Exception ex) + { +#if COMPACT + // + // IceSSL is not supported with the Compact Framework. + // + if(name == "IceSSL") + { + if(!_sslWarnOnce) + { + _communicator.getLogger().warning( + "IceSSL plug-in not loaded: IceSSL is not supported with the .NET Compact Framework"); + _sslWarnOnce = true; + } + return; + } +#else + // + // IceSSL is not yet supported with Mono. We avoid throwing an exception in that case, + // so the same configuration can be used with Mono or Visual C#. + // + if(IceInternal.AssemblyUtil.runtime_ == IceInternal.AssemblyUtil.Runtime.Mono && name == "IceSSL") + { + if(!_sslWarnOnce) + { + _communicator.getLogger().warning( + "IceSSL plug-in not loaded: IceSSL is not supported with Mono"); + _sslWarnOnce = true; + } + return; + } +#endif + + PluginInitializationException e = new PluginInitializationException(); + e.reason = err + "unable to load assembly: `" + assemblyName + "': " + ex.ToString(); + throw e; + } + + // + // Instantiate the class. + // + PluginFactory pluginFactory = null; + System.Type c = null; + try + { + c = pluginAssembly.GetType(className, true); + } + catch(System.Exception ex) + { + PluginInitializationException e = new PluginInitializationException(ex); + e.reason = err + "GetType failed for `" + className + "'"; + throw e; + } + + try + { + pluginFactory = (PluginFactory)IceInternal.AssemblyUtil.createInstance(c); + if(pluginFactory == null) + { + PluginInitializationException e = new PluginInitializationException(); + e.reason = err + "can't find constructor for `" + className + "'"; + throw e; + } + } + catch(System.InvalidCastException ex) + { + PluginInitializationException e = new PluginInitializationException(ex); + e.reason = err + "InvalidCastException to Ice.PluginFactory"; + throw e; + } + catch(System.UnauthorizedAccessException ex) + { + PluginInitializationException e = new PluginInitializationException(ex); + e.reason = err + "UnauthorizedAccessException: " + ex.ToString(); + throw e; + } + catch(System.Exception ex) + { + PluginInitializationException e = new PluginInitializationException(ex); + e.reason = err + "System.Exception: " + ex.ToString(); + throw e; + } + + Plugin plugin = null; + try + { + plugin = pluginFactory.create(_communicator, name, args); + } + catch(PluginInitializationException ex) + { + ex.reason = err + ex.reason; + throw ex; + } + catch(System.Exception ex) + { + PluginInitializationException e = new PluginInitializationException(ex); + e.reason = err + "System.Exception in factory.create: " + ex.ToString(); + throw e; + } + + if(plugin == null) + { + PluginInitializationException ex = new PluginInitializationException(); + ex.reason = err + "factory.create returned null plug-in"; + throw ex; + } + + PluginInfo info = new PluginInfo(); + info.name = name; + info.plugin = plugin; + _plugins.Add(info); + } + + private Plugin findPlugin(string name) + { + foreach(PluginInfo p in _plugins) + { + if(name.Equals(p.name)) + { + return p.plugin; + } + } + return null; + } + + internal class PluginInfo + { + internal string name; + internal Plugin plugin; + } + + private Communicator _communicator; + private ArrayList _plugins; + private bool _initialized; + private static bool _sslWarnOnce = false; + } +} +#endif diff --git a/csharp/src/Ice/ProcessI.cs b/csharp/src/Ice/ProcessI.cs new file mode 100644 index 00000000000..a4e2b9f6300 --- /dev/null +++ b/csharp/src/Ice/ProcessI.cs @@ -0,0 +1,43 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + public sealed class ProcessI : Ice.ProcessDisp_ + { + public ProcessI(Ice.Communicator communicator) + { + _communicator = communicator; + } + + public override void shutdown(Ice.Current current) + { + _communicator.shutdown(); + } + + public override void writeMessage(string message, int fd, Ice.Current current) + { + switch(fd) + { + case 1: + { + System.Console.Out.WriteLine(message); + break; + } + case 2: + { + System.Console.Error.WriteLine(message); + break; + } + } + } + + private Ice.Communicator _communicator; + } +} diff --git a/csharp/src/Ice/PropertiesAdminI.cs b/csharp/src/Ice/PropertiesAdminI.cs new file mode 100644 index 00000000000..7578cf1938f --- /dev/null +++ b/csharp/src/Ice/PropertiesAdminI.cs @@ -0,0 +1,240 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections.Generic; + +namespace Ice +{ + +public interface PropertiesAdminUpdateCallback +{ + void updated(Dictionary<string, string> changes); +} + +public interface NativePropertiesAdmin +{ + void addUpdateCallback(PropertiesAdminUpdateCallback callback); + void removeUpdateCallback(PropertiesAdminUpdateCallback callback); +} + +} + +namespace IceInternal +{ + sealed class PropertiesAdminI : Ice.PropertiesAdminDisp_, Ice.NativePropertiesAdmin + { + internal PropertiesAdminI(Ice.Properties properties, Ice.Logger logger) + { + _properties = properties; + _logger = logger; + } + + public override string + getProperty(string name, Ice.Current current) + { + return _properties.getProperty(name); + } + + public override Dictionary<string, string> + getPropertiesForPrefix(string name, Ice.Current current) + { + return _properties.getPropertiesForPrefix(name); + } + + public override void setProperties_async(Ice.AMD_PropertiesAdmin_setProperties cb, + Dictionary<string, string> props, + Ice.Current current) + { + lock(this) + { + Dictionary<string, string> old = _properties.getPropertiesForPrefix(""); + int traceLevel = _properties.getPropertyAsInt("Ice.Trace.Admin.Properties"); + + // + // Compute the difference between the new property set and the existing property set: + // + // 1) Any properties in the new set that were not defined in the existing set. + // + // 2) Any properties that appear in both sets but with different values. + // + // 3) Any properties not present in the new set but present in the existing set. + // In other words, the property has been removed. + // + Dictionary<string, string> added = new Dictionary<string, string>(); + Dictionary<string, string> changed = new Dictionary<string, string>(); + Dictionary<string, string> removed = new Dictionary<string, string>(); + foreach(KeyValuePair<string, string> e in props) + { + string key = e.Key; + string value = e.Value; + if(!old.ContainsKey(key)) + { + if(value.Length > 0) + { + // + // This property is new. + // + added.Add(key, value); + } + } + else + { + string v; + if(!old.TryGetValue(key, out v) || !value.Equals(v)) + { + if(value.Length == 0) + { + // + // This property was removed. + // + removed.Add(key, value); + } + else + { + // + // This property has changed. + // + changed.Add(key, value); + } + } + + old.Remove(key); + } + } + + if(traceLevel > 0 && (added.Count > 0 || changed.Count > 0 || removed.Count > 0)) + { + System.Text.StringBuilder message = new System.Text.StringBuilder("Summary of property changes"); + + if(added.Count > 0) + { + message.Append("\nNew properties:"); + foreach(KeyValuePair<string, string> e in added) + { + message.Append("\n "); + message.Append(e.Key); + if(traceLevel > 1) + { + message.Append(" = "); + message.Append(e.Value); + } + } + } + + if(changed.Count > 0) + { + message.Append("\nChanged properties:"); + foreach(KeyValuePair<string, string> e in changed) + { + message.Append("\n "); + message.Append(e.Key); + if(traceLevel > 1) + { + message.Append(" = "); + message.Append(e.Value); + message.Append(" (old value = "); + message.Append(_properties.getProperty(e.Key)); + message.Append(")"); + } + } + } + + if(removed.Count > 0) + { + message.Append("\nRemoved properties:"); + foreach(KeyValuePair<string, string> e in removed) + { + message.Append("\n "); + message.Append(e.Key); + } + } + + _logger.trace(_traceCategory, message.ToString()); + } + + // + // Update the property set. + // + + foreach(KeyValuePair<string, string> e in added) + { + _properties.setProperty(e.Key, e.Value); + } + + foreach(KeyValuePair<string, string> e in changed) + { + _properties.setProperty(e.Key, e.Value); + } + + foreach(KeyValuePair<string, string> e in removed) + { + _properties.setProperty(e.Key, ""); + } + + // + // Send the response now so that we do not block the client during the call to the update callback. + // + cb.ice_response(); + + if(_updateCallbacks.Count > 0) + { + // + // Copy the callbacks to allow callbacks to update the callbacks. + // + List<Ice.PropertiesAdminUpdateCallback> callbacks = + new List<Ice.PropertiesAdminUpdateCallback>(_updateCallbacks); + + Dictionary<string, string> changes = new Dictionary<string, string>(added); + foreach(KeyValuePair<string, string> e in changed) + { + changes.Add(e.Key, e.Value); + } + foreach(KeyValuePair<string, string> e in removed) + { + changes.Add(e.Key, e.Value); + } + + foreach(Ice.PropertiesAdminUpdateCallback callback in callbacks) + { + try + { + callback.updated(changes); + } + catch(System.Exception) + { + // Ignore. + } + } + } + } + } + + public void addUpdateCallback(Ice.PropertiesAdminUpdateCallback cb) + { + lock(this) + { + _updateCallbacks.Add(cb); + } + } + + public void removeUpdateCallback(Ice.PropertiesAdminUpdateCallback cb) + { + lock(this) + { + _updateCallbacks.Remove(cb); + } + } + + private readonly Ice.Properties _properties; + private readonly Ice.Logger _logger; + private List<Ice.PropertiesAdminUpdateCallback> _updateCallbacks = new List<Ice.PropertiesAdminUpdateCallback>(); + + private static readonly string _traceCategory = "Admin.Properties"; + } +} diff --git a/csharp/src/Ice/PropertiesI.cs b/csharp/src/Ice/PropertiesI.cs new file mode 100644 index 00000000000..0ca06bddd9e --- /dev/null +++ b/csharp/src/Ice/PropertiesI.cs @@ -0,0 +1,692 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Globalization; +using Microsoft.Win32; + +namespace Ice +{ + sealed class PropertiesI : Properties + { + class PropertyValue + { + public PropertyValue(PropertyValue v) + { + val = v.val; + used = v.used; + } + + public PropertyValue(string v, bool u) + { + val = v; + used = u; + } + + public string val; + public bool used; + } + + public string getProperty(string key) + { + lock(this) + { + string result = ""; + PropertyValue pv; + if(_properties.TryGetValue(key, out pv)) + { + pv.used = true; + result = pv.val; + } + return result; + } + } + + public string getPropertyWithDefault(string key, string val) + { + lock(this) + { + string result = val; + PropertyValue pv; + if(_properties.TryGetValue(key, out pv)) + { + pv.used = true; + result = pv.val; + } + return result; + } + } + + public int getPropertyAsInt(string key) + { + return getPropertyAsIntWithDefault(key, 0); + } + + public int getPropertyAsIntWithDefault(string key, int val) + { + lock(this) + { + PropertyValue pv; + if(!_properties.TryGetValue(key, out pv)) + { + return val; + } + pv.used = true; + try + { + return System.Int32.Parse(pv.val, CultureInfo.InvariantCulture); + } + catch(System.FormatException) + { + Ice.Util.getProcessLogger().warning("numeric property " + key + + " set to non-numeric value, defaulting to " + val); + return val; + } + } + } + + public string[] getPropertyAsList(string key) + { + return getPropertyAsListWithDefault(key, null); + } + + public string[] getPropertyAsListWithDefault(string key, string[] val) + { + if(val == null) + { + val = new string[0]; + } + + lock(this) + { + PropertyValue pv; + if(!_properties.TryGetValue(key, out pv)) + { + return val; + } + + pv.used = true; + + string[] result = IceUtilInternal.StringUtil.splitString(pv.val, ", \t\r\n"); + if(result == null) + { + Ice.Util.getProcessLogger().warning("mismatched quotes in property " + key + + "'s value, returning default value"); + return val; + } + else + { + return result; + } + } + } + + + public Dictionary<string, string> getPropertiesForPrefix(string prefix) + { + lock(this) + { + Dictionary<string, string> result = new Dictionary<string, string>(); + + foreach(string s in _properties.Keys) + { + if(prefix.Length == 0 || s.StartsWith(prefix, StringComparison.Ordinal)) + { + PropertyValue pv = (PropertyValue)_properties[s]; + pv.used = true; + result[s] = pv.val; + } + } + return result; + } + } + + public void setProperty(string key, string val) + { + // + // Trim whitespace + // + if(key != null) + { + key = key.Trim(); + } + if(key == null || key.Length == 0) + { + throw new Ice.InitializationException("Attempt to set property with empty key"); + } + + // + // Check if the property is legal. + // + Logger logger = Ice.Util.getProcessLogger(); + int dotPos = key.IndexOf('.'); + if(dotPos != -1) + { + string prefix = key.Substring(0, dotPos); + for(int i = 0; IceInternal.PropertyNames.validProps[i] != null; ++i) + { + string pattern = IceInternal.PropertyNames.validProps[i][0].pattern(); + dotPos = pattern.IndexOf('.'); + Debug.Assert(dotPos != -1); + string propPrefix = pattern.Substring(1, dotPos - 2); + bool mismatchCase = false; + string otherKey = ""; + if(!propPrefix.ToUpper().Equals(prefix.ToUpper())) + { + continue; + } + + bool found = false; + for(int j = 0; IceInternal.PropertyNames.validProps[i][j] != null && !found; ++j) + { + Regex r = new Regex(IceInternal.PropertyNames.validProps[i][j].pattern()); + Match m = r.Match(key); + found = m.Success; + if(found && IceInternal.PropertyNames.validProps[i][j].deprecated()) + { + logger.warning("deprecated property: " + key); + if(IceInternal.PropertyNames.validProps[i][j].deprecatedBy() != null) + { + key = IceInternal.PropertyNames.validProps[i][j].deprecatedBy(); + } + } + + if(!found) + { + r = new Regex(IceInternal.PropertyNames.validProps[i][j].pattern().ToUpper()); + m = r.Match(key.ToUpper()); + if(m.Success) + { + found = true; + mismatchCase = true; + otherKey = IceInternal.PropertyNames.validProps[i][j].pattern().Replace("\\", ""). + Replace("^", ""). + Replace("$", ""); + break; + } + } + } + if(!found) + { + logger.warning("unknown property: " + key); + } + else if(mismatchCase) + { + logger.warning("unknown property: `" + key + "'; did you mean `" + otherKey + "'"); + } + } + } + + lock(this) + { + // + // + // Set or clear the property. + // + if(val != null && val.Length > 0) + { + PropertyValue pv; + if(_properties.TryGetValue(key, out pv)) + { + pv.val = val; + } + else + { + pv = new PropertyValue(val, false); + } + _properties[key] = pv; + } + else + { + _properties.Remove(key); + } + } + } + + public string[] getCommandLineOptions() + { + lock(this) + { + string[] result = new string[_properties.Count]; + int i = 0; + foreach(KeyValuePair<string, PropertyValue> entry in _properties) + { + result[i++] = "--" + entry.Key + "=" + entry.Value.val; + } + return result; + } + } + + public string[] parseCommandLineOptions(string pfx, string[] options) + { + if(pfx.Length > 0 && pfx[pfx.Length - 1] != '.') + { + pfx += '.'; + } + pfx = "--" + pfx; + + List<string> result = new List<string>(); + for(int i = 0; i < options.Length; i++) + { + string opt = options[i]; + if(opt.StartsWith(pfx, StringComparison.Ordinal)) + { + if(opt.IndexOf('=') == -1) + { + opt += "=1"; + } + + parseLine(opt.Substring(2)); + } + else + { + result.Add(opt); + } + } + string[] arr = new string[result.Count]; + if(arr.Length != 0) + { + result.CopyTo(arr); + } + return arr; + } + + public string[] parseIceCommandLineOptions(string[] options) + { + string[] args = options; + for(int i = 0; IceInternal.PropertyNames.clPropNames[i] != null; ++i) + { + args = parseCommandLineOptions(IceInternal.PropertyNames.clPropNames[i], args); + } + return args; + } + + public void load(string file) + { +#if UNITY + throw new FeatureNotSupportedException("File I/O not supported in UNITY build"); +#else +# if !SILVERLIGHT + if(IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows && + (file.StartsWith("HKLM\\", StringComparison.Ordinal))) + { + RegistryKey iceKey = Registry.LocalMachine.OpenSubKey(file.Substring(5)); + if(iceKey == null) + { + Ice.InitializationException ex = new Ice.InitializationException(); + ex.reason = "Could not open Windows registry key `" + file + "'"; + throw ex; + } + + foreach(string propKey in iceKey.GetValueNames()) + { + RegistryValueKind kind = iceKey.GetValueKind(propKey); + if(kind == RegistryValueKind.String || kind == RegistryValueKind.ExpandString) + { + setProperty(propKey, iceKey.GetValue(propKey).ToString()); + } + } + } + else + { +# endif + try + { + using(System.IO.StreamReader sr = new System.IO.StreamReader(file)) + { + parse(sr); + } + } + catch(System.IO.IOException ex) + { + Ice.FileException fe = new Ice.FileException(ex); + fe.path = file; + throw fe; + } +# if !SILVERLIGHT + } +# endif +#endif + } + + public Properties ice_clone_() + { + lock(this) + { + return new PropertiesI(this); + } + } + + public List<string> getUnusedProperties() + { + lock(this) + { + List<string> unused = new List<string>(); + foreach(KeyValuePair<string, PropertyValue> entry in _properties) + { + if(!entry.Value.used) + { + unused.Add(entry.Key); + } + } + return unused; + } + } + + internal PropertiesI(PropertiesI p) + { + // + // NOTE: we can't just do a shallow copy of the map as the map values + // would otherwise be shared between the two PropertiesI object. + // + //_properties = new Dictionary<string, PropertyValue>(p._properties); + _properties = new Dictionary<string, PropertyValue>(); + foreach(KeyValuePair<string, PropertyValue> entry in p._properties) + { + _properties[entry.Key] = new PropertyValue(entry.Value); + } + } + + internal PropertiesI() + { + _properties = new Dictionary<string, PropertyValue>(); + } + + internal PropertiesI(ref string[] args, Properties defaults) + { + if(defaults == null) + { + _properties = new Dictionary<string, PropertyValue>(); + } + else + { + // + // NOTE: we can't just do a shallow copy of the map as the map values + // would otherwise be shared between the two PropertiesI object. + // + //_properties = ((PropertiesI)defaults)._properties; + _properties = new Dictionary<string, PropertyValue>(); + foreach(KeyValuePair<string, PropertyValue> entry in ((PropertiesI)defaults)._properties) + { + _properties[entry.Key] = new PropertyValue(entry.Value); + } + } + + PropertyValue pv; + if(_properties.TryGetValue("Ice.ProgramName", out pv)) + { + pv.used = true; + } + else + { + _properties["Ice.ProgramName"] = new PropertyValue(System.AppDomain.CurrentDomain.FriendlyName, true); + } + + bool loadConfigFiles = false; + + for(int i = 0; i < args.Length; i++) + { + if(args[i].StartsWith("--Ice.Config", StringComparison.Ordinal)) + { + string line = args[i]; + if(line.IndexOf('=') == -1) + { + line += "=1"; + } + parseLine(line.Substring(2)); + loadConfigFiles = true; + + string[] arr = new string[args.Length - 1]; + System.Array.Copy(args, 0, arr, 0, i); + if(i < args.Length - 1) + { + System.Array.Copy(args, i + 1, arr, i, args.Length - i - 1); + } + args = arr; + } + } + + if(!loadConfigFiles) + { + // + // If Ice.Config is not set, load from ICE_CONFIG (if set) + // + loadConfigFiles = !_properties.ContainsKey("Ice.Config"); + } + + if(loadConfigFiles) + { + loadConfig(); + } + + args = parseIceCommandLineOptions(args); + } + + private void parse(System.IO.StreamReader input) + { + try + { + string line; + while((line = input.ReadLine()) != null) + { + parseLine(line); + } + } + catch(System.IO.IOException ex) + { + SyscallException se = new SyscallException(ex); + throw se; + } + } + + private const int ParseStateKey = 0; + private const int ParseStateValue = 1; + + private void parseLine(string line) + { + string key = ""; + string val = ""; + + int state = ParseStateKey; + + string whitespace = ""; + string escapedspace = ""; + bool finished = false; + for(int i = 0; i < line.Length; ++i) + { + char c = line[i]; + switch(state) + { + case ParseStateKey: + { + switch(c) + { + case '\\': + if(i < line.Length - 1) + { + c = line[++i]; + switch(c) + { + case '\\': + case '#': + case '=': + key += whitespace; + whitespace= ""; + key += c; + break; + + case ' ': + if(key.Length != 0) + { + whitespace += c; + } + break; + + default: + key += whitespace; + whitespace= ""; + key += '\\'; + key += c; + break; + } + } + else + { + key += whitespace; + key += c; + } + break; + + case ' ': + case '\t': + case '\r': + case '\n': + if(key.Length != 0) + { + whitespace += c; + } + break; + + case '=': + whitespace= ""; + state = ParseStateValue; + break; + + case '#': + finished = true; + break; + + default: + key += whitespace; + whitespace= ""; + key += c; + break; + } + break; + } + + case ParseStateValue: + { + switch(c) + { + case '\\': + if(i < line.Length - 1) + { + c = line[++i]; + switch(c) + { + case '\\': + case '#': + case '=': + val += val.Length == 0 ? escapedspace : whitespace; + whitespace= ""; + escapedspace= ""; + val += c; + break; + + case ' ': + whitespace += c; + escapedspace += c; + break; + + default: + val += val.Length == 0 ? escapedspace : whitespace; + whitespace= ""; + escapedspace= ""; + val += '\\'; + val += c; + break; + } + } + else + { + val += val.Length == 0 ? escapedspace : whitespace; + val += c; + } + break; + + case ' ': + case '\t': + case '\r': + case '\n': + if(val.Length != 0) + { + whitespace += c; + } + break; + + case '#': + finished = true; + break; + + default: + val += val.Length == 0 ? escapedspace : whitespace; + whitespace = ""; + escapedspace = ""; + val += c; + break; + } + break; + } + } + if(finished) + { + break; + } + } + val += escapedspace; + + if((state == ParseStateKey && key.Length != 0) || (state == ParseStateValue && key.Length == 0)) + { + Ice.Util.getProcessLogger().warning("invalid config file entry: \"" + line + "\""); + return; + } + else if(key.Length == 0) + { + return; + } + + setProperty(key, val); + } + + private void loadConfig() + { + string val = getProperty("Ice.Config"); + +#if !COMPACT && !SILVERLIGHT + if(val.Length == 0 || val.Equals("1")) + { + string s = System.Environment.GetEnvironmentVariable("ICE_CONFIG"); + if(s != null && s.Length != 0) + { + val = s; + } + } +#endif + + if(val.Length > 0) + { + char[] separator = { ',' }; + string[] files = val.Split(separator); + for(int i = 0; i < files.Length; i++) + { + load(files[i].Trim()); + } + + _properties["Ice.Config"] = new PropertyValue(val, true); + } + } + + private Dictionary<string, PropertyValue> _properties; + } +} diff --git a/csharp/src/Ice/Property.cs b/csharp/src/Ice/Property.cs new file mode 100644 index 00000000000..0363bd2e446 --- /dev/null +++ b/csharp/src/Ice/Property.cs @@ -0,0 +1,43 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + public sealed class Property + { + public Property(string pattern, bool deprecated, string deprecatedBy) + { + _pattern = pattern; + _deprecated = deprecated; + _deprecatedBy = deprecatedBy; + } + + public string + pattern() + { + return _pattern; + } + + public bool + deprecated() + { + return _deprecated; + } + + public string + deprecatedBy() + { + return _deprecatedBy; + } + + private string _pattern; + private bool _deprecated; + private string _deprecatedBy; + } +} diff --git a/csharp/src/Ice/PropertyNames.cs b/csharp/src/Ice/PropertyNames.cs new file mode 100644 index 00000000000..279d5cb0ff2 --- /dev/null +++ b/csharp/src/Ice/PropertyNames.cs @@ -0,0 +1,1249 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** +// Generated by makeprops.py from file ../config/PropertyNames.xml, Tue Dec 9 12:08:30 2014 + +// IMPORTANT: Do not edit this file -- any edits made here will be lost! + +namespace IceInternal +{ + public sealed class PropertyNames + { + public static Property[] IceProps = + { + new Property(@"^Ice\.ACM\.Client$", true, null), + new Property(@"^Ice\.ACM\.Server$", true, null), + new Property(@"^Ice\.ACM\.Timeout$", false, null), + new Property(@"^Ice\.ACM\.Heartbeat$", false, null), + new Property(@"^Ice\.ACM\.Close$", false, null), + new Property(@"^Ice\.ACM$", false, null), + new Property(@"^Ice\.ACM\.Client\.Timeout$", false, null), + new Property(@"^Ice\.ACM\.Client\.Heartbeat$", false, null), + new Property(@"^Ice\.ACM\.Client\.Close$", false, null), + new Property(@"^Ice\.ACM\.Client$", false, null), + new Property(@"^Ice\.ACM\.Server\.Timeout$", false, null), + new Property(@"^Ice\.ACM\.Server\.Heartbeat$", false, null), + new Property(@"^Ice\.ACM\.Server\.Close$", false, null), + new Property(@"^Ice\.ACM\.Server$", false, null), + new Property(@"^Ice\.Admin\.ACM\.Timeout$", false, null), + new Property(@"^Ice\.Admin\.ACM\.Heartbeat$", false, null), + new Property(@"^Ice\.Admin\.ACM\.Close$", false, null), + new Property(@"^Ice\.Admin\.ACM$", false, null), + new Property(@"^Ice\.Admin\.AdapterId$", false, null), + new Property(@"^Ice\.Admin\.Endpoints$", false, null), + new Property(@"^Ice\.Admin\.Locator\.EndpointSelection$", false, null), + new Property(@"^Ice\.Admin\.Locator\.ConnectionCached$", false, null), + new Property(@"^Ice\.Admin\.Locator\.PreferSecure$", false, null), + new Property(@"^Ice\.Admin\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^Ice\.Admin\.Locator\.InvocationTimeout$", false, null), + new Property(@"^Ice\.Admin\.Locator\.Locator$", false, null), + new Property(@"^Ice\.Admin\.Locator\.Router$", false, null), + new Property(@"^Ice\.Admin\.Locator\.CollocationOptimized$", false, null), + new Property(@"^Ice\.Admin\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^Ice\.Admin\.Locator$", false, null), + new Property(@"^Ice\.Admin\.PublishedEndpoints$", false, null), + new Property(@"^Ice\.Admin\.ReplicaGroupId$", false, null), + new Property(@"^Ice\.Admin\.Router\.EndpointSelection$", false, null), + new Property(@"^Ice\.Admin\.Router\.ConnectionCached$", false, null), + new Property(@"^Ice\.Admin\.Router\.PreferSecure$", false, null), + new Property(@"^Ice\.Admin\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^Ice\.Admin\.Router\.InvocationTimeout$", false, null), + new Property(@"^Ice\.Admin\.Router\.Locator$", false, null), + new Property(@"^Ice\.Admin\.Router\.Router$", false, null), + new Property(@"^Ice\.Admin\.Router\.CollocationOptimized$", false, null), + new Property(@"^Ice\.Admin\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^Ice\.Admin\.Router$", false, null), + new Property(@"^Ice\.Admin\.ProxyOptions$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.Size$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.SizeMax$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.StackSize$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.Serialize$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^Ice\.Admin\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^Ice\.Admin\.MessageSizeMax$", false, null), + new Property(@"^Ice\.Admin\.DelayCreation$", false, null), + new Property(@"^Ice\.Admin\.Enabled$", false, null), + new Property(@"^Ice\.Admin\.Facets$", false, null), + new Property(@"^Ice\.Admin\.InstanceName$", false, null), + new Property(@"^Ice\.Admin\.Logger\.KeepLogs$", false, null), + new Property(@"^Ice\.Admin\.Logger\.KeepTraces$", false, null), + new Property(@"^Ice\.Admin\.Logger\.Properties$", false, null), + new Property(@"^Ice\.Admin\.ServerId$", false, null), + new Property(@"^Ice\.BackgroundLocatorCacheUpdates$", false, null), + new Property(@"^Ice\.BatchAutoFlush$", true, null), + new Property(@"^Ice\.BatchAutoFlushSize$", false, null), + new Property(@"^Ice\.ChangeUser$", false, null), + new Property(@"^Ice\.ClientAccessPolicyProtocol$", false, null), + new Property(@"^Ice\.Compression\.Level$", false, null), + new Property(@"^Ice\.CollectObjects$", false, null), + new Property(@"^Ice\.Config$", false, null), + new Property(@"^Ice\.ConsoleListener$", false, null), + new Property(@"^Ice\.Default\.CollocationOptimized$", false, null), + new Property(@"^Ice\.Default\.EncodingVersion$", false, null), + new Property(@"^Ice\.Default\.EndpointSelection$", false, null), + new Property(@"^Ice\.Default\.Host$", false, null), + new Property(@"^Ice\.Default\.Locator\.EndpointSelection$", false, null), + new Property(@"^Ice\.Default\.Locator\.ConnectionCached$", false, null), + new Property(@"^Ice\.Default\.Locator\.PreferSecure$", false, null), + new Property(@"^Ice\.Default\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^Ice\.Default\.Locator\.InvocationTimeout$", false, null), + new Property(@"^Ice\.Default\.Locator\.Locator$", false, null), + new Property(@"^Ice\.Default\.Locator\.Router$", false, null), + new Property(@"^Ice\.Default\.Locator\.CollocationOptimized$", false, null), + new Property(@"^Ice\.Default\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^Ice\.Default\.Locator$", false, null), + new Property(@"^Ice\.Default\.LocatorCacheTimeout$", false, null), + new Property(@"^Ice\.Default\.InvocationTimeout$", false, null), + new Property(@"^Ice\.Default\.Package$", false, null), + new Property(@"^Ice\.Default\.PreferSecure$", false, null), + new Property(@"^Ice\.Default\.Protocol$", false, null), + new Property(@"^Ice\.Default\.Router\.EndpointSelection$", false, null), + new Property(@"^Ice\.Default\.Router\.ConnectionCached$", false, null), + new Property(@"^Ice\.Default\.Router\.PreferSecure$", false, null), + new Property(@"^Ice\.Default\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^Ice\.Default\.Router\.InvocationTimeout$", false, null), + new Property(@"^Ice\.Default\.Router\.Locator$", false, null), + new Property(@"^Ice\.Default\.Router\.Router$", false, null), + new Property(@"^Ice\.Default\.Router\.CollocationOptimized$", false, null), + new Property(@"^Ice\.Default\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^Ice\.Default\.Router$", false, null), + new Property(@"^Ice\.Default\.SlicedFormat$", false, null), + new Property(@"^Ice\.Default\.SourceAddress$", false, null), + new Property(@"^Ice\.Default\.Timeout$", false, null), + new Property(@"^Ice\.EventLog\.Source$", false, null), + new Property(@"^Ice\.FactoryAssemblies$", false, null), + new Property(@"^Ice\.HTTPProxyHost$", false, null), + new Property(@"^Ice\.HTTPProxyPort$", false, null), + new Property(@"^Ice\.ImplicitContext$", false, null), + new Property(@"^Ice\.InitPlugins$", false, null), + new Property(@"^Ice\.IPv4$", false, null), + new Property(@"^Ice\.IPv6$", false, null), + new Property(@"^Ice\.LogFile$", false, null), + new Property(@"^Ice\.LogStdErr\.Convert$", false, null), + new Property(@"^Ice\.MessageSizeMax$", false, null), + new Property(@"^Ice\.Nohup$", false, null), + new Property(@"^Ice\.NullHandleAbort$", false, null), + new Property(@"^Ice\.Override\.CloseTimeout$", false, null), + new Property(@"^Ice\.Override\.Compress$", false, null), + new Property(@"^Ice\.Override\.ConnectTimeout$", false, null), + new Property(@"^Ice\.Override\.Timeout$", false, null), + new Property(@"^Ice\.Override\.Secure$", false, null), + new Property(@"^Ice\.Package\.[^\s]+$", false, null), + new Property(@"^Ice\.Plugin\.[^\s]+$", false, null), + new Property(@"^Ice\.PluginLoadOrder$", false, null), + new Property(@"^Ice\.PreferIPv6Address$", false, null), + new Property(@"^Ice\.PrintAdapterReady$", false, null), + new Property(@"^Ice\.PrintProcessId$", false, null), + new Property(@"^Ice\.PrintStackTraces$", false, null), + new Property(@"^Ice\.ProgramName$", false, null), + new Property(@"^Ice\.RetryIntervals$", false, null), + new Property(@"^Ice\.ServerIdleTime$", false, null), + new Property(@"^Ice\.SOCKSProxyHost$", false, null), + new Property(@"^Ice\.SOCKSProxyPort$", false, null), + new Property(@"^Ice\.StdErr$", false, null), + new Property(@"^Ice\.StdOut$", false, null), + new Property(@"^Ice\.SyslogFacility$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.Size$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.SizeMax$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.SizeWarn$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.StackSize$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.Serialize$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.ThreadIdleTime$", false, null), + new Property(@"^Ice\.ThreadPool\.Client\.ThreadPriority$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.Size$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.SizeMax$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.SizeWarn$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.StackSize$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.Serialize$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.ThreadIdleTime$", false, null), + new Property(@"^Ice\.ThreadPool\.Server\.ThreadPriority$", false, null), + new Property(@"^Ice\.ThreadPriority$", false, null), + new Property(@"^Ice\.Trace\.Admin\.Properties$", false, null), + new Property(@"^Ice\.Trace\.Admin\.Logger$", false, null), + new Property(@"^Ice\.Trace\.Locator$", false, null), + new Property(@"^Ice\.Trace\.Network$", false, null), + new Property(@"^Ice\.Trace\.Protocol$", false, null), + new Property(@"^Ice\.Trace\.Retry$", false, null), + new Property(@"^Ice\.Trace\.Slicing$", false, null), + new Property(@"^Ice\.Trace\.ThreadPool$", false, null), + new Property(@"^Ice\.UDP\.RcvSize$", false, null), + new Property(@"^Ice\.UDP\.SndSize$", false, null), + new Property(@"^Ice\.TCP\.Backlog$", false, null), + new Property(@"^Ice\.TCP\.RcvSize$", false, null), + new Property(@"^Ice\.TCP\.SndSize$", false, null), + new Property(@"^Ice\.UseApplicationClassLoader$", false, null), + new Property(@"^Ice\.UseSyslog$", false, null), + new Property(@"^Ice\.Warn\.AMICallback$", false, null), + new Property(@"^Ice\.Warn\.Connections$", false, null), + new Property(@"^Ice\.Warn\.Datagrams$", false, null), + new Property(@"^Ice\.Warn\.Dispatch$", false, null), + new Property(@"^Ice\.Warn\.Endpoints$", false, null), + new Property(@"^Ice\.Warn\.UnknownProperties$", false, null), + new Property(@"^Ice\.Warn\.UnusedProperties$", false, null), + new Property(@"^Ice\.CacheMessageBuffers$", false, null), + new Property(@"^Ice\.ThreadInterruptSafe$", false, null), + null + }; + + public static Property[] IceMXProps = + { + new Property(@"^IceMX\.Metrics\.[^\s]+\.GroupBy$", false, null), + new Property(@"^IceMX\.Metrics\.[^\s]+\.Map$", false, null), + new Property(@"^IceMX\.Metrics\.[^\s]+\.RetainDetached$", false, null), + new Property(@"^IceMX\.Metrics\.[^\s]+\.Accept$", false, null), + new Property(@"^IceMX\.Metrics\.[^\s]+\.Reject$", false, null), + new Property(@"^IceMX\.Metrics\.[^\s]+$", false, null), + null + }; + + public static Property[] IceDiscoveryProps = + { + new Property(@"^IceDiscovery\.Multicast\.ACM\.Timeout$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ACM\.Heartbeat$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ACM\.Close$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ACM$", false, null), + new Property(@"^IceDiscovery\.Multicast\.AdapterId$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Endpoints$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.PreferSecure$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.Locator$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.Router$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Locator$", false, null), + new Property(@"^IceDiscovery\.Multicast\.PublishedEndpoints$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ReplicaGroupId$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.EndpointSelection$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.ConnectionCached$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.PreferSecure$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.Locator$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.Router$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceDiscovery\.Multicast\.Router$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ProxyOptions$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.Size$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceDiscovery\.Multicast\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceDiscovery\.Multicast\.MessageSizeMax$", false, null), + new Property(@"^IceDiscovery\.Reply\.ACM\.Timeout$", false, null), + new Property(@"^IceDiscovery\.Reply\.ACM\.Heartbeat$", false, null), + new Property(@"^IceDiscovery\.Reply\.ACM\.Close$", false, null), + new Property(@"^IceDiscovery\.Reply\.ACM$", false, null), + new Property(@"^IceDiscovery\.Reply\.AdapterId$", false, null), + new Property(@"^IceDiscovery\.Reply\.Endpoints$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.PreferSecure$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.Locator$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.Router$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceDiscovery\.Reply\.Locator$", false, null), + new Property(@"^IceDiscovery\.Reply\.PublishedEndpoints$", false, null), + new Property(@"^IceDiscovery\.Reply\.ReplicaGroupId$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.EndpointSelection$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.ConnectionCached$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.PreferSecure$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.Locator$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.Router$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceDiscovery\.Reply\.Router$", false, null), + new Property(@"^IceDiscovery\.Reply\.ProxyOptions$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.Size$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceDiscovery\.Reply\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceDiscovery\.Reply\.MessageSizeMax$", false, null), + new Property(@"^IceDiscovery\.Locator\.ACM\.Timeout$", false, null), + new Property(@"^IceDiscovery\.Locator\.ACM\.Heartbeat$", false, null), + new Property(@"^IceDiscovery\.Locator\.ACM\.Close$", false, null), + new Property(@"^IceDiscovery\.Locator\.ACM$", false, null), + new Property(@"^IceDiscovery\.Locator\.AdapterId$", false, null), + new Property(@"^IceDiscovery\.Locator\.Endpoints$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.PreferSecure$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.Locator$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.Router$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceDiscovery\.Locator\.Locator$", false, null), + new Property(@"^IceDiscovery\.Locator\.PublishedEndpoints$", false, null), + new Property(@"^IceDiscovery\.Locator\.ReplicaGroupId$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.EndpointSelection$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.ConnectionCached$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.PreferSecure$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.Locator$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.Router$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceDiscovery\.Locator\.Router$", false, null), + new Property(@"^IceDiscovery\.Locator\.ProxyOptions$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.Size$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceDiscovery\.Locator\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceDiscovery\.Locator\.MessageSizeMax$", false, null), + new Property(@"^IceDiscovery\.Lookup$", false, null), + new Property(@"^IceDiscovery\.Timeout$", false, null), + new Property(@"^IceDiscovery\.RetryCount$", false, null), + new Property(@"^IceDiscovery\.LatencyMultiplier$", false, null), + new Property(@"^IceDiscovery\.Address$", false, null), + new Property(@"^IceDiscovery\.Port$", false, null), + new Property(@"^IceDiscovery\.Interface$", false, null), + new Property(@"^IceDiscovery\.DomainId$", false, null), + null + }; + + public static Property[] IceGridDiscoveryProps = + { + new Property(@"^IceGridDiscovery\.Reply\.ACM\.Timeout$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ACM\.Close$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ACM$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.AdapterId$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Endpoints$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.Locator$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.Router$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Locator$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.PublishedEndpoints$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ReplicaGroupId$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.PreferSecure$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.Locator$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.Router$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.Router$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ProxyOptions$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.Size$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGridDiscovery\.Reply\.MessageSizeMax$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ACM\.Timeout$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ACM\.Close$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ACM$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.AdapterId$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Endpoints$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.Locator$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.Router$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Locator$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.PublishedEndpoints$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ReplicaGroupId$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.PreferSecure$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.Locator$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.Router$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.Router$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ProxyOptions$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.Size$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGridDiscovery\.Locator\.MessageSizeMax$", false, null), + new Property(@"^IceGridDiscovery\.Lookup$", false, null), + new Property(@"^IceGridDiscovery\.Timeout$", false, null), + new Property(@"^IceGridDiscovery\.RetryCount$", false, null), + new Property(@"^IceGridDiscovery\.RetryDelay$", false, null), + new Property(@"^IceGridDiscovery\.Address$", false, null), + new Property(@"^IceGridDiscovery\.Port$", false, null), + new Property(@"^IceGridDiscovery\.Interface$", false, null), + new Property(@"^IceGridDiscovery\.InstanceName$", false, null), + null + }; + + public static Property[] IceBoxProps = + { + new Property(@"^IceBox\.InheritProperties$", false, null), + new Property(@"^IceBox\.InstanceName$", true, null), + new Property(@"^IceBox\.LoadOrder$", false, null), + new Property(@"^IceBox\.PrintServicesReady$", false, null), + new Property(@"^IceBox\.Service\.[^\s]+$", false, null), + new Property(@"^IceBox\.ServiceManager\.AdapterId$", true, null), + new Property(@"^IceBox\.ServiceManager\.Endpoints$", true, null), + new Property(@"^IceBox\.ServiceManager\.Locator$", true, null), + new Property(@"^IceBox\.ServiceManager\.PublishedEndpoints$", true, null), + new Property(@"^IceBox\.ServiceManager\.ReplicaGroupId$", true, null), + new Property(@"^IceBox\.ServiceManager\.Router$", true, null), + new Property(@"^IceBox\.ServiceManager\.ThreadPool\.Size$", true, null), + new Property(@"^IceBox\.ServiceManager\.ThreadPool\.SizeMax$", true, null), + new Property(@"^IceBox\.ServiceManager\.ThreadPool\.SizeWarn$", true, null), + new Property(@"^IceBox\.ServiceManager\.ThreadPool\.StackSize$", true, null), + new Property(@"^IceBox\.Trace\.ServiceObserver$", false, null), + new Property(@"^IceBox\.UseSharedCommunicator\.[^\s]+$", false, null), + null + }; + + public static Property[] IceBoxAdminProps = + { + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.EndpointSelection$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.ConnectionCached$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.PreferSecure$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.LocatorCacheTimeout$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.InvocationTimeout$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.Locator$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.Router$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.CollocationOptimized$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy\.Context\.[^\s]+$", false, null), + new Property(@"^IceBoxAdmin\.ServiceManager\.Proxy$", false, null), + null + }; + + public static Property[] IceGridAdminProps = + { + new Property(@"^IceGridAdmin\.AuthenticateUsingSSL$", false, null), + new Property(@"^IceGridAdmin\.MetricsConfig$", false, null), + new Property(@"^IceGridAdmin\.Username$", false, null), + new Property(@"^IceGridAdmin\.Password$", false, null), + new Property(@"^IceGridAdmin\.Replica$", false, null), + new Property(@"^IceGridAdmin\.Host$", false, null), + new Property(@"^IceGridAdmin\.Port$", false, null), + new Property(@"^IceGridAdmin\.InstanceName$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Address$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Interface$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Lookup$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ACM\.Timeout$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ACM\.Close$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ACM$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.AdapterId$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Endpoints$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.Locator$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.Router$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Locator$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.PublishedEndpoints$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ReplicaGroupId$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.PreferSecure$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.Locator$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.Router$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.Router$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ProxyOptions$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.Size$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGridAdmin\.Discovery\.Reply\.MessageSizeMax$", false, null), + new Property(@"^IceGridAdmin\.Trace\.Observers$", false, null), + new Property(@"^IceGridAdmin\.Trace\.SaveToRegistry$", false, null), + null + }; + + public static Property[] IceGridProps = + { + new Property(@"^IceGrid\.AdminRouter\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ACM$", false, null), + new Property(@"^IceGrid\.AdminRouter\.AdapterId$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Endpoints$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Locator$", false, null), + new Property(@"^IceGrid\.AdminRouter\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.Router$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.AdminRouter\.Router$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.AdminRouter\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.AdminRouter\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.InstanceName$", false, null), + new Property(@"^IceGrid\.Node\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Node\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Node\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Node\.ACM$", false, null), + new Property(@"^IceGrid\.Node\.AdapterId$", false, null), + new Property(@"^IceGrid\.Node\.Endpoints$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Node\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Node\.Locator$", false, null), + new Property(@"^IceGrid\.Node\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Node\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Node\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Node\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Node\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Node\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Node\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Node\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Node\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Node\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Node\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Node\.Router$", false, null), + new Property(@"^IceGrid\.Node\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Node\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Node\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Node\.AllowRunningServersAsRoot$", false, null), + new Property(@"^IceGrid\.Node\.AllowEndpointsOverride$", false, null), + new Property(@"^IceGrid\.Node\.CollocateRegistry$", false, null), + new Property(@"^IceGrid\.Node\.Data$", false, null), + new Property(@"^IceGrid\.Node\.DisableOnFailure$", false, null), + new Property(@"^IceGrid\.Node\.Name$", false, null), + new Property(@"^IceGrid\.Node\.Output$", false, null), + new Property(@"^IceGrid\.Node\.ProcessorSocketCount$", false, null), + new Property(@"^IceGrid\.Node\.PrintServersReady$", false, null), + new Property(@"^IceGrid\.Node\.PropertiesOverride$", false, null), + new Property(@"^IceGrid\.Node\.RedirectErrToOut$", false, null), + new Property(@"^IceGrid\.Node\.Trace\.Activator$", false, null), + new Property(@"^IceGrid\.Node\.Trace\.Adapter$", false, null), + new Property(@"^IceGrid\.Node\.Trace\.Patch$", false, null), + new Property(@"^IceGrid\.Node\.Trace\.Replica$", false, null), + new Property(@"^IceGrid\.Node\.Trace\.Server$", false, null), + new Property(@"^IceGrid\.Node\.UserAccounts$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.Locator$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.Router$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Node\.UserAccountMapper$", false, null), + new Property(@"^IceGrid\.Node\.WaitTime$", false, null), + new Property(@"^IceGrid\.Registry\.AdminCryptPasswords$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.AdminPermissionsVerifier$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionFilters$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ACM$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.AdapterId$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Endpoints$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSessionManager\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.AdminSSLPermissionsVerifier$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ACM$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.AdapterId$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Endpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Registry\.Client\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.CryptPasswords$", false, null), + new Property(@"^IceGrid\.Registry\.Data$", false, null), + new Property(@"^IceGrid\.Registry\.DefaultTemplates$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ACM$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.AdapterId$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Endpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Enabled$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Address$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Port$", false, null), + new Property(@"^IceGrid\.Registry\.Discovery\.Interface$", false, null), + new Property(@"^IceGrid\.Registry\.DynamicRegistration$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ACM$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.AdapterId$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Endpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Registry\.Internal\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.NodeSessionTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.PermissionsVerifier$", false, null), + new Property(@"^IceGrid\.Registry\.ReplicaName$", false, null), + new Property(@"^IceGrid\.Registry\.ReplicaSessionTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.RequireNodeCertCN$", false, null), + new Property(@"^IceGrid\.Registry\.RequireReplicaCertCN$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ACM$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.AdapterId$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Endpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Registry\.Server\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.SessionFilters$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ACM\.Timeout$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ACM\.Heartbeat$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ACM\.Close$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ACM$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.AdapterId$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Endpoints$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.PublishedEndpoints$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ReplicaGroupId$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ProxyOptions$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.Size$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.StackSize$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.Serialize$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IceGrid\.Registry\.SessionManager\.MessageSizeMax$", false, null), + new Property(@"^IceGrid\.Registry\.SessionTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.EndpointSelection$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.ConnectionCached$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.PreferSecure$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.LocatorCacheTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.InvocationTimeout$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.Router$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.CollocationOptimized$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier\.Context\.[^\s]+$", false, null), + new Property(@"^IceGrid\.Registry\.SSLPermissionsVerifier$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Application$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Adapter$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Locator$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Node$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Object$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Patch$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Replica$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Server$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Session$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Subscriber$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.Topic$", false, null), + new Property(@"^IceGrid\.Registry\.Trace\.TopicManager$", false, null), + new Property(@"^IceGrid\.Registry\.UserAccounts$", false, null), + new Property(@"^IceGrid\.SQL\.DatabaseType$", false, null), + new Property(@"^IceGrid\.SQL\.EncodingVersion$", false, null), + new Property(@"^IceGrid\.SQL\.HostName$", false, null), + new Property(@"^IceGrid\.SQL\.Port$", false, null), + new Property(@"^IceGrid\.SQL\.DatabaseName$", false, null), + new Property(@"^IceGrid\.SQL\.UserName$", false, null), + new Property(@"^IceGrid\.SQL\.Password$", false, null), + null + }; + + public static Property[] IcePatch2Props = + { + new Property(@"^IcePatch2\.ACM\.Timeout$", false, null), + new Property(@"^IcePatch2\.ACM\.Heartbeat$", false, null), + new Property(@"^IcePatch2\.ACM\.Close$", false, null), + new Property(@"^IcePatch2\.ACM$", false, null), + new Property(@"^IcePatch2\.AdapterId$", false, null), + new Property(@"^IcePatch2\.Endpoints$", false, null), + new Property(@"^IcePatch2\.Locator\.EndpointSelection$", false, null), + new Property(@"^IcePatch2\.Locator\.ConnectionCached$", false, null), + new Property(@"^IcePatch2\.Locator\.PreferSecure$", false, null), + new Property(@"^IcePatch2\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^IcePatch2\.Locator\.InvocationTimeout$", false, null), + new Property(@"^IcePatch2\.Locator\.Locator$", false, null), + new Property(@"^IcePatch2\.Locator\.Router$", false, null), + new Property(@"^IcePatch2\.Locator\.CollocationOptimized$", false, null), + new Property(@"^IcePatch2\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^IcePatch2\.Locator$", false, null), + new Property(@"^IcePatch2\.PublishedEndpoints$", false, null), + new Property(@"^IcePatch2\.ReplicaGroupId$", false, null), + new Property(@"^IcePatch2\.Router\.EndpointSelection$", false, null), + new Property(@"^IcePatch2\.Router\.ConnectionCached$", false, null), + new Property(@"^IcePatch2\.Router\.PreferSecure$", false, null), + new Property(@"^IcePatch2\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^IcePatch2\.Router\.InvocationTimeout$", false, null), + new Property(@"^IcePatch2\.Router\.Locator$", false, null), + new Property(@"^IcePatch2\.Router\.Router$", false, null), + new Property(@"^IcePatch2\.Router\.CollocationOptimized$", false, null), + new Property(@"^IcePatch2\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^IcePatch2\.Router$", false, null), + new Property(@"^IcePatch2\.ProxyOptions$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.Size$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.SizeMax$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.StackSize$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.Serialize$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^IcePatch2\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^IcePatch2\.MessageSizeMax$", false, null), + new Property(@"^IcePatch2\.Directory$", false, null), + new Property(@"^IcePatch2\.InstanceName$", false, null), + null + }; + + public static Property[] IcePatch2ClientProps = + { + new Property(@"^IcePatch2Client\.ChunkSize$", false, null), + new Property(@"^IcePatch2Client\.Directory$", false, null), + new Property(@"^IcePatch2Client\.Proxy$", false, null), + new Property(@"^IcePatch2Client\.Remove$", false, null), + new Property(@"^IcePatch2Client\.Thorough$", false, null), + null + }; + + public static Property[] IceSSLProps = + { + new Property(@"^IceSSL\.Alias$", false, null), + new Property(@"^IceSSL\.CertAuthDir$", false, null), + new Property(@"^IceSSL\.CertAuthFile$", false, null), + new Property(@"^IceSSL\.CertStore$", false, null), + new Property(@"^IceSSL\.CertFile$", false, null), + new Property(@"^IceSSL\.CertVerifier$", false, null), + new Property(@"^IceSSL\.CheckCertName$", false, null), + new Property(@"^IceSSL\.CheckCRL$", false, null), + new Property(@"^IceSSL\.Ciphers$", false, null), + new Property(@"^IceSSL\.DefaultDir$", false, null), + new Property(@"^IceSSL\.DH\.[^\s]+$", false, null), + new Property(@"^IceSSL\.DHParams$", false, null), + new Property(@"^IceSSL\.EntropyDaemon$", false, null), + new Property(@"^IceSSL\.FindCert$", false, null), + new Property(@"^IceSSL\.FindCert\.[^\s]+$", false, null), + new Property(@"^IceSSL\.ImportCert\.[^\s]+$", true, null), + new Property(@"^IceSSL\.InitOpenSSL$", false, null), + new Property(@"^IceSSL\.KeyFile$", false, null), + new Property(@"^IceSSL\.KeySet$", false, null), + new Property(@"^IceSSL\.Keychain$", false, null), + new Property(@"^IceSSL\.KeychainPassword$", false, null), + new Property(@"^IceSSL\.Keystore$", false, null), + new Property(@"^IceSSL\.KeystorePassword$", false, null), + new Property(@"^IceSSL\.KeystoreType$", false, null), + new Property(@"^IceSSL\.Password$", false, null), + new Property(@"^IceSSL\.PasswordCallback$", false, null), + new Property(@"^IceSSL\.PasswordRetryMax$", false, null), + new Property(@"^IceSSL\.PersistKeySet$", false, null), + new Property(@"^IceSSL\.Protocols$", false, null), + new Property(@"^IceSSL\.ProtocolVersionMax$", false, null), + new Property(@"^IceSSL\.ProtocolVersionMin$", false, null), + new Property(@"^IceSSL\.Random$", false, null), + new Property(@"^IceSSL\.Trace\.Security$", false, null), + new Property(@"^IceSSL\.TrustOnly$", false, null), + new Property(@"^IceSSL\.TrustOnly\.Client$", false, null), + new Property(@"^IceSSL\.TrustOnly\.Server$", false, null), + new Property(@"^IceSSL\.TrustOnly\.Server\.[^\s]+$", false, null), + new Property(@"^IceSSL\.Truststore$", false, null), + new Property(@"^IceSSL\.TruststorePassword$", false, null), + new Property(@"^IceSSL\.TruststoreType$", false, null), + new Property(@"^IceSSL\.VerifyDepthMax$", false, null), + new Property(@"^IceSSL\.VerifyPeer$", false, null), + null + }; + + public static Property[] IceStormAdminProps = + { + new Property(@"^IceStormAdmin\.TopicManager\.[^\s]+$", false, null), + new Property(@"^IceStormAdmin\.Host$", false, null), + new Property(@"^IceStormAdmin\.Port$", false, null), + null + }; + + public static Property[] Glacier2Props = + { + new Property(@"^Glacier2\.AddConnectionContext$", false, null), + new Property(@"^Glacier2\.Client\.ACM\.Timeout$", false, null), + new Property(@"^Glacier2\.Client\.ACM\.Heartbeat$", false, null), + new Property(@"^Glacier2\.Client\.ACM\.Close$", false, null), + new Property(@"^Glacier2\.Client\.ACM$", false, null), + new Property(@"^Glacier2\.Client\.AdapterId$", false, null), + new Property(@"^Glacier2\.Client\.Endpoints$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.PreferSecure$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.Locator$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.Router$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.Client\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.Client\.Locator$", false, null), + new Property(@"^Glacier2\.Client\.PublishedEndpoints$", false, null), + new Property(@"^Glacier2\.Client\.ReplicaGroupId$", false, null), + new Property(@"^Glacier2\.Client\.Router\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.Client\.Router\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.Client\.Router\.PreferSecure$", false, null), + new Property(@"^Glacier2\.Client\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.Client\.Router\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.Client\.Router\.Locator$", false, null), + new Property(@"^Glacier2\.Client\.Router\.Router$", false, null), + new Property(@"^Glacier2\.Client\.Router\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.Client\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.Client\.Router$", false, null), + new Property(@"^Glacier2\.Client\.ProxyOptions$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.Size$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.SizeMax$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.StackSize$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.Serialize$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^Glacier2\.Client\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^Glacier2\.Client\.MessageSizeMax$", false, null), + new Property(@"^Glacier2\.Client\.AlwaysBatch$", false, null), + new Property(@"^Glacier2\.Client\.Buffered$", false, null), + new Property(@"^Glacier2\.Client\.ForwardContext$", false, null), + new Property(@"^Glacier2\.Client\.SleepTime$", false, null), + new Property(@"^Glacier2\.Client\.Trace\.Override$", false, null), + new Property(@"^Glacier2\.Client\.Trace\.Reject$", false, null), + new Property(@"^Glacier2\.Client\.Trace\.Request$", false, null), + new Property(@"^Glacier2\.CryptPasswords$", false, null), + new Property(@"^Glacier2\.Filter\.Address\.Reject$", false, null), + new Property(@"^Glacier2\.Filter\.Address\.Accept$", false, null), + new Property(@"^Glacier2\.Filter\.ProxySizeMax$", false, null), + new Property(@"^Glacier2\.Filter\.Category\.Accept$", false, null), + new Property(@"^Glacier2\.Filter\.Category\.AcceptUser$", false, null), + new Property(@"^Glacier2\.Filter\.AdapterId\.Accept$", false, null), + new Property(@"^Glacier2\.Filter\.Identity\.Accept$", false, null), + new Property(@"^Glacier2\.InstanceName$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.PreferSecure$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.Locator$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.Router$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.PermissionsVerifier$", false, null), + new Property(@"^Glacier2\.ReturnClientProxy$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.PreferSecure$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.Locator$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.Router$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.SSLPermissionsVerifier$", false, null), + new Property(@"^Glacier2\.RoutingTable\.MaxSize$", false, null), + new Property(@"^Glacier2\.Server\.ACM\.Timeout$", false, null), + new Property(@"^Glacier2\.Server\.ACM\.Heartbeat$", false, null), + new Property(@"^Glacier2\.Server\.ACM\.Close$", false, null), + new Property(@"^Glacier2\.Server\.ACM$", false, null), + new Property(@"^Glacier2\.Server\.AdapterId$", false, null), + new Property(@"^Glacier2\.Server\.Endpoints$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.PreferSecure$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.Locator$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.Router$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.Server\.Locator\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.Server\.Locator$", false, null), + new Property(@"^Glacier2\.Server\.PublishedEndpoints$", false, null), + new Property(@"^Glacier2\.Server\.ReplicaGroupId$", false, null), + new Property(@"^Glacier2\.Server\.Router\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.Server\.Router\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.Server\.Router\.PreferSecure$", false, null), + new Property(@"^Glacier2\.Server\.Router\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.Server\.Router\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.Server\.Router\.Locator$", false, null), + new Property(@"^Glacier2\.Server\.Router\.Router$", false, null), + new Property(@"^Glacier2\.Server\.Router\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.Server\.Router\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.Server\.Router$", false, null), + new Property(@"^Glacier2\.Server\.ProxyOptions$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.Size$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.SizeMax$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.SizeWarn$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.StackSize$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.Serialize$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.ThreadIdleTime$", false, null), + new Property(@"^Glacier2\.Server\.ThreadPool\.ThreadPriority$", false, null), + new Property(@"^Glacier2\.Server\.MessageSizeMax$", false, null), + new Property(@"^Glacier2\.Server\.AlwaysBatch$", false, null), + new Property(@"^Glacier2\.Server\.Buffered$", false, null), + new Property(@"^Glacier2\.Server\.ForwardContext$", false, null), + new Property(@"^Glacier2\.Server\.SleepTime$", false, null), + new Property(@"^Glacier2\.Server\.Trace\.Override$", false, null), + new Property(@"^Glacier2\.Server\.Trace\.Request$", false, null), + new Property(@"^Glacier2\.SessionManager\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.SessionManager\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.SessionManager\.PreferSecure$", false, null), + new Property(@"^Glacier2\.SessionManager\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.SessionManager\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.SessionManager\.Locator$", false, null), + new Property(@"^Glacier2\.SessionManager\.Router$", false, null), + new Property(@"^Glacier2\.SessionManager\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.SessionManager\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.SessionManager$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.EndpointSelection$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.ConnectionCached$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.PreferSecure$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.LocatorCacheTimeout$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.InvocationTimeout$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.Locator$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.Router$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.CollocationOptimized$", false, null), + new Property(@"^Glacier2\.SSLSessionManager\.Context\.[^\s]+$", false, null), + new Property(@"^Glacier2\.SSLSessionManager$", false, null), + new Property(@"^Glacier2\.SessionTimeout$", false, null), + new Property(@"^Glacier2\.Trace\.RoutingTable$", false, null), + new Property(@"^Glacier2\.Trace\.Session$", false, null), + null + }; + + public static Property[] Glacier2CryptPermissionsVerifierProps = + { + new Property(@"^Glacier2CryptPermissionsVerifier\.[^\s]+\.PermissionsVerifier$", false, null), + new Property(@"^Glacier2CryptPermissionsVerifier\.[^\s]+\.AdminPermissionsVerifier$", false, null), + null + }; + + public static Property[] FreezeProps = + { + new Property(@"^Freeze\.DbEnv\.[^\s]+\.CheckpointPeriod$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.DbHome$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.DbPrivate$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.DbRecoverFatal$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.EncodingVersion$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.LockFile$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.OldLogsAutoDelete$", false, null), + new Property(@"^Freeze\.DbEnv\.[^\s]+\.PeriodicCheckpointMinSize$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.BtreeMinKey$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.Checksum$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.MaxTxSize$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.PageSize$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.PopulateEmptyIndices$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.RollbackOnUserException$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.SavePeriod$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.SaveSizeTrigger$", false, null), + new Property(@"^Freeze\.Evictor\.[^\s]+\.StreamTimeout$", false, null), + new Property(@"^Freeze\.Map\.[^\s]+\.BtreeMinKey$", false, null), + new Property(@"^Freeze\.Map\.[^\s]+\.Checksum$", false, null), + new Property(@"^Freeze\.Map\.[^\s]+\.PageSize$", false, null), + new Property(@"^Freeze\.Trace\.DbEnv$", false, null), + new Property(@"^Freeze\.Trace\.Evictor$", false, null), + new Property(@"^Freeze\.Trace\.Map$", false, null), + new Property(@"^Freeze\.Trace\.Transaction$", false, null), + new Property(@"^Freeze\.Warn\.Deadlocks$", false, null), + new Property(@"^Freeze\.Warn\.Rollback$", false, null), + null + }; + + public static Property[][] validProps = + { + IceProps, + IceMXProps, + IceDiscoveryProps, + IceGridDiscoveryProps, + IceBoxProps, + IceBoxAdminProps, + IceGridAdminProps, + IceGridProps, + IcePatch2Props, + IcePatch2ClientProps, + IceSSLProps, + IceStormAdminProps, + Glacier2Props, + Glacier2CryptPermissionsVerifierProps, + FreezeProps, + null + }; + + public static string[] clPropNames = + { + "Ice", + "IceMX", + "IceDiscovery", + "IceGridDiscovery", + "IceBox", + "IceBoxAdmin", + "IceGridAdmin", + "IceGrid", + "IcePatch2", + "IcePatch2Client", + "IceSSL", + "IceStormAdmin", + "Glacier2", + "Glacier2CryptPermissionsVerifier", + "Freeze", + null + }; + } +} diff --git a/csharp/src/Ice/Protocol.cs b/csharp/src/Ice/Protocol.cs new file mode 100644 index 00000000000..f4d8823369a --- /dev/null +++ b/csharp/src/Ice/Protocol.cs @@ -0,0 +1,184 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + sealed class Protocol + { + // + // Size of the Ice protocol header + // + // Magic number (4 bytes) + // Protocol version major (Byte) + // Protocol version minor (Byte) + // Encoding version major (Byte) + // Encoding version minor (Byte) + // Message type (Byte) + // Compression status (Byte) + // Message size (Int) + // + internal const int headerSize = 14; + + // + // The magic number at the front of each message + // + internal static readonly byte[] magic + = new byte[] { (byte)0x49, (byte)0x63, (byte)0x65, (byte)0x50 }; // 'I', 'c', 'e', 'P' + + // + // The current Ice protocol and encoding version + // + internal const byte protocolMajor = 1; + internal const byte protocolMinor = 0; + internal const byte protocolEncodingMajor = 1; + internal const byte protocolEncodingMinor = 0; + + internal const byte encodingMajor = 1; + internal const byte encodingMinor = 1; + + // + // The Ice protocol message types + // + internal const byte requestMsg = 0; + internal const byte requestBatchMsg = 1; + internal const byte replyMsg = 2; + internal const byte validateConnectionMsg = 3; + internal const byte closeConnectionMsg = 4; + + internal static readonly byte[] requestHdr = new byte[] + { + IceInternal.Protocol.magic[0], IceInternal.Protocol.magic[1], IceInternal.Protocol.magic[2], + IceInternal.Protocol.magic[3], + IceInternal.Protocol.protocolMajor, IceInternal.Protocol.protocolMinor, + IceInternal.Protocol.protocolEncodingMajor, IceInternal.Protocol.protocolEncodingMinor, + IceInternal.Protocol.requestMsg, + (byte)0, // Compression status. + (byte)0, (byte)0, (byte)0, (byte)0, // Message size (placeholder). + (byte)0, (byte)0, (byte)0, (byte)0 // Request ID (placeholder). + }; + + internal static readonly byte[] requestBatchHdr = new byte[] + { + IceInternal.Protocol.magic[0], IceInternal.Protocol.magic[1], IceInternal.Protocol.magic[2], + IceInternal.Protocol.magic[3], + IceInternal.Protocol.protocolMajor, IceInternal.Protocol.protocolMinor, + IceInternal.Protocol.protocolEncodingMajor, IceInternal.Protocol.protocolEncodingMinor, + IceInternal.Protocol.requestBatchMsg, + (byte)0, // Compression status. + (byte)0, (byte)0, (byte)0, (byte)0, // Message size (placeholder). + (byte)0, (byte)0, (byte)0, (byte)0 // Number of requests in batch (placeholder). + }; + + internal static readonly byte[] replyHdr = new byte[] + { + IceInternal.Protocol.magic[0], IceInternal.Protocol.magic[1], IceInternal.Protocol.magic[2], + IceInternal.Protocol.magic[3], + IceInternal.Protocol.protocolMajor, IceInternal.Protocol.protocolMinor, + IceInternal.Protocol.protocolEncodingMajor, IceInternal.Protocol.protocolEncodingMinor, + IceInternal.Protocol.replyMsg, + (byte)0, // Compression status. + (byte)0, (byte)0, (byte)0, (byte)0 // Message size (placeholder). + }; + + internal static void + checkSupportedProtocol(Ice.ProtocolVersion v) + { + if(v.major != protocolMajor || v.minor > protocolMinor) + { + throw new Ice.UnsupportedProtocolException("", v, Ice.Util.currentProtocol); + } + } + + internal static void + checkSupportedProtocolEncoding(Ice.EncodingVersion v) + { + if(v.major != protocolEncodingMajor || v.minor > protocolEncodingMinor) + { + throw new Ice.UnsupportedEncodingException("", v, Ice.Util.currentProtocolEncoding); + } + } + + internal static void + checkSupportedEncoding(Ice.EncodingVersion v) + { + if(v.major != encodingMajor || v.minor > encodingMinor) + { + throw new Ice.UnsupportedEncodingException("", v, Ice.Util.currentEncoding); + } + } + + // + // Either return the given protocol if not compatible, or the greatest + // supported protocol otherwise. + // + internal static Ice.ProtocolVersion + getCompatibleProtocol(Ice.ProtocolVersion v) + { + if(v.major != Ice.Util.currentProtocol.major) + { + return v; // Unsupported protocol, return as is. + } + else if(v.minor < Ice.Util.currentProtocol.minor) + { + return v; // Supported protocol. + } + else + { + // + // Unsupported but compatible, use the currently supported + // protocol, that's the best we can do. + // + return Ice.Util.currentProtocol; + } + } + + // + // Either return the given encoding if not compatible, or the greatest + // supported encoding otherwise. + // + internal static Ice.EncodingVersion + getCompatibleEncoding(Ice.EncodingVersion v) + { + if(v.major != Ice.Util.currentEncoding.major) + { + return v; // Unsupported encoding, return as is. + } + else if(v.minor < Ice.Util.currentEncoding.minor) + { + return v; // Supported encoding. + } + else + { + // + // Unsupported but compatible, use the currently supported + // encoding, that's the best we can do. + // + return Ice.Util.currentEncoding; + } + } + + internal static bool + isSupported(Ice.ProtocolVersion version, Ice.ProtocolVersion supported) + { + return version.major == supported.major && version.minor <= supported.minor; + } + + internal static bool + isSupported(Ice.EncodingVersion version, Ice.EncodingVersion supported) + { + return version.major == supported.major && version.minor <= supported.minor; + } + + private Protocol() + { + } + } + +} diff --git a/csharp/src/Ice/ProtocolInstance.cs b/csharp/src/Ice/ProtocolInstance.cs new file mode 100644 index 00000000000..68c8d33e13f --- /dev/null +++ b/csharp/src/Ice/ProtocolInstance.cs @@ -0,0 +1,149 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Net; + using System.Collections.Generic; + + public class ProtocolInstance + { + public ProtocolInstance(Ice.Communicator communicator, short type, string protocol, bool secure) + { + instance_ = Util.getInstance(communicator); + traceLevel_ = instance_.traceLevels().network; + traceCategory_ = instance_.traceLevels().networkCat; + logger_ = instance_.initializationData().logger; + properties_ = instance_.initializationData().properties; + type_ = type; + protocol_ = protocol; + secure_ = secure; + } + + public ProtocolInstance(Instance instance, short type, string protocol, bool secure) + { + instance_ = instance; + traceLevel_ = instance_.traceLevels().network; + traceCategory_ = instance_.traceLevels().networkCat; + logger_ = instance_.initializationData().logger; + properties_ = instance_.initializationData().properties; + type_ = type; + protocol_ = protocol; + secure_ = secure; + } + + public int traceLevel() + { + return traceLevel_; + } + + public string traceCategory() + { + return traceCategory_; + } + + public Ice.Logger logger() + { + return logger_; + } + + public string protocol() + { + return protocol_; + } + + public short type() + { + return type_; + } + + public bool secure() + { + return secure_; + } + + public Ice.Properties properties() + { + return properties_; + } + + public bool preferIPv6() + { + return instance_.preferIPv6(); + } + + public int protocolSupport() + { + return instance_.protocolSupport(); + } + + public string defaultHost() + { + return instance_.defaultsAndOverrides().defaultHost; + } + + public EndPoint defaultSourceAddress() + { + return instance_.defaultsAndOverrides().defaultSourceAddress; + } + + public Ice.EncodingVersion defaultEncoding() + { + return instance_.defaultsAndOverrides().defaultEncoding; + } + + public int defaultTimeout() + { + return instance_.defaultsAndOverrides().defaultTimeout; + } + + public NetworkProxy networkProxy() + { + return instance_.networkProxy(); + } + + public int messageSizeMax() + { + return instance_.messageSizeMax(); + } + +#if !SILVERLIGHT + public void resolve(string host, int port, Ice.EndpointSelectionType type, IPEndpointI endpt, + EndpointI_connectors callback) + { + instance_.endpointHostResolver().resolve(host, port, type, endpt, callback); + } +#endif + + public BufSizeWarnInfo getBufSizeWarn(short type) + { + return instance_.getBufSizeWarn(type); + } + + public void setSndBufSizeWarn(short type, int size) + { + instance_.setSndBufSizeWarn(type, size); + } + + public void setRcvBufSizeWarn(short type, int size) + { + instance_.setRcvBufSizeWarn(type, size); + } + + protected Instance instance_; + protected int traceLevel_; + protected string traceCategory_; + protected Ice.Logger logger_; + protected Ice.Properties properties_; + protected string protocol_; + protected short type_; + protected bool secure_; + } + +} diff --git a/csharp/src/Ice/ProtocolPluginFacade.cs b/csharp/src/Ice/ProtocolPluginFacade.cs new file mode 100644 index 00000000000..3d924e4c186 --- /dev/null +++ b/csharp/src/Ice/ProtocolPluginFacade.cs @@ -0,0 +1,80 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + public interface ProtocolPluginFacade + { + // + // Get the Communicator instance with which this facade is + // associated. + // + Ice.Communicator getCommunicator(); + + // + // Register an EndpointFactory. + // + void addEndpointFactory(EndpointFactory factory); + + // + // Get an EndpointFactory. + // + EndpointFactory getEndpointFactory(short type); + + // + // Obtain the type for a name. + // + System.Type findType(string name); + } + + public sealed class ProtocolPluginFacadeI : ProtocolPluginFacade + { + public ProtocolPluginFacadeI(Ice.Communicator communicator) + { + _communicator = communicator; + _instance = IceInternal.Util.getInstance(communicator); + } + + // + // Get the Communicator instance with which this facade is + // associated. + // + public Ice.Communicator getCommunicator() + { + return _communicator; + } + + // + // Register an EndpointFactory. + // + public void addEndpointFactory(EndpointFactory factory) + { + _instance.endpointFactoryManager().add(factory); + } + + // + // Get an EndpointFactory. + // + public EndpointFactory getEndpointFactory(short type) + { + return _instance.endpointFactoryManager().get(type); + } + + // + // Obtain the type for a name. + // + public System.Type findType(string name) + { + return AssemblyUtil.findType(_instance, name); + } + + private Instance _instance; + private Ice.Communicator _communicator; + } +} diff --git a/csharp/src/Ice/Proxy.cs b/csharp/src/Ice/Proxy.cs new file mode 100644 index 00000000000..22ea3f51874 --- /dev/null +++ b/csharp/src/Ice/Proxy.cs @@ -0,0 +1,2611 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using IceUtilInternal; +using Ice.Instrumentation; + +namespace Ice +{ + /// <summary> + /// Delegate for a successful <code>ice_isA</code> invocation. + /// <param name="ret__">True if the remote object supports the type, false otherwise.</param> + /// </summary> + public delegate void Callback_Object_ice_isA(bool ret__); + + /// <summary> + /// Delegate for a successful <code>ice_ids</code> invocation. + /// <param name="ret__">The array of Slice type ids supported by the remote object.</param> + /// </summary> + public delegate void Callback_Object_ice_ids(string[] ret__); + + /// <summary> + /// Delegate for a successful <code>ice_id</code> invocation. + /// <param name="ret__">The Slice type id of the most-derived interface supported by the remote object.</param> + /// </summary> + public delegate void Callback_Object_ice_id(string ret__); + + /// <summary> + /// Delegate for a successful <code>ice_ping</code> invocation. + /// </summary> + public delegate void Callback_Object_ice_ping(); + + /// <summary> + /// Delegate for a successful <code>ice_invoke</code> invocation. + /// <param name="ret__">True if the invocation succeeded, or false if the invocation + /// raised a user exception.</param> + /// <param name="outEncaps">The encoded out-parameters or user exception.</param> + /// </summary> + public delegate void Callback_Object_ice_invoke(bool ret__, byte[] outEncaps); + + /// <summary> + /// Delegate for a successful <code>ice_getConnection</code> invocation. + /// <param name="ret__">The connection used by the proxy.</param> + /// </summary> + public delegate void Callback_Object_ice_getConnection(Connection ret__); + + /// <summary> + /// Base interface of all object proxies. + /// </summary> + public interface ObjectPrx + { + /// <summary> + /// Returns the communicator that created this proxy. + /// </summary> + /// <returns>The communicator that created this proxy.</returns> + Communicator ice_getCommunicator(); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id__">The type ID of the Slice interface to test against.</param> + /// <returns>True if the target object has the interface specified by id__ or derives + /// from the interface specified by id__.</returns> + bool ice_isA(string id__); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id__">The type ID of the Slice interface to test against.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>True if the target object has the interface specified by id__ or derives + /// from the interface specified by id__.</returns> + bool ice_isA(string id__, Dictionary<string, string> context__); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id">The type ID of the Slice interface to test against.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_isA> begin_ice_isA(string id); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id">The type ID of the Slice interface to test against.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_isA> begin_ice_isA(string id, Dictionary<string, string> context__); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id">The type ID of the Slice interface to test against.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_isA(string id, AsyncCallback cb__, object cookie__); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id">The type ID of the Slice interface to test against.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_isA(string id, Dictionary<string, string> context__, AsyncCallback cb__, object cookie__); + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="r__">The asynchronous result object returned by <code>begin_ice_isA</code>.</param> + /// <returns>True if the object supports the Slice interface, false otherwise.</returns> + bool end_ice_isA(AsyncResult r__); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + void ice_ping(); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + void ice_ping(Dictionary<string, string> context__); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_ping> begin_ice_ping(); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_ping> begin_ice_ping(Dictionary<string, string> context__); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_ping(AsyncCallback cb__, object cookie__); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_ping(Dictionary<string, string> context__, AsyncCallback cb__, object cookie__); + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <param name="r__">The asynchronous result object returned by <code>begin_ice_ping</code>.</param> + void end_ice_ping(AsyncResult r__); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <returns>The Slice type IDs of the interfaces supported by the target object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + string[] ice_ids(); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>The Slice type IDs of the interfaces supported by the target object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + string[] ice_ids(Dictionary<string, string> context__); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_ids> begin_ice_ids(); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_ids> begin_ice_ids(Dictionary<string, string> context__); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_ids(AsyncCallback cb__, object cookie__); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_ids(Dictionary<string, string> context__, AsyncCallback cb__, object cookie__); + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <param name="r__">The asynchronous result object returned by <code>begin_ice_ids</code>.</param> + /// <returns>The Slice type IDs of the interfaces supported by the target object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + string[] end_ice_ids(AsyncResult r__); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <returns>The Slice type ID of the most-derived interface.</returns> + string ice_id(); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>The Slice type ID of the most-derived interface.</returns> + string ice_id(Dictionary<string, string> context__); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_id> begin_ice_id(); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_id> begin_ice_id(Dictionary<string, string> context__); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_id(AsyncCallback cb__, object cookie__); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_id(Dictionary<string, string> context__, AsyncCallback cb__, object cookie__); + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <param name="r__">The asynchronous result object returned by <code>begin_ice_id</code>.</param> + /// <returns>The Slice type ID of the most-derived interface.</returns> + string end_ice_id(AsyncResult r__); + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="outEncaps">The encoded out-paramaters and return value + /// for the operation. The return value follows any out-parameters.</param> + /// <returns>If the operation completed successfully, the return value + /// is true. If the operation raises a user exception, + /// the return value is false; in this case, outEncaps + /// contains the encoded user exception. If the operation raises a run-time exception, + /// it throws it directly.</returns> + bool ice_invoke(string operation, OperationMode mode, byte[] inEncaps, out byte[] outEncaps); + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="outEncaps">The encoded out-paramaters and return value + /// for the operation. The return value follows any out-parameters.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>If the operation completed successfully, the return value + /// is true. If the operation raises a user exception, + /// the return value is false; in this case, outEncaps + /// contains the encoded user exception. If the operation raises a run-time exception, + /// it throws it directly.</returns> + bool ice_invoke(string operation, OperationMode mode, byte[] inEncaps, out byte[] outEncaps, + Dictionary<string, string> context__); + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_invoke> begin_ice_invoke(string operation, OperationMode mode, + byte[] inEncaps); + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_invoke> begin_ice_invoke(string operation, OperationMode mode, + byte[] inEncaps, + Dictionary<string, string> context__); + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_invoke(string operation, OperationMode mode, byte[] inEncaps, AsyncCallback cb__, + object cookie__); + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_invoke(string operation, OperationMode mode, byte[] inEncaps, + Dictionary<string, string> context__, AsyncCallback cb__, object cookie__); + + /// <summary> + /// Completes a dynamic invocation. + /// </summary> + /// <param name="outEncaps">The encoded out parameters or user exception.</param> + /// <param name="r__">The asynchronous result object returned by <code>begin_ice_invoke</code>.</param> + /// <returns>If the operation completed successfully, the return value + /// is true. If the operation raises a user exception, + /// the return value is false; in this case, outEncaps + /// contains the encoded user exception. If the operation raises a run-time exception, + /// it throws it directly.</returns> + bool end_ice_invoke(out byte[] outEncaps, AsyncResult r__); + + /// <summary> + /// Returns the identity embedded in this proxy. + /// <returns>The identity of the target object.</returns> + /// </summary> + Identity ice_getIdentity(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the per-proxy context. + /// <param name="newIdentity">The identity for the new proxy.</param> + /// <returns>The proxy with the new identity.</returns> + /// </summary> + ObjectPrx ice_identity(Identity newIdentity); + + /// <summary> + /// Returns the per-proxy context for this proxy. + /// </summary> + /// <returns>The per-proxy context. If the proxy does not have a per-proxy (implicit) context, the return value + /// is null.</returns> + Dictionary<string, string> ice_getContext(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the per-proxy context. + /// </summary> + /// <param name="newContext">The context for the new proxy.</param> + /// <returns>The proxy with the new per-proxy context.</returns> + ObjectPrx ice_context(Dictionary<string, string> newContext); + + /// <summary> + /// Returns the facet for this proxy. + /// </summary> + /// <returns>The facet for this proxy. If the proxy uses the default facet, the return value is the + /// empty string.</returns> + string ice_getFacet(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the facet. + /// </summary> + /// <param name="newFacet">The facet for the new proxy.</param> + /// <returns>The proxy with the new facet.</returns> + ObjectPrx ice_facet(string newFacet); + + /// <summary> + /// Returns the adapter ID for this proxy. + /// </summary> + /// <returns>The adapter ID. If the proxy does not have an adapter ID, the return value is the + /// empty string.</returns> + string ice_getAdapterId(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the adapter ID. + /// </summary> + /// <param name="newAdapterId">The adapter ID for the new proxy.</param> + /// <returns>The proxy with the new adapter ID.</returns> + ObjectPrx ice_adapterId(string newAdapterId); + + /// <summary> + /// Returns the endpoints used by this proxy. + /// </summary> + /// <returns>The endpoints used by this proxy.</returns> + Endpoint[] ice_getEndpoints(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the endpoints. + /// </summary> + /// <param name="newEndpoints">The endpoints for the new proxy.</param> + /// <returns>The proxy with the new endpoints.</returns> + ObjectPrx ice_endpoints(Endpoint[] newEndpoints); + + /// <summary> + /// Returns the locator cache timeout of this proxy. + /// </summary> + /// <returns>The locator cache timeout value (in seconds).</returns> + int ice_getLocatorCacheTimeout(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the locator cache timeout. + /// </summary> + /// <param name="timeout">The new locator cache timeout (in seconds).</param> + ObjectPrx ice_locatorCacheTimeout(int timeout); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the invocation timeout. + /// </summary> + /// <param name="timeout">The new invocation timeout (in seconds).</param> + ObjectPrx ice_invocationTimeout(int timeout); + + /// <summary> + /// Returns the invocation timeout of this proxy. + /// </summary> + /// <returns>The invocation timeout value (in seconds).</returns> + int ice_getInvocationTimeout(); + + /// <summary> + /// Returns whether this proxy caches connections. + /// </summary> + /// <returns>True if this proxy caches connections; false, otherwise.</returns> + bool ice_isConnectionCached(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for connection caching. + /// </summary> + /// <param name="newCache">True if the new proxy should cache connections; false, otherwise.</param> + /// <returns>The new proxy with the specified caching policy.</returns> + ObjectPrx ice_connectionCached(bool newCache); + + /// <summary> + /// Returns how this proxy selects endpoints (randomly or ordered). + /// </summary> + /// <returns>The endpoint selection policy.</returns> + EndpointSelectionType ice_getEndpointSelection(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the endpoint selection policy. + /// </summary> + /// <param name="newType">The new endpoint selection policy.</param> + /// <returns>The new proxy with the specified endpoint selection policy.</returns> + ObjectPrx ice_endpointSelection(EndpointSelectionType newType); + + /// <summary> + /// Returns whether this proxy communicates only via secure endpoints. + /// </summary> + /// <returns>True if this proxy communicates only vi secure endpoints; false, otherwise.</returns> + bool ice_isSecure(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for how it selects endpoints. + /// </summary> + /// <param name="b"> If b is true, only endpoints that use a secure transport are + /// used by the new proxy. If b is false, the returned proxy uses both secure and insecure + /// endpoints.</param> + /// <returns>The new proxy with the specified selection policy.</returns> + ObjectPrx ice_secure(bool b); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the encoding used to marshal + /// parameters. + /// </summary> + /// <param name="e">The encoding version to use to marshal requests parameters.</param> + /// <returns>The new proxy with the specified encoding version.</returns> + ObjectPrx ice_encodingVersion(Ice.EncodingVersion e); + + /// <summary>Returns the encoding version used to marshal requests parameters.</summary> + /// <returns>The encoding version.</returns> + Ice.EncodingVersion ice_getEncodingVersion(); + + /// <summary> + /// Returns whether this proxy prefers secure endpoints. + /// </summary> + /// <returns>True if the proxy always attempts to invoke via secure endpoints before it + /// attempts to use insecure endpoints; false, otherwise.</returns> + bool ice_isPreferSecure(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for its endpoint selection policy. + /// </summary> + /// <param name="b">If b is true, the new proxy will use secure endpoints for invocations + /// and only use insecure endpoints if an invocation cannot be made via secure endpoints. If b is + /// false, the proxy prefers insecure endpoints to secure ones.</param> + /// <returns>The new proxy with the new endpoint selection policy.</returns> + ObjectPrx ice_preferSecure(bool b); + + /// <summary> + /// Returns the router for this proxy. + /// </summary> + /// <returns>The router for the proxy. If no router is configured for the proxy, the return value + /// is null.</returns> + Ice.RouterPrx ice_getRouter(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the router. + /// </summary> + /// <param name="router">The router for the new proxy.</param> + /// <returns>The new proxy with the specified router.</returns> + ObjectPrx ice_router(Ice.RouterPrx router); + + /// <summary> + /// Returns the locator for this proxy. + /// </summary> + /// <returns>The locator for this proxy. If no locator is configured, the return value is null.</returns> + Ice.LocatorPrx ice_getLocator(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the locator. + /// </summary> + /// <param name="locator">The locator for the new proxy.</param> + /// <returns>The new proxy with the specified locator.</returns> + ObjectPrx ice_locator(Ice.LocatorPrx locator); + + /// <summary> + /// Returns whether this proxy uses collocation optimization. + /// </summary> + /// <returns>True if the proxy uses collocation optimization; false, otherwise.</returns> + bool ice_isCollocationOptimized(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for collocation optimization. + /// </summary> + /// <param name="b">True if the new proxy enables collocation optimization; false, otherwise.</param> + /// <returns>The new proxy the specified collocation optimization.</returns> + ObjectPrx ice_collocationOptimized(bool b); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses twoway invocations. + /// </summary> + /// <returns>A new proxy that uses twoway invocations.</returns> + ObjectPrx ice_twoway(); + + /// <summary> + /// Returns whether this proxy uses twoway invocations. + /// </summary> + /// <returns>True if this proxy uses twoway invocations; false, otherwise.</returns> + bool ice_isTwoway(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses oneway invocations. + /// </summary> + /// <returns>A new proxy that uses oneway invocations.</returns> + ObjectPrx ice_oneway(); + + /// <summary> + /// Returns whether this proxy uses oneway invocations. + /// </summary> + /// <returns>True if this proxy uses oneway invocations; false, otherwise.</returns> + bool ice_isOneway(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses batch oneway invocations. + /// </summary> + /// <returns>A new proxy that uses batch oneway invocations.</returns> + ObjectPrx ice_batchOneway(); + + /// <summary> + /// Returns whether this proxy uses batch oneway invocations. + /// </summary> + /// <returns>True if this proxy uses batch oneway invocations; false, otherwise.</returns> + bool ice_isBatchOneway(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses datagram invocations. + /// </summary> + /// <returns>A new proxy that uses datagram invocations.</returns> + ObjectPrx ice_datagram(); + + /// <summary> + /// Returns whether this proxy uses datagram invocations. + /// </summary> + /// <returns>True if this proxy uses datagram invocations; false, otherwise.</returns> + bool ice_isDatagram(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses batch datagram invocations. + /// </summary> + /// <returns>A new proxy that uses batch datagram invocations.</returns> + ObjectPrx ice_batchDatagram(); + + /// <summary> + /// Returns whether this proxy uses batch datagram invocations. + /// </summary> + /// <returns>True if this proxy uses batch datagram invocations; false, otherwise.</returns> + bool ice_isBatchDatagram(); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for compression. + /// </summary> + /// <param name="co">True enables compression for the new proxy; false disables compression.</param> + /// <returns>A new proxy with the specified compression setting.</returns> + ObjectPrx ice_compress(bool co); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for its timeout setting. + /// </summary> + /// <param name="t">The timeout for the new proxy in milliseconds.</param> + /// <returns>A new proxy with the specified timeout.</returns> + ObjectPrx ice_timeout(int t); + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for its connection ID. + /// </summary> + /// <param name="connectionId">The connection ID for the new proxy. An empty string removes the + /// connection ID.</param> + /// <returns>A new proxy with the specified connection ID.</returns> + ObjectPrx ice_connectionId(string connectionId); + + /// <summary> + /// Returns the connection id of this proxy. + /// </summary> + /// <returns>The connection id.</returns> + string ice_getConnectionId(); + + /// <summary> + /// Returns the Connection for this proxy. If the proxy does not yet have an established connection, + /// it first attempts to create a connection. + /// </summary> + /// <returns>The Connection for this proxy.</returns> + /// <exception name="CollocationOptimizationException">If the proxy uses collocation optimization and denotes a + /// collocated object.</exception> + Connection ice_getConnection(); + + /// <summary> + /// Asynchronously gets the connection for this proxy. + /// </summary> + /// <returns>An asynchronous result object.</returns> + AsyncResult<Callback_Object_ice_getConnection> begin_ice_getConnection(); + + /// <summary> + /// Asynchronously gets the connection for this proxy. + /// </summary> + /// <param name="cb__">A callback to be invoked when the invocation completes.</param> + /// <param name="cookie__">Application-specific data to be stored in the result.</param> + /// <returns>An asynchronous result object.</returns> + AsyncResult begin_ice_getConnection(AsyncCallback cb__, object cookie__); + + /// <summary> + /// Asynchronously gets the connection for this proxy. + /// </summary> + /// <param name="r__">The asynchronous result object returned by <code>begin_ice_getConnection</code>.</param> + /// <returns>The connection.</returns> + Connection end_ice_getConnection(AsyncResult r__); + + /// <summary> + /// Returns the cached Connection for this proxy. If the proxy does not yet have an established + /// connection, it does not attempt to create a connection. + /// </summary> + /// <returns>The cached Connection for this proxy (null if the proxy does not have + /// an established connection).</returns> + /// <exception name="CollocationOptimizationException">If the proxy uses collocation optimization and denotes a + /// collocated object.</exception> + Connection ice_getCachedConnection(); + + /// <summary> + /// Flushes any pending batched requests for this communicator. The call blocks until the flush is complete. + /// </summary> + void ice_flushBatchRequests(); + + AsyncResult begin_ice_flushBatchRequests(); + AsyncResult begin_ice_flushBatchRequests(AsyncCallback cb__, object cookie__); + + void end_ice_flushBatchRequests(AsyncResult r__); + } + + /// <summary> + /// Base class of all object proxies. + /// </summary> + public class ObjectPrxHelperBase : ObjectPrx + { + /// <summary> + /// Returns a hash code for this proxy. + /// </summary> + /// <returns>The hash code.</returns> + public override int GetHashCode() + { + return _reference.GetHashCode(); + } + + /// <summary> + /// Returns the communicator that created this proxy. + /// </summary> + /// <returns>The communicator that created this proxy.</returns> + public Communicator ice_getCommunicator() + { + return _reference.getCommunicator(); + } + + /// <summary> + /// Returns the stringified form of this proxy. + /// </summary> + /// <returns>The stringified proxy.</returns> + public override string ToString() + { + return _reference.ToString(); + } + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id__">The type ID of the Slice interface to test against.</param> + /// <returns>True if the target object has the interface specified by id__ or derives + /// from the interface specified by id__.</returns> + public bool ice_isA(string id__) + { + return ice_isA(id__, null, false); + } + + /// <summary> + /// Tests whether this object supports a specific Slice interface. + /// </summary> + /// <param name="id__">The type ID of the Slice interface to test against.</param> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>True if the target object has the interface specified by id__ or derives + /// from the interface specified by id__.</returns> + public bool ice_isA(string id__, Dictionary<string, string> context__) + { + return ice_isA(id__, context__, true); + } + + private bool ice_isA(string id__, Dictionary<string, string> context__, bool explicitCtx__) + { + checkTwowayOnly__(__ice_isA_name); + return end_ice_isA(begin_ice_isA(id__, context__, explicitCtx__, true, null, null)); + } + + public AsyncResult<Callback_Object_ice_isA> begin_ice_isA(string id) + { + return begin_ice_isA(id, null, false, false, null, null); + } + + public AsyncResult<Callback_Object_ice_isA> begin_ice_isA(string id, Dictionary<string, string> context__) + { + return begin_ice_isA(id, context__, true, false, null, null); + } + + public AsyncResult begin_ice_isA(string id, AsyncCallback cb__, object cookie__) + { + return begin_ice_isA(id, null, false, false, cb__, cookie__); + } + + public AsyncResult begin_ice_isA(string id, Dictionary<string, string> context__, AsyncCallback cb__, + object cookie__) + { + return begin_ice_isA(id, context__, true, false, cb__, cookie__); + } + + internal const string __ice_isA_name = "ice_isA"; + + public bool end_ice_isA(AsyncResult r__) + { + IceInternal.OutgoingAsync outAsync__ = IceInternal.OutgoingAsync.check(r__, this, __ice_isA_name); + try + { + if(!outAsync__.wait()) + { + try + { + outAsync__.throwUserException(); + } + catch(Ice.UserException ex__) + { + throw new Ice.UnknownUserException(ex__.ice_name(), ex__); + } + } + bool ret__; + IceInternal.BasicStream is__ = outAsync__.startReadParams(); + ret__ = is__.readBool(); + outAsync__.endReadParams(); + return ret__; + } + finally + { + if(outAsync__ != null) + { + outAsync__.cacheMessageBuffers(); + } + } + } + + private AsyncResult<Callback_Object_ice_isA> begin_ice_isA(string id, + Dictionary<string, string> context__, + bool explicitCtx__, + bool synchronous__, + Ice.AsyncCallback cb__, + object cookie__) + { + checkAsyncTwowayOnly__(__ice_isA_name); + + IceInternal.TwowayOutgoingAsync<Callback_Object_ice_isA> result__ = + getTwowayOutgoingAsync<Callback_Object_ice_isA>(__ice_isA_name, ice_isA_completed__, cookie__); + if(cb__ != null) + { + result__.whenCompletedWithAsyncCallback(cb__); + } + + try + { + result__.prepare(__ice_isA_name, OperationMode.Nonmutating, context__, explicitCtx__, synchronous__); + IceInternal.BasicStream os__ = result__.startWriteParams(FormatType.DefaultFormat); + os__.writeString(id); + result__.endWriteParams(); + result__.invoke(); + } + catch(Ice.Exception ex__) + { + result__.abort(ex__); + } + return result__; + } + + protected IceInternal.TwowayOutgoingAsync<T> + getTwowayOutgoingAsync<T>(string operation, IceInternal.ProxyTwowayCallback<T> cb, + object cookie) { + bool haveEntry = false; + IceInternal.BasicStream iss = null; + IceInternal.BasicStream os = null; + + if(_reference.getInstance().cacheMessageBuffers() > 0) + { + lock(this) + { + if(_streamCache != null && _streamCache.Count > 0) + { + haveEntry = true; + iss = _streamCache.First.Value.iss; + os = _streamCache.First.Value.os; + + _streamCache.RemoveFirst(); + } + } + } + if(!haveEntry) + { + return new IceInternal.TwowayOutgoingAsync<T>(this, operation, cb, cookie); + } + else + { + return new IceInternal.TwowayOutgoingAsync<T>(this, operation, cb, cookie, iss, os); + } + } + + protected IceInternal.OnewayOutgoingAsync<T> + getOnewayOutgoingAsync<T>(string operation, IceInternal.ProxyOnewayCallback<T> cb, + object cookie) { + bool haveEntry = false; + IceInternal.BasicStream iss = null; + IceInternal.BasicStream os = null; + + if(_reference.getInstance().cacheMessageBuffers() > 0) + { + lock(this) + { + if(_streamCache != null && _streamCache.Count > 0) + { + haveEntry = true; + iss = _streamCache.First.Value.iss; + os = _streamCache.First.Value.os; + _streamCache.RemoveFirst(); + } + } + } + if(!haveEntry) + { + return new IceInternal.OnewayOutgoingAsync<T>(this, operation, cb, cookie); + } + else + { + return new IceInternal.OnewayOutgoingAsync<T>(this, operation, cb, cookie, iss, os); + } + } + + public void + cacheMessageBuffers(IceInternal.BasicStream iss, IceInternal.BasicStream os) + { + lock(this) + { + if(_streamCache == null) + { + _streamCache = new LinkedList<StreamCacheEntry>(); + } + StreamCacheEntry cacheEntry; + cacheEntry.iss = iss; + cacheEntry.os = os; + _streamCache.AddLast(cacheEntry); + } + } + + private void ice_isA_completed__(AsyncResult r__, Callback_Object_ice_isA cb__, Ice.ExceptionCallback excb__) + { + bool ret__; + try + { + ret__ = end_ice_isA(r__); + } + catch(Ice.Exception ex__) + { + if(excb__ != null) + { + excb__(ex__); + } + return; + } + if(cb__ != null) + { + cb__(ret__); + } + } + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + public void ice_ping() + { + ice_ping(null, false); + } + + /// <summary> + /// Tests whether the target object of this proxy can be reached. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + public void ice_ping(Dictionary<string, string> context__) + { + ice_ping(context__, true); + } + + private void ice_ping(Dictionary<string, string> context__, bool explicitCtx__) + { + end_ice_ping(begin_ice_ping(context__, explicitCtx__, true, null, null)); + } + + public AsyncResult<Callback_Object_ice_ping> begin_ice_ping() + { + return begin_ice_ping(null, false, false, null, null); + } + + public AsyncResult<Callback_Object_ice_ping> begin_ice_ping(Dictionary<string, string> context__) + { + return begin_ice_ping(context__, true, false, null, null); + } + + public AsyncResult begin_ice_ping(AsyncCallback cb__, object cookie__) + { + return begin_ice_ping(null, false, false, cb__, cookie__); + } + + public AsyncResult begin_ice_ping(Dictionary<string, string> context__, AsyncCallback cb__, object cookie__) + { + return begin_ice_ping(null, false, false, cb__, cookie__); + } + + internal const string __ice_ping_name = "ice_ping"; + + public void end_ice_ping(AsyncResult r__) + { + end__(r__, __ice_ping_name); + } + + private AsyncResult<Callback_Object_ice_ping> begin_ice_ping(Dictionary<string, string> context__, + bool explicitCtx__, + bool synchronous__, + Ice.AsyncCallback cb__, + object cookie__) + { + IceInternal.OnewayOutgoingAsync<Callback_Object_ice_ping> result__ = + getOnewayOutgoingAsync<Callback_Object_ice_ping>(__ice_ping_name, ice_ping_completed__, cookie__); + if(cb__ != null) + { + result__.whenCompletedWithAsyncCallback(cb__); + } + + try + { + result__.prepare(__ice_ping_name, OperationMode.Nonmutating, context__, explicitCtx__, synchronous__); + result__.writeEmptyParams(); + result__.invoke(); + } + catch(Ice.Exception ex__) + { + result__.abort(ex__); + } + return result__; + } + + private void ice_ping_completed__(Callback_Object_ice_ping cb) + { + if(cb != null) + { + cb(); + } + } + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <returns>The Slice type IDs of the interfaces supported by the target object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + public string[] ice_ids() + { + return ice_ids(null, false); + } + + /// <summary> + /// Returns the Slice type IDs of the interfaces supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>The Slice type IDs of the interfaces supported by the target object, in base-to-derived + /// order. The first element of the returned array is always ::Ice::Object.</returns> + public string[] ice_ids(Dictionary<string, string> context__) + { + return ice_ids(context__, true); + } + + private string[] ice_ids(Dictionary<string, string> context__, bool explicitCtx__) + { + + checkTwowayOnly__(__ice_ids_name); + return end_ice_ids(begin_ice_ids(context__, explicitCtx__, true, null, null)); + } + + public AsyncResult<Callback_Object_ice_ids> begin_ice_ids() + { + return begin_ice_ids(null, false, false, null, null); + } + + public AsyncResult<Callback_Object_ice_ids> begin_ice_ids(Dictionary<string, string> context__) + { + return begin_ice_ids(context__, true, false, null, null); + } + + public AsyncResult begin_ice_ids(AsyncCallback cb__, object cookie__) + { + return begin_ice_ids(null, false, false, cb__, cookie__); + } + + public AsyncResult begin_ice_ids(Dictionary<string, string> context__, AsyncCallback cb__, object cookie__) + { + return begin_ice_ids(null, false, false, cb__, cookie__); + } + + internal const string __ice_ids_name = "ice_ids"; + + public string[] end_ice_ids(AsyncResult r__) + { + IceInternal.OutgoingAsync outAsync__ = IceInternal.OutgoingAsync.check(r__, this, __ice_ids_name); + try + { + if(!outAsync__.wait()) + { + try + { + outAsync__.throwUserException(); + } + catch(Ice.UserException ex__) + { + throw new Ice.UnknownUserException(ex__.ice_name(), ex__); + } + } + string[] ret__; + IceInternal.BasicStream is__ = outAsync__.startReadParams(); + ret__ = is__.readStringSeq(); + outAsync__.endReadParams(); + return ret__; + } + finally + { + if(outAsync__ != null) + { + outAsync__.cacheMessageBuffers(); + } + } + } + + private AsyncResult<Callback_Object_ice_ids> begin_ice_ids(Dictionary<string, string> context__, + bool explicitCtx__, + bool synchronous__, + Ice.AsyncCallback cb__, + object cookie__) + { + checkAsyncTwowayOnly__(__ice_ids_name); + + IceInternal.TwowayOutgoingAsync<Callback_Object_ice_ids> result__ = + getTwowayOutgoingAsync<Callback_Object_ice_ids>(__ice_ids_name, ice_ids_completed__, cookie__); + if(cb__ != null) + { + result__.whenCompletedWithAsyncCallback(cb__); + } + + try + { + result__.prepare(__ice_ids_name, OperationMode.Nonmutating, context__, explicitCtx__, synchronous__); + result__.writeEmptyParams(); + result__.invoke(); + } + catch(Ice.Exception ex__) + { + result__.abort(ex__); + } + return result__; + } + + private void ice_ids_completed__(AsyncResult r__, Callback_Object_ice_ids cb__, Ice.ExceptionCallback excb__) + { + string[] ret__; + try + { + ret__ = end_ice_ids(r__); + } + catch(Ice.Exception ex__) + { + if(excb__ != null) + { + excb__(ex__); + } + return; + } + if(cb__ != null) + { + cb__(ret__); + } + } + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <returns>The Slice type ID of the most-derived interface.</returns> + public string ice_id() + { + return ice_id(null, false); + } + + /// <summary> + /// Returns the Slice type ID of the most-derived interface supported by the target object of this proxy. + /// </summary> + /// <param name="context__">The context dictionary for the invocation.</param> + /// <returns>The Slice type ID of the most-derived interface.</returns> + public string ice_id(Dictionary<string, string> context__) + { + return ice_id(context__, true); + } + + private string ice_id(Dictionary<string, string> context__, bool explicitCtx__) + { + checkTwowayOnly__(__ice_id_name); + return end_ice_id(begin_ice_id(context__, explicitCtx__, true, null, null)); + } + + public AsyncResult<Callback_Object_ice_id> begin_ice_id() + { + return begin_ice_id(null, false, false, null, null); + } + + public AsyncResult<Callback_Object_ice_id> begin_ice_id(Dictionary<string, string> context__) + { + return begin_ice_id(context__, true, false, null, null); + } + + public AsyncResult begin_ice_id(AsyncCallback cb__, object cookie__) + { + return begin_ice_id(null, false, false, cb__, cookie__); + } + + public AsyncResult begin_ice_id(Dictionary<string, string> context__, AsyncCallback cb__, object cookie__) + { + return begin_ice_id(null, false, false, cb__, cookie__); + } + + internal const string __ice_id_name = "ice_id"; + + public string end_ice_id(AsyncResult r__) + { + IceInternal.OutgoingAsync outAsync__ = IceInternal.OutgoingAsync.check(r__, this, __ice_id_name); + try + { + if(!outAsync__.wait()) + { + try + { + outAsync__.throwUserException(); + } + catch(Ice.UserException ex__) + { + throw new Ice.UnknownUserException(ex__.ice_name(), ex__); + } + } + string ret__; + IceInternal.BasicStream is__ = outAsync__.startReadParams(); + ret__ = is__.readString(); + outAsync__.endReadParams(); + return ret__; + } + finally + { + if(outAsync__ != null) + { + outAsync__.cacheMessageBuffers(); + } + } + } + + private AsyncResult<Callback_Object_ice_id> begin_ice_id(Dictionary<string, string> context__, + bool explicitCtx__, + bool synchronous__, + Ice.AsyncCallback cb__, + object cookie__) + { + checkAsyncTwowayOnly__(__ice_id_name); + + IceInternal.TwowayOutgoingAsync<Callback_Object_ice_id> result__ = + getTwowayOutgoingAsync<Callback_Object_ice_id>(__ice_id_name, ice_id_completed__, cookie__); + if(cb__ != null) + { + result__.whenCompletedWithAsyncCallback(cb__); + } + + try + { + result__.prepare(__ice_id_name, OperationMode.Nonmutating, context__, explicitCtx__, synchronous__); + result__.writeEmptyParams(); + result__.invoke(); + } + catch(Ice.Exception ex__) + { + result__.abort(ex__); + } + return result__; + } + + private void ice_id_completed__(AsyncResult r__, Callback_Object_ice_id cb__, Ice.ExceptionCallback excb__) + { + string ret__; + try + { + ret__ = end_ice_id(r__); + } + catch(Ice.Exception ex__) + { + if(excb__ != null) + { + excb__(ex__); + } + return; + } + if(cb__ != null) + { + cb__(ret__); + } + } + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="outEncaps">The encoded out-paramaters and return value + /// for the operation. The return value follows any out-parameters.</param> + /// <returns>If the operation completed successfully, the return value + /// is true. If the operation raises a user exception, + /// the return value is false; in this case, outEncaps + /// contains the encoded user exception. If the operation raises a run-time exception, + /// it throws it directly.</returns> + public bool ice_invoke(string operation, OperationMode mode, byte[] inEncaps, out byte[] outEncaps) + { + return ice_invoke(operation, mode, inEncaps, out outEncaps, null, false); + } + + /// <summary> + /// Invokes an operation dynamically. + /// </summary> + /// <param name="operation">The name of the operation to invoke.</param> + /// <param name="mode">The operation mode (normal or idempotent).</param> + /// <param name="inEncaps">The encoded in-parameters for the operation.</param> + /// <param name="outEncaps">The encoded out-paramaters and return value + /// for the operation. The return value follows any out-parameters.</param> + /// <param name="context">The context dictionary for the invocation.</param> + /// <returns>If the operation completed successfully, the return value + /// is true. If the operation raises a user exception, + /// the return value is false; in this case, outEncaps + /// contains the encoded user exception. If the operation raises a run-time exception, + /// it throws it directly.</returns> + public bool ice_invoke(string operation, OperationMode mode, byte[] inEncaps, out byte[] outEncaps, + Dictionary<string, string> context) + { + return ice_invoke(operation, mode, inEncaps, out outEncaps, context, true); + } + + private bool ice_invoke(string operation, OperationMode mode, byte[] inEncaps, out byte[] outEncaps, + Dictionary<string, string> context, bool explicitCtx) + { + return end_ice_invoke(out outEncaps, begin_ice_invoke(operation, mode, inEncaps, context, explicitCtx, true, null, null)); + } + + public AsyncResult<Callback_Object_ice_invoke> begin_ice_invoke(string operation, + OperationMode mode, + byte[] inEncaps) + { + return begin_ice_invoke(operation, mode, inEncaps, null, false, false, null, null); + } + + public AsyncResult<Callback_Object_ice_invoke> begin_ice_invoke(string operation, + OperationMode mode, + byte[] inEncaps, + Dictionary<string, string> context__) + { + return begin_ice_invoke(operation, mode, inEncaps, context__, true, false, null, null); + } + + public AsyncResult begin_ice_invoke(string operation, OperationMode mode, byte[] inEncaps, AsyncCallback cb__, + object cookie__) + { + return begin_ice_invoke(operation, mode, inEncaps, null, false, false, cb__, cookie__); + } + + public AsyncResult begin_ice_invoke(string operation, OperationMode mode, byte[] inEncaps, + Dictionary<string, string> context__, AsyncCallback cb__, object cookie__) + { + return begin_ice_invoke(operation, mode, inEncaps, null, false, false, cb__, cookie__); + } + + internal const string __ice_invoke_name = "ice_invoke"; + + public bool end_ice_invoke(out byte[] outEncaps, AsyncResult r__) + { + IceInternal.OutgoingAsync outAsync__ = IceInternal.OutgoingAsync.check(r__, this, __ice_invoke_name); + try + { + bool ok = outAsync__.wait(); + if(_reference.getMode() == IceInternal.Reference.Mode.ModeTwoway) + { + outEncaps = outAsync__.readParamEncaps(); + } + else + { + outEncaps = null; // Satisfy compiler + } + return ok; + } + finally + { + if(outAsync__ != null) + { + outAsync__.cacheMessageBuffers(); + } + } + + } + + private AsyncResult<Callback_Object_ice_invoke> begin_ice_invoke(string operation, + OperationMode mode, + byte[] inEncaps, + Dictionary<string, string> context__, + bool explicitCtx__, + bool synchronous__, + Ice.AsyncCallback cb__, + object cookie__) + { + IceInternal.TwowayOutgoingAsync<Callback_Object_ice_invoke> result__ = + getTwowayOutgoingAsync<Callback_Object_ice_invoke>(__ice_invoke_name, ice_invoke_completed__, cookie__); + if(cb__ != null) + { + result__.whenCompletedWithAsyncCallback(cb__); + } + + try + { + result__.prepare(operation, mode, context__, explicitCtx__, synchronous__); + result__.writeParamEncaps(inEncaps); + result__.invoke(); + } + catch(Ice.Exception ex__) + { + result__.abort(ex__); + } + return result__; + } + + private void ice_invoke_completed__(AsyncResult r__, + Callback_Object_ice_invoke cb__, + Ice.ExceptionCallback excb__) + { + byte[] outEncaps; + bool ret__; + try + { + ret__ = end_ice_invoke(out outEncaps, r__); + } + catch(Ice.Exception ex__) + { + if(excb__ != null) + { + excb__(ex__); + } + return; + } + if(cb__ != null) + { + cb__(ret__, outEncaps); + } + } + + /// <summary> + /// Returns the identity embedded in this proxy. + /// <returns>The identity of the target object.</returns> + /// </summary> + public Identity ice_getIdentity() + { + return (Identity)_reference.getIdentity().Clone(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the per-proxy context. + /// <param name="newIdentity">The identity for the new proxy.</param> + /// <returns>The proxy with the new identity.</returns> + /// </summary> + public ObjectPrx ice_identity(Identity newIdentity) + { + if(newIdentity.name.Length == 0) + { + throw new IllegalIdentityException(); + } + if(newIdentity.Equals(_reference.getIdentity())) + { + return this; + } + else + { + ObjectPrxHelperBase proxy = new ObjectPrxHelperBase(); + proxy.setup(_reference.changeIdentity(newIdentity)); + return proxy; + } + } + + /// <summary> + /// Returns the per-proxy context for this proxy. + /// </summary> + /// <returns>The per-proxy context. If the proxy does not have a per-proxy (implicit) context, the return value + /// is null.</returns> + public Dictionary<string, string> ice_getContext() + { + return new Dictionary<string, string>(_reference.getContext()); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the per-proxy context. + /// </summary> + /// <param name="newContext">The context for the new proxy.</param> + /// <returns>The proxy with the new per-proxy context.</returns> + public ObjectPrx ice_context(Dictionary<string, string> newContext) + { + return newInstance(_reference.changeContext(newContext)); + } + + /// <summary> + /// Returns the facet for this proxy. + /// </summary> + /// <returns>The facet for this proxy. If the proxy uses the default facet, the return value is the + /// empty string.</returns> + public string ice_getFacet() + { + return _reference.getFacet(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the facet. + /// </summary> + /// <param name="newFacet">The facet for the new proxy.</param> + /// <returns>The proxy with the new facet.</returns> + public ObjectPrx ice_facet(string newFacet) + { + if(newFacet == null) + { + newFacet = ""; + } + + if(newFacet.Equals(_reference.getFacet())) + { + return this; + } + else + { + ObjectPrxHelperBase proxy = new ObjectPrxHelperBase(); + proxy.setup(_reference.changeFacet(newFacet)); + return proxy; + } + } + + /// <summary> + /// Returns the adapter ID for this proxy. + /// </summary> + /// <returns>The adapter ID. If the proxy does not have an adapter ID, the return value is the + /// empty string.</returns> + public string ice_getAdapterId() + { + return _reference.getAdapterId(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the adapter ID. + /// </summary> + /// <param name="newAdapterId">The adapter ID for the new proxy.</param> + /// <returns>The proxy with the new adapter ID.</returns> + public ObjectPrx ice_adapterId(string newAdapterId) + { + if(newAdapterId == null) + { + newAdapterId = ""; + } + + if(newAdapterId.Equals(_reference.getAdapterId())) + { + return this; + } + else + { + return newInstance(_reference.changeAdapterId(newAdapterId)); + } + } + + /// <summary> + /// Returns the endpoints used by this proxy. + /// </summary> + /// <returns>The endpoints used by this proxy.</returns> + public Endpoint[] ice_getEndpoints() + { + return (Endpoint[])_reference.getEndpoints().Clone(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the endpoints. + /// </summary> + /// <param name="newEndpoints">The endpoints for the new proxy.</param> + /// <returns>The proxy with the new endpoints.</returns> + public ObjectPrx ice_endpoints(Endpoint[] newEndpoints) + { + if(Arrays.Equals(newEndpoints, _reference.getEndpoints())) + { + return this; + } + else + { + IceInternal.EndpointI[] endpts = new IceInternal.EndpointI[newEndpoints.Length]; + for(int i = 0; i < newEndpoints.Length; ++i) + { + endpts[i] = (IceInternal.EndpointI)newEndpoints[i]; + } + return newInstance(_reference.changeEndpoints(endpts)); + } + } + + /// <summary> + /// Returns the locator cache timeout of this proxy. + /// </summary> + /// <returns>The locator cache timeout value (in seconds).</returns> + public int ice_getLocatorCacheTimeout() + { + return _reference.getLocatorCacheTimeout(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the locator cache timeout. + /// </summary> + /// <param name="newTimeout">The new locator cache timeout (in seconds).</param> + public ObjectPrx ice_locatorCacheTimeout(int newTimeout) + { + if(newTimeout < -1) + { + throw new System.ArgumentException("invalid value passed to ice_locatorCacheTimeout: " + newTimeout); + } + if(newTimeout == _reference.getLocatorCacheTimeout()) + { + return this; + } + else + { + return newInstance(_reference.changeLocatorCacheTimeout(newTimeout)); + } + } + + /// <summary> + /// Returns the invocation timeout of this proxy. + /// </summary> + /// <returns>The invocation timeout value (in seconds).</returns> + public int ice_getInvocationTimeout() + { + return _reference.getInvocationTimeout(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the invocation timeout. + /// </summary> + /// <param name="newTimeout">The new invocation timeout (in seconds).</param> + public ObjectPrx ice_invocationTimeout(int newTimeout) + { + if(newTimeout < 1 && newTimeout != -1 && newTimeout != -2) + { + throw new System.ArgumentException("invalid value passed to ice_invocationTimeout: " + newTimeout); + } + if(newTimeout == _reference.getInvocationTimeout()) + { + return this; + } + else + { + return newInstance(_reference.changeInvocationTimeout(newTimeout)); + } + } + + /// <summary> + /// Returns whether this proxy caches connections. + /// </summary> + /// <returns>True if this proxy caches connections; false, otherwise.</returns> + public bool ice_isConnectionCached() + { + return _reference.getCacheConnection(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for connection caching. + /// </summary> + /// <param name="newCache">True if the new proxy should cache connections; false, otherwise.</param> + /// <returns>The new proxy with the specified caching policy.</returns> + public ObjectPrx ice_connectionCached(bool newCache) + { + if(newCache == _reference.getCacheConnection()) + { + return this; + } + else + { + return newInstance(_reference.changeCacheConnection(newCache)); + } + } + + /// <summary> + /// Returns how this proxy selects endpoints (randomly or ordered). + /// </summary> + /// <returns>The endpoint selection policy.</returns> + public EndpointSelectionType ice_getEndpointSelection() + { + return _reference.getEndpointSelection(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the endpoint selection policy. + /// </summary> + /// <param name="newType">The new endpoint selection policy.</param> + /// <returns>The new proxy with the specified endpoint selection policy.</returns> + public ObjectPrx ice_endpointSelection(EndpointSelectionType newType) + { + if(newType == _reference.getEndpointSelection()) + { + return this; + } + else + { + return newInstance(_reference.changeEndpointSelection(newType)); + } + } + + /// <summary> + /// Returns whether this proxy communicates only via secure endpoints. + /// </summary> + /// <returns>True if this proxy communicates only vi secure endpoints; false, otherwise.</returns> + public bool ice_isSecure() + { + return _reference.getSecure(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for how it selects endpoints. + /// </summary> + /// <param name="b"> If b is true, only endpoints that use a secure transport are + /// used by the new proxy. If b is false, the returned proxy uses both secure and insecure + /// endpoints.</param> + /// <returns>The new proxy with the specified selection policy.</returns> + public ObjectPrx ice_secure(bool b) + { + if(b == _reference.getSecure()) + { + return this; + } + else + { + return newInstance(_reference.changeSecure(b)); + } + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the encoding used to marshal + /// parameters. + /// </summary> + /// <param name="e">The encoding version to use to marshal requests parameters.</param> + /// <returns>The new proxy with the specified encoding version.</returns> + public ObjectPrx ice_encodingVersion(Ice.EncodingVersion e) + { + if(e.Equals(_reference.getEncoding())) + { + return this; + } + else + { + return newInstance(_reference.changeEncoding(e)); + } + } + + /// <summary>Returns the encoding version used to marshal requests parameters.</summary> + /// <returns>The encoding version.</returns> + public Ice.EncodingVersion ice_getEncodingVersion() + { + return _reference.getEncoding(); + } + + /// <summary> + /// Returns whether this proxy prefers secure endpoints. + /// </summary> + /// <returns>True if the proxy always attempts to invoke via secure endpoints before it + /// attempts to use insecure endpoints; false, otherwise.</returns> + public bool ice_isPreferSecure() + { + return _reference.getPreferSecure(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for its endpoint selection policy. + /// </summary> + /// <param name="b">If b is true, the new proxy will use secure endpoints for invocations + /// and only use insecure endpoints if an invocation cannot be made via secure endpoints. If b is + /// false, the proxy prefers insecure endpoints to secure ones.</param> + /// <returns>The new proxy with the new endpoint selection policy.</returns> + public ObjectPrx ice_preferSecure(bool b) + { + if(b == _reference.getPreferSecure()) + { + return this; + } + else + { + return newInstance(_reference.changePreferSecure(b)); + } + } + + /// <summary> + /// Returns the router for this proxy. + /// </summary> + /// <returns>The router for the proxy. If no router is configured for the proxy, the return value + /// is null.</returns> + public Ice.RouterPrx ice_getRouter() + { + IceInternal.RouterInfo ri = _reference.getRouterInfo(); + return ri != null ? ri.getRouter() : null; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the router. + /// </summary> + /// <param name="router">The router for the new proxy.</param> + /// <returns>The new proxy with the specified router.</returns> + public ObjectPrx ice_router(RouterPrx router) + { + IceInternal.Reference @ref = _reference.changeRouter(router); + if(@ref.Equals(_reference)) + { + return this; + } + else + { + return newInstance(@ref); + } + } + + /// <summary> + /// Returns the locator for this proxy. + /// </summary> + /// <returns>The locator for this proxy. If no locator is configured, the return value is null.</returns> + public Ice.LocatorPrx ice_getLocator() + { + IceInternal.LocatorInfo li = _reference.getLocatorInfo(); + return li != null ? li.getLocator() : null; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for the locator. + /// </summary> + /// <param name="locator">The locator for the new proxy.</param> + /// <returns>The new proxy with the specified locator.</returns> + public ObjectPrx ice_locator(LocatorPrx locator) + { + IceInternal.Reference @ref = _reference.changeLocator(locator); + if(@ref.Equals(_reference)) + { + return this; + } + else + { + return newInstance(@ref); + } + } + + /// <summary> + /// Returns whether this proxy uses collocation optimization. + /// </summary> + /// <returns>True if the proxy uses collocation optimization; false, otherwise.</returns> + public bool ice_isCollocationOptimized() + { + return _reference.getCollocationOptimized(); + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for collocation optimization. + /// </summary> + /// <param name="b">True if the new proxy enables collocation optimization; false, otherwise.</param> + /// <returns>The new proxy the specified collocation optimization.</returns> + public ObjectPrx ice_collocationOptimized(bool b) + { + if(b == _reference.getCollocationOptimized()) + { + return this; + } + else + { + return newInstance(_reference.changeCollocationOptimized(b)); + } + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses twoway invocations. + /// </summary> + /// <returns>A new proxy that uses twoway invocations.</returns> + public ObjectPrx ice_twoway() + { + if(_reference.getMode() == IceInternal.Reference.Mode.ModeTwoway) + { + return this; + } + else + { + return newInstance(_reference.changeMode(IceInternal.Reference.Mode.ModeTwoway)); + } + } + + /// <summary> + /// Returns whether this proxy uses twoway invocations. + /// </summary> + /// <returns>True if this proxy uses twoway invocations; false, otherwise.</returns> + public bool ice_isTwoway() + { + return _reference.getMode() == IceInternal.Reference.Mode.ModeTwoway; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses oneway invocations. + /// </summary> + /// <returns>A new proxy that uses oneway invocations.</returns> + public ObjectPrx ice_oneway() + { + if(_reference.getMode() == IceInternal.Reference.Mode.ModeOneway) + { + return this; + } + else + { + return newInstance(_reference.changeMode(IceInternal.Reference.Mode.ModeOneway)); + } + } + + /// <summary> + /// Returns whether this proxy uses oneway invocations. + /// </summary> + /// <returns>True if this proxy uses oneway invocations; false, otherwise.</returns> + public bool ice_isOneway() + { + return _reference.getMode() == IceInternal.Reference.Mode.ModeOneway; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses batch oneway invocations. + /// </summary> + /// <returns>A new proxy that uses batch oneway invocations.</returns> + public ObjectPrx ice_batchOneway() + { + if(_reference.getMode() == IceInternal.Reference.Mode.ModeBatchOneway) + { + return this; + } + else + { + return newInstance(_reference.changeMode(IceInternal.Reference.Mode.ModeBatchOneway)); + } + } + + /// <summary> + /// Returns whether this proxy uses batch oneway invocations. + /// </summary> + /// <returns>True if this proxy uses batch oneway invocations; false, otherwise.</returns> + public bool ice_isBatchOneway() + { + return _reference.getMode() == IceInternal.Reference.Mode.ModeBatchOneway; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses datagram invocations. + /// </summary> + /// <returns>A new proxy that uses datagram invocations.</returns> + public ObjectPrx ice_datagram() + { + if(_reference.getMode() == IceInternal.Reference.Mode.ModeDatagram) + { + return this; + } + else + { + return newInstance(_reference.changeMode(IceInternal.Reference.Mode.ModeDatagram)); + } + } + + /// <summary> + /// Returns whether this proxy uses datagram invocations. + /// </summary> + /// <returns>True if this proxy uses datagram invocations; false, otherwise.</returns> + public bool ice_isDatagram() + { + return _reference.getMode() == IceInternal.Reference.Mode.ModeDatagram; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, but uses batch datagram invocations. + /// </summary> + /// <returns>A new proxy that uses batch datagram invocations.</returns> + public ObjectPrx ice_batchDatagram() + { + if(_reference.getMode() == IceInternal.Reference.Mode.ModeBatchDatagram) + { + return this; + } + else + { + return newInstance(_reference.changeMode(IceInternal.Reference.Mode.ModeBatchDatagram)); + } + } + + /// <summary> + /// Returns whether this proxy uses batch datagram invocations. + /// </summary> + /// <returns>True if this proxy uses batch datagram invocations; false, otherwise.</returns> + public bool ice_isBatchDatagram() + { + return _reference.getMode() == IceInternal.Reference.Mode.ModeBatchDatagram; + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for compression. + /// </summary> + /// <param name="co">True enables compression for the new proxy; false disables compression.</param> + /// <returns>A new proxy with the specified compression setting.</returns> + public ObjectPrx ice_compress(bool co) + { + IceInternal.Reference @ref = _reference.changeCompress(co); + if(@ref.Equals(_reference)) + { + return this; + } + else + { + return newInstance(@ref); + } + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for its timeout setting. + /// </summary> + /// <param name="t">The timeout for the new proxy in milliseconds.</param> + /// <returns>A new proxy with the specified timeout.</returns> + public ObjectPrx ice_timeout(int t) + { + if(t < 1 && t != -1) + { + throw new System.ArgumentException("invalid value passed to ice_timeout: " + t); + } + IceInternal.Reference @ref = _reference.changeTimeout(t); + if(@ref.Equals(_reference)) + { + return this; + } + else + { + return newInstance(@ref); + } + } + + /// <summary> + /// Creates a new proxy that is identical to this proxy, except for its connection ID. + /// </summary> + /// <param name="connectionId">The connection ID for the new proxy. An empty string removes the + /// connection ID.</param> + /// <returns>A new proxy with the specified connection ID.</returns> + public ObjectPrx ice_connectionId(string connectionId) + { + IceInternal.Reference @ref = _reference.changeConnectionId(connectionId); + if(@ref.Equals(_reference)) + { + return this; + } + else + { + return newInstance(@ref); + } + } + + /// <summary> + /// Returns the connection id of this proxy. + /// </summary> + /// <returns>The connection id.</returns> + public string ice_getConnectionId() + { + return _reference.getConnectionId(); + } + + /// <summary> + /// Returns the Connection for this proxy. If the proxy does not yet have an established connection, + /// it first attempts to create a connection. + /// </summary> + /// <returns>The Connection for this proxy.</returns> + /// <exception name="CollocationOptimizationException">If the proxy uses collocation optimization and denotes a + /// collocated object.</exception> + public Connection ice_getConnection() + { + return end_ice_getConnection(begin_ice_getConnection()); + } + + public AsyncResult<Callback_Object_ice_getConnection> begin_ice_getConnection() + { + return begin_ice_getConnectionInternal(null, null); + } + + internal const string __ice_getConnection_name = "ice_getConnection"; + + public AsyncResult begin_ice_getConnection(Ice.AsyncCallback cb, object cookie) + { + return begin_ice_getConnectionInternal(cb, cookie); + } + + public Connection end_ice_getConnection(Ice.AsyncResult r) + { + IceInternal.ProxyGetConnection outAsync = + IceInternal.ProxyGetConnection.check(r, this, __ice_getConnection_name); + outAsync.wait(); + return ice_getCachedConnection(); + } + + private AsyncResult<Callback_Object_ice_getConnection> begin_ice_getConnectionInternal(Ice.AsyncCallback cb, + object cookie) + { + IceInternal.ProxyGetConnection result = new IceInternal.ProxyGetConnection(this, + __ice_getConnection_name, + ice_getConnection_completed__, + cookie); + if(cb != null) + { + result.whenCompletedWithAsyncCallback(cb); + } + try + { + result.invoke(); + } + catch(Ice.Exception ex) + { + result.abort(ex); + } + return result; + } + + + + private void ice_getConnection_completed__(AsyncResult r, + Callback_Object_ice_getConnection cb, + Ice.ExceptionCallback excb) + { + Connection ret; + try + { + ret = end_ice_getConnection(r); + } + catch(Ice.Exception ex) + { + if(excb != null) + { + excb(ex); + } + return; + } + if(cb != null) + { + cb(ret); + } + } + + /// <summary> + /// Returns the cached Connection for this proxy. If the proxy does not yet have an established + /// connection, it does not attempt to create a connection. + /// </summary> + /// <returns>The cached Connection for this proxy (null if the proxy does not have + /// an established connection).</returns> + /// <exception name="CollocationOptimizationException">If the proxy uses collocation optimization and denotes a + /// collocated object.</exception> + public Connection ice_getCachedConnection() + { + IceInternal.RequestHandler handler; + lock(this) + { + handler = _requestHandler; + } + + if(handler != null) + { + try + { + return handler.getConnection(); + } + catch(LocalException) + { + } + } + return null; + } + + /// <summary> + /// Flushes any pending batched requests for this communicator. The call blocks until the flush is complete. + /// </summary> + public void ice_flushBatchRequests() + { + end_ice_flushBatchRequests(begin_ice_flushBatchRequests()); + } + + internal const string __ice_flushBatchRequests_name = "ice_flushBatchRequests"; + + public AsyncResult begin_ice_flushBatchRequests() + { + return begin_ice_flushBatchRequests(null, null); + } + + public AsyncResult begin_ice_flushBatchRequests(Ice.AsyncCallback cb, object cookie) + { + IceInternal.ProxyFlushBatch result = new IceInternal.ProxyFlushBatch(this, + __ice_flushBatchRequests_name, + cookie); + if(cb != null) + { + result.whenCompletedWithAsyncCallback(cb); + } + try + { + result.invoke(); + } + catch(Ice.Exception ex) + { + result.abort(ex); + } + return result; + } + + public void end_ice_flushBatchRequests(Ice.AsyncResult r) + { + IceInternal.ProxyFlushBatch outAsync = + IceInternal.ProxyFlushBatch.check(r, this, __ice_flushBatchRequests_name); + outAsync.wait(); + } + + /// <summary> + /// Returns whether this proxy equals the passed object. Two proxies are equal if they are equal in all + /// respects, that is, if their object identity, endpoints timeout settings, and so on are all equal. + /// </summary> + /// <param name="r">The object to compare this proxy with.</param> + /// <returns>True if this proxy is equal to r; false, otherwise.</returns> + public override bool Equals(object r) + { + ObjectPrxHelperBase rhs = r as ObjectPrxHelperBase; + return object.ReferenceEquals(rhs, null) ? false : _reference.Equals(rhs._reference); + } + + /// <summary> + /// Returns whether two proxies are equal. Two proxies are equal if they are equal in all + /// respects, that is, if their object identity, endpoints timeout settings, and so on are all equal. + /// </summary> + /// <param name="lhs">A proxy to compare with the proxy rhs.</param> + /// <param name="rhs">A proxy to compare with the proxy lhs.</param> + /// <returns>True if the proxies are equal; false, otherwise.</returns> + public static bool Equals(ObjectPrxHelperBase lhs, ObjectPrxHelperBase rhs) + { + return object.ReferenceEquals(lhs, null) ? object.ReferenceEquals(rhs, null) : lhs.Equals(rhs); + } + + /// <summary> + /// Returns whether two proxies are equal. Two proxies are equal if they are equal in all + /// respects, that is, if their object identity, endpoints timeout settings, and so on are all equal. + /// </summary> + /// <param name="lhs">A proxy to compare with the proxy rhs.</param> + /// <param name="rhs">A proxy to compare with the proxy lhs.</param> + /// <returns>True if the proxies are equal; false, otherwise.</returns> + public static bool operator==(ObjectPrxHelperBase lhs, ObjectPrxHelperBase rhs) + { + return Equals(lhs, rhs); + } + + /// <summary> + /// Returns whether two proxies are not equal. Two proxies are equal if they are equal in all + /// respects, that is, if their object identity, endpoints timeout settings, and so on are all equal. + /// </summary> + /// <param name="lhs">A proxy to compare with the proxy rhs.</param> + /// <param name="rhs">A proxy to compare with the proxy lhs.</param> + /// <returns>True if the proxies are not equal; false, otherwise.</returns> + public static bool operator!=(ObjectPrxHelperBase lhs, ObjectPrxHelperBase rhs) + { + return !Equals(lhs, rhs); + } + + public IceInternal.Reference reference__() + { + return _reference; + } + + public void copyFrom__(ObjectPrx from) + { + lock(from) + { + ObjectPrxHelperBase h = (ObjectPrxHelperBase)from; + _reference = h._reference; + _requestHandler = h._requestHandler; + } + } + + public int handleException__(Exception ex, IceInternal.RequestHandler handler, OperationMode mode, bool sent, + ref int cnt) + { + updateRequestHandler__(handler, null); // Clear the request handler + + // + // We only retry local exception, system exceptions aren't retried. + // + // A CloseConnectionException indicates graceful server shutdown, and is therefore + // always repeatable without violating "at-most-once". That's because by sending a + // close connection message, the server guarantees that all outstanding requests + // can safely be repeated. + // + // An ObjectNotExistException can always be retried as well without violating + // "at-most-once" (see the implementation of the checkRetryAfterException method + // of the ProxyFactory class for the reasons why it can be useful). + // + // If the request didn't get sent or if it's non-mutating or idempotent it can + // also always be retried if the retry count isn't reached. + // + if(ex is LocalException && (!sent || + mode == OperationMode.Nonmutating || mode == OperationMode.Idempotent || + ex is CloseConnectionException || + ex is ObjectNotExistException)) + { + try + { + return _reference.getInstance().proxyFactory().checkRetryAfterException((LocalException)ex, + _reference, + ref cnt); + } + catch(CommunicatorDestroyedException) + { + // + // The communicator is already destroyed, so we cannot retry. + // + throw ex; + } + } + else + { + throw ex; // Retry could break at-most-once semantics, don't retry. + } + } + + public void checkTwowayOnly__(string name) + { + // + // No mutex lock necessary, there is nothing mutable in this + // operation. + // + + if(!ice_isTwoway()) + { + TwowayOnlyException ex = new TwowayOnlyException(); + ex.operation = name; + throw ex; + } + } + + public void checkAsyncTwowayOnly__(string name) + { + // + // No mutex lock necessary, there is nothing mutable in this + // operation. + // + + if(!ice_isTwoway()) + { + throw new System.ArgumentException("`" + name + "' can only be called with a twoway proxy"); + } + } + + public void end__(AsyncResult r, string operation) + { + IceInternal.ProxyOutgoingAsyncBase result = IceInternal.ProxyOutgoingAsyncBase.check(r, this, operation); + try + { + bool ok = result.wait(); + if(_reference.getMode() == IceInternal.Reference.Mode.ModeTwoway) + { + IceInternal.OutgoingAsync outAsync = (IceInternal.OutgoingAsync)result; + if(!ok) + { + try + { + outAsync.throwUserException(); + } + catch(Ice.UserException ex) + { + throw new Ice.UnknownUserException(ex.ice_name(), ex); + } + } + outAsync.readEmptyParams(); + } + } + finally + { + if(result != null) + { + result.cacheMessageBuffers(); + } + } + } + + public IceInternal.RequestHandler getRequestHandler__() + { + if(_reference.getCacheConnection()) + { + lock(this) + { + if(_requestHandler != null) + { + return _requestHandler; + } + } + } + return _reference.getRequestHandler(this); + } + + public IceInternal.BatchRequestQueue + getBatchRequestQueue__() + { + lock(this) + { + if(_batchRequestQueue == null) + { + _batchRequestQueue = _reference.getBatchRequestQueue(); + } + return _batchRequestQueue; + } + } + + public IceInternal.RequestHandler + setRequestHandler__(IceInternal.RequestHandler handler) + { + if(_reference.getCacheConnection()) + { + lock(this) + { + if(_requestHandler == null) + { + _requestHandler = handler; + } + return _requestHandler; + } + } + return handler; + } + + public void updateRequestHandler__(IceInternal.RequestHandler previous, IceInternal.RequestHandler handler) + { + if(_reference.getCacheConnection() && previous != null) + { + lock(this) + { + if(_requestHandler != null && _requestHandler != handler) + { + // + // Update the request handler only if "previous" is the same + // as the current request handler. This is called after + // connection binding by the connect request handler. We only + // replace the request handler if the current handler is the + // connect request handler. + // + _requestHandler = _requestHandler.update(previous, handler); + } + } + } + } + + // + // Only for use by IceInternal.ProxyFactory + // + public void setup(IceInternal.Reference @ref) + { + // + // No need to synchronize, as this operation is only called + // upon initial initialization. + // + + Debug.Assert(_reference == null); + Debug.Assert(_requestHandler == null); + + _reference = @ref; + } + + private ObjectPrxHelperBase newInstance(IceInternal.Reference @ref) + { + ObjectPrxHelperBase proxy = (ObjectPrxHelperBase)Activator.CreateInstance(GetType()); + proxy.setup(@ref); + return proxy; + } + + private IceInternal.Reference _reference; + private IceInternal.RequestHandler _requestHandler; + private IceInternal.BatchRequestQueue _batchRequestQueue; + private struct StreamCacheEntry + { + public IceInternal.BasicStream iss; + public IceInternal.BasicStream os; + } + + private LinkedList<StreamCacheEntry> _streamCache; + } + + /// <summary> + /// Base class for all proxy helpers. + /// </summary> + public class ObjectPrxHelper : ObjectPrxHelperBase + { + /// <summary> + /// Casts a proxy to {@link ObjectPrx}. This call contacts + /// the server and will throw an Ice run-time exception if the target + /// object does not exist or the server cannot be reached. + /// </summary> + /// <param name="b">The proxy to cast to ObjectPrx.</param> + /// <returns>b.</returns> + public static ObjectPrx checkedCast(ObjectPrx b) + { + return b; + } + + /// <summary> + /// Casts a proxy to {@link ObjectPrx}. This call contacts + /// the server and throws an Ice run-time exception if the target + /// object does not exist or the server cannot be reached. + /// </summary> + /// <param name="b">The proxy to cast to ObjectPrx.</param> + /// <param name="ctx">The Context map for the invocation.</param> + /// <returns>b.</returns> + public static ObjectPrx checkedCast(ObjectPrx b, Dictionary<string, string> ctx) + { + return b; + } + + /// <summary> + /// Creates a new proxy that is identical to the passed proxy, except + /// for its facet. This call contacts + /// the server and throws an Ice run-time exception if the target + /// object does not exist, the specified facet does not exist, or the server cannot be reached. + /// </summary> + /// <param name="b">The proxy to cast to ObjectPrx.</param> + /// <param name="f">The facet for the new proxy.</param> + /// <returns>The new proxy with the specified facet.</returns> + public static ObjectPrx checkedCast(ObjectPrx b, string f) + { + ObjectPrx d = null; + if(b != null) + { + try + { + ObjectPrx bb = b.ice_facet(f); + bool ok = bb.ice_isA("::Ice::Object"); + Debug.Assert(ok); + ObjectPrxHelper h = new ObjectPrxHelper(); + h.copyFrom__(bb); + d = h; + } + catch(Ice.FacetNotExistException) + { + } + } + return d; + } + + /// <summary> + /// Creates a new proxy that is identical to the passed proxy, except + /// for its facet. This call contacts + /// the server and throws an Ice run-time exception if the target + /// object does not exist, the specified facet does not exist, or the server cannot be reached. + /// </summary> + /// <param name="b">The proxy to cast to ObjectPrx.</param> + /// <param name="f">The facet for the new proxy.</param> + /// <param name="ctx">The Context map for the invocation.</param> + /// <returns>The new proxy with the specified facet.</returns> + public static ObjectPrx checkedCast(ObjectPrx b, string f, Dictionary<string, string> ctx) + { + ObjectPrx d = null; + if(b != null) + { + try + { + ObjectPrx bb = b.ice_facet(f); + bool ok = bb.ice_isA("::Ice::Object", ctx); + Debug.Assert(ok); + ObjectPrxHelper h = new ObjectPrxHelper(); + h.copyFrom__(bb); + d = h; + } + catch(Ice.FacetNotExistException) + { + } + } + return d; + } + + /// <summary> + /// Casts a proxy to {@link ObjectPrx}. This call does + /// not contact the server and always succeeds. + /// </summary> + /// <param name="b">The proxy to cast to ObjectPrx.</param> + /// <returns>b.</returns> + public static ObjectPrx uncheckedCast(ObjectPrx b) + { + return b; + } + + /// <summary> + /// Creates a new proxy that is identical to the passed proxy, except + /// for its facet. This call does not contact the server and always succeeds. + /// </summary> + /// <param name="b">The proxy to cast to ObjectPrx.</param> + /// <param name="f">The facet for the new proxy.</param> + /// <returns>The new proxy with the specified facet.</returns> + public static ObjectPrx uncheckedCast(ObjectPrx b, string f) + { + ObjectPrx d = null; + if(b != null) + { + ObjectPrx bb = b.ice_facet(f); + ObjectPrxHelper h = new ObjectPrxHelper(); + h.copyFrom__(bb); + d = h; + } + return d; + } + + + /// <summary> + /// Returns the Slice type id of the interface or class associated + /// with this proxy class. + /// </summary> + /// <returns>The type id, "::Ice::Object".</returns> + public static string ice_staticId() + { + return Ice.ObjectImpl.ice_staticId(); + } + } +} diff --git a/csharp/src/Ice/ProxyFactory.cs b/csharp/src/Ice/ProxyFactory.cs new file mode 100644 index 00000000000..b9d964ad03f --- /dev/null +++ b/csharp/src/Ice/ProxyFactory.cs @@ -0,0 +1,299 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Diagnostics; +using System.Collections.Generic; +using System.Globalization; + +namespace IceInternal +{ + public sealed class ProxyFactory + { + public Ice.ObjectPrx stringToProxy(string str) + { + Reference r = instance_.referenceFactory().create(str, null); + return referenceToProxy(r); + } + + public string proxyToString(Ice.ObjectPrx proxy) + { + if(proxy != null) + { + Ice.ObjectPrxHelperBase h = (Ice.ObjectPrxHelperBase) proxy; + return h.reference__().ToString(); + } + else + { + return ""; + } + } + + public Ice.ObjectPrx propertyToProxy(string prefix) + { + string proxy = instance_.initializationData().properties.getProperty(prefix); + Reference r = instance_.referenceFactory().create(proxy, prefix); + return referenceToProxy(r); + } + + public Dictionary<string, string> + proxyToProperty(Ice.ObjectPrx proxy, string prefix) + { + if(proxy != null) + { + Ice.ObjectPrxHelperBase h = (Ice.ObjectPrxHelperBase) proxy; + return h.reference__().toProperty(prefix); + } + else + { + return new Dictionary<string, string>(); + } + } + + public Ice.ObjectPrx streamToProxy(BasicStream s) + { + Ice.Identity ident = new Ice.Identity(); + ident.read__(s); + + Reference r = instance_.referenceFactory().create(ident, s); + return referenceToProxy(r); + } + + public Ice.ObjectPrx referenceToProxy(Reference r) + { + if(r != null) + { + Ice.ObjectPrxHelperBase proxy = new Ice.ObjectPrxHelperBase(); + proxy.setup(r); + return proxy; + } + else + { + return null; + } + } + + public void proxyToStream(Ice.ObjectPrx proxy, BasicStream s) + { + if(proxy != null) + { + Ice.ObjectPrxHelperBase h = (Ice.ObjectPrxHelperBase)proxy; + Reference r = h.reference__(); + r.getIdentity().write__(s); + r.streamWrite(s); + } + else + { + Ice.Identity ident = new Ice.Identity(); + ident.name = ""; + ident.category = ""; + ident.write__(s); + } + } + + public int checkRetryAfterException(Ice.LocalException ex, Reference @ref, ref int cnt) + { + TraceLevels traceLevels = instance_.traceLevels(); + Ice.Logger logger = instance_.initializationData().logger; + + // + // We don't retry batch requests because the exception might have caused + // the all the requests batched with the connection to be aborted and we + // want the application to be notified. + // + if(@ref.getMode() == Reference.Mode.ModeBatchOneway || @ref.getMode() == Reference.Mode.ModeBatchDatagram) + { + throw ex; + } + + Ice.ObjectNotExistException one = ex as Ice.ObjectNotExistException; + if(one != null) + { + if(@ref.getRouterInfo() != null && one.operation.Equals("ice_add_proxy")) + { + // + // If we have a router, an ObjectNotExistException with an + // operation name "ice_add_proxy" indicates to the client + // that the router isn't aware of the proxy (for example, + // because it was evicted by the router). In this case, we + // must *always* retry, so that the missing proxy is added + // to the router. + // + + @ref.getRouterInfo().clearCache(@ref); + + if(traceLevels.retry >= 1) + { + string s = "retrying operation call to add proxy to router\n" + ex; + logger.trace(traceLevels.retryCat, s); + } + return 0; // We must always retry, so we don't look at the retry count. + } + else if(@ref.isIndirect()) + { + // + // We retry ObjectNotExistException if the reference is + // indirect. + // + + if(@ref.isWellKnown()) + { + LocatorInfo li = @ref.getLocatorInfo(); + if(li != null) + { + li.clearCache(@ref); + } + } + } + else + { + // + // For all other cases, we don't retry ObjectNotExistException. + // + throw ex; + } + } + else if(ex is Ice.RequestFailedException) + { + throw ex; + } + + // + // There is no point in retrying an operation that resulted in a + // MarshalException. This must have been raised locally (because if + // it happened in a server it would result in an UnknownLocalException + // instead), which means there was a problem in this process that will + // not change if we try again. + // + // The most likely cause for a MarshalException is exceeding the + // maximum message size, which is represented by the subclass + // MemoryLimitException. For example, a client can attempt to send a + // message that exceeds the maximum memory size, or accumulate enough + // batch requests without flushing that the maximum size is reached. + // + // This latter case is especially problematic, because if we were to + // retry a batch request after a MarshalException, we would in fact + // silently discard the accumulated requests and allow new batch + // requests to accumulate. If the subsequent batched requests do not + // exceed the maximum message size, it appears to the client that all + // of the batched requests were accepted, when in reality only the + // last few are actually sent. + // + if(ex is Ice.MarshalException) + { + throw ex; + } + + + // + // Don't retry if the communicator is destroyed or object adapter + // deactivated. + // + if(ex is Ice.CommunicatorDestroyedException || ex is Ice.ObjectAdapterDeactivatedException) + { + throw ex; + } + + // + // Don't retry invocation timeouts. + // + if(ex is Ice.InvocationTimeoutException || ex is Ice.InvocationCanceledException) + { + throw ex; + } + + ++cnt; + Debug.Assert(cnt > 0); + + int interval; + if(cnt == (_retryIntervals.Length + 1) && ex is Ice.CloseConnectionException) + { + // + // A close connection exception is always retried at least once, even if the retry + // limit is reached. + // + interval = 0; + } + else if(cnt > _retryIntervals.Length) + { + if(traceLevels.retry >= 1) + { + string s = "cannot retry operation call because retry limit has been exceeded\n" + ex; + logger.trace(traceLevels.retryCat, s); + } + throw ex; + } + else + { + interval = _retryIntervals[cnt - 1]; + } + + if(traceLevels.retry >= 1) + { + string s = "retrying operation call"; + if(interval > 0) + { + s += " in " + interval + "ms"; + } + s += " because of exception\n" + ex; + logger.trace(traceLevels.retryCat, s); + } + + return interval; + } + + // + // Only for use by Instance + // + internal ProxyFactory(Instance instance) + { + instance_ = instance; + + string[] arr = instance_.initializationData().properties.getPropertyAsList("Ice.RetryIntervals"); + + if(arr.Length > 0) + { + _retryIntervals = new int[arr.Length]; + + for (int i = 0; i < arr.Length; i++) + { + int v; + + try + { + v = System.Int32.Parse(arr[i], CultureInfo.InvariantCulture); + } + catch(System.FormatException) + { + v = 0; + } + + // + // If -1 is the first value, no retry and wait intervals. + // + if(i == 0 && v == -1) + { + _retryIntervals = new int[0]; + break; + } + + _retryIntervals[i] = v > 0?v:0; + } + } + else + { + _retryIntervals = new int[1]; + _retryIntervals[0] = 0; + } + } + + private Instance instance_; + private int[] _retryIntervals; + } + +} diff --git a/csharp/src/Ice/ProxyIdentityKey.cs b/csharp/src/Ice/ProxyIdentityKey.cs new file mode 100644 index 00000000000..3fbf481b439 --- /dev/null +++ b/csharp/src/Ice/ProxyIdentityKey.cs @@ -0,0 +1,135 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Globalization; + +namespace Ice +{ + /// <summary> + /// This class allows a proxy to be used as the key for a hashed collection. + /// The GetHashCode, Equals, and Compare methods are based on the object identity + /// of the proxy. + /// </summary> + public class ProxyIdentityKey : System.Collections.IEqualityComparer, System.Collections.IComparer + { + /// <summary> + /// Computes a hash value based on the object identity of the proxy. + /// </summary> + /// <param name="obj">The proxy whose hash value to compute.</param> + /// <returns>The hash value for the proxy based on the identity.</returns> + public int GetHashCode(object obj) + { + int h = 5381; + IceInternal.HashUtil.hashAdd(ref h, ((Ice.ObjectPrx)obj).ice_getIdentity()); + return h; + } + + /// Compares two proxies for equality. + /// <param name="obj1">A proxy to compare.</param> + /// <param name="obj2">A proxy to compare.</param> + /// <returns>True if the passed proxies have the same object + /// identity; false, otherwise.</returns> + public new bool Equals(object obj1, object obj2) + { + try + { + return Compare(obj1, obj2) == 0; + } + catch(System.Exception) + { + return false; + } + } + + /// Compares two proxies using the object identity for comparison. + /// <param name="obj1">A proxy to compare.</param> + /// <param name="obj2">A proxy to compare.</param> + /// <returns>< 0 if obj1 is less than obj2; > 0 if obj1 is greater than obj2; + /// 0, otherwise.</returns> + public int Compare(object obj1, object obj2) + { + Ice.ObjectPrx proxy1 = obj1 as Ice.ObjectPrx; + if(obj1 != null && proxy1 == null) + { + throw new System.ArgumentException("Argument must be derived from Ice.ObjectPrx", "obj1"); + } + + Ice.ObjectPrx proxy2 = obj2 as Ice.ObjectPrx; + if(obj2 != null && proxy2 == null) + { + throw new System.ArgumentException("Argument must be derived from Ice.ObjectPrx", "obj2"); + } + return Ice.Util.proxyIdentityCompare(proxy1, proxy2); + } + } + + /// <summary> + /// This class allows a proxy to be used as the key for a hashed collection. + /// The GetHashCode, Equals, and Compare methods are based on the object identity and + /// the facet of the proxy. + /// </summary> + public class ProxyIdentityFacetKey : System.Collections.IEqualityComparer, System.Collections.IComparer + { + /// <summary> + /// Computes a hash value based on the object identity and facet of the proxy. + /// </summary> + /// <param name="obj">The proxy whose hash value to compute.</param> + /// <returns>The hash value for the proxy based on the identity and facet.</returns> + public int GetHashCode(object obj) + { + Ice.ObjectPrx o = (Ice.ObjectPrx)obj; + Ice.Identity identity = o.ice_getIdentity(); + string facet = o.ice_getFacet(); + int h = 5381; + IceInternal.HashUtil.hashAdd(ref h, identity); + IceInternal.HashUtil.hashAdd(ref h, facet); + return h; + } + + /// Compares two proxies for equality. + /// <param name="obj1">A proxy to compare.</param> + /// <param name="obj2">A proxy to compare.</param> + /// <returns>True if the passed proxies have the same object + /// identity and facet; false, otherwise.</returns> + public new bool Equals(object obj1, object obj2) + { + try + { + return Compare(obj1, obj2) == 0; + } + catch(System.Exception) + { + return false; + } + } + + /// Compares two proxies using the object identity and facet for comparison. + /// <param name="obj1">A proxy to compare.</param> + /// <param name="obj2">A proxy to compare.</param> + /// <returns>< 0 if obj1 is less than obj2; > 0 if obj1 is greater than obj2; + /// 0, otherwise.</returns> + public int Compare(object obj1, object obj2) + { + Ice.ObjectPrx proxy1 = obj1 as Ice.ObjectPrx; + if(obj1 != null && proxy1 == null) + { + throw new System.ArgumentException("Argument must be derived from Ice.ObjectPrx", "obj1"); + } + + Ice.ObjectPrx proxy2 = obj2 as Ice.ObjectPrx; + if(obj2 != null && proxy2 == null) + { + throw new System.ArgumentException("Argument must be derived from Ice.ObjectPrx", "obj2"); + } + return Ice.Util.proxyIdentityAndFacetCompare(proxy1, proxy2); + } + } + +} diff --git a/csharp/src/Ice/Reference.cs b/csharp/src/Ice/Reference.cs new file mode 100644 index 00000000000..80e48608a5d --- /dev/null +++ b/csharp/src/Ice/Reference.cs @@ -0,0 +1,1685 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Globalization; + +namespace IceInternal +{ + public abstract class Reference : ICloneable + { + public enum Mode { + ModeTwoway, + ModeOneway, + ModeBatchOneway, + ModeDatagram, + ModeBatchDatagram, + ModeLast=ModeBatchDatagram + }; + + public interface GetConnectionCallback + { + void setConnection(Ice.ConnectionI connection, bool compress); + void setException(Ice.LocalException ex); + } + + public Mode getMode() + { + return mode_; + } + + public bool getSecure() + { + return secure_; + } + + public Ice.ProtocolVersion getProtocol() + { + return protocol_; + } + + public Ice.EncodingVersion getEncoding() + { + return encoding_; + } + + public Ice.Identity getIdentity() + { + return identity_; + } + + public string getFacet() + { + return facet_; + } + + public Instance getInstance() + { + return instance_; + } + + public Dictionary<string, string> getContext() + { + return context_; + } + + public int + getInvocationTimeout() + { + return invocationTimeout_; + } + + public Ice.Communicator getCommunicator() + { + return communicator_; + } + + public abstract EndpointI[] getEndpoints(); + public abstract string getAdapterId(); + public abstract LocatorInfo getLocatorInfo(); + public abstract RouterInfo getRouterInfo(); + public abstract bool getCollocationOptimized(); + public abstract bool getCacheConnection(); + public abstract bool getPreferSecure(); + public abstract Ice.EndpointSelectionType getEndpointSelection(); + public abstract int getLocatorCacheTimeout(); + public abstract String getConnectionId(); + + // + // The change* methods (here and in derived classes) create + // a new reference based on the existing one, with the + // corresponding value changed. + // + public Reference changeContext(Dictionary<string, string> newContext) + { + if(newContext == null) + { + newContext = _emptyContext; + } + Reference r = instance_.referenceFactory().copy(this); + if(newContext.Count == 0) + { + r.context_ = _emptyContext; + } + else + { + r.context_ = new Dictionary<string, string>(newContext); + } + return r; + } + + public Reference changeMode(Mode newMode) + { + if(newMode == mode_) + { + return this; + } + Reference r = instance_.referenceFactory().copy(this); + r.mode_ = newMode; + return r; + } + + public Reference changeSecure(bool newSecure) + { + if(newSecure == secure_) + { + return this; + } + Reference r = instance_.referenceFactory().copy(this); + r.secure_ = newSecure; + return r; + } + + public Reference changeIdentity(Ice.Identity newIdentity) + { + if(newIdentity.Equals(identity_)) + { + return this; + } + Reference r = instance_.referenceFactory().copy(this); + r.identity_ = newIdentity; // Identity is a value type, therefore a copy of newIdentity is made. + return r; + } + + public Reference changeFacet(string newFacet) + { + if(newFacet.Equals(facet_)) + { + return this; + } + Reference r = instance_.referenceFactory().copy(this); + r.facet_ = newFacet; + return r; + } + + public Reference changeInvocationTimeout(int newTimeout) + { + if(newTimeout == invocationTimeout_) + { + return this; + } + Reference r = instance_.referenceFactory().copy(this); + r.invocationTimeout_ = newTimeout; + return r; + } + + public virtual Reference changeEncoding(Ice.EncodingVersion newEncoding) + { + if(newEncoding.Equals(encoding_)) + { + return this; + } + Reference r = instance_.referenceFactory().copy(this); + r.encoding_ = newEncoding; + return r; + } + + public virtual Reference changeCompress(bool newCompress) + { + if(overrideCompress_ && compress_ == newCompress) + { + return this; + } + + Reference r = instance_.referenceFactory().copy(this); + r.compress_ = newCompress; + r.overrideCompress_ = true; + return r; + } + + public abstract Reference changeEndpoints(EndpointI[] newEndpoints); + public abstract Reference changeAdapterId(string newAdapterId); + public abstract Reference changeLocator(Ice.LocatorPrx newLocator); + public abstract Reference changeRouter(Ice.RouterPrx newRouter); + public abstract Reference changeCollocationOptimized(bool newCollocationOptimized); + public abstract Reference changeCacheConnection(bool newCache); + public abstract Reference changePreferSecure(bool newPreferSecure); + public abstract Reference changeEndpointSelection(Ice.EndpointSelectionType newType); + public abstract Reference changeLocatorCacheTimeout(int newTimeout); + + public abstract Reference changeTimeout(int newTimeout); + public abstract Reference changeConnectionId(string connectionId); + + public override int GetHashCode() + { + lock(this) + { + if(hashInitialized_) + { + return hashValue_; + } + int h = 5381; + IceInternal.HashUtil.hashAdd(ref h, mode_); + IceInternal.HashUtil.hashAdd(ref h, secure_); + IceInternal.HashUtil.hashAdd(ref h, identity_); + IceInternal.HashUtil.hashAdd(ref h, context_); + IceInternal.HashUtil.hashAdd(ref h, facet_); + IceInternal.HashUtil.hashAdd(ref h, overrideCompress_); + if(overrideCompress_) + { + IceInternal.HashUtil.hashAdd(ref h, compress_); + } + IceInternal.HashUtil.hashAdd(ref h, protocol_); + IceInternal.HashUtil.hashAdd(ref h, encoding_); + IceInternal.HashUtil.hashAdd(ref h, invocationTimeout_); + hashValue_ = h; + hashInitialized_ = true; + return hashValue_; + } + } + + public abstract bool isIndirect(); + public abstract bool isWellKnown(); + + // + // Marshal the reference. + // + public virtual void streamWrite(BasicStream s) + { + // + // Don't write the identity here. Operations calling streamWrite + // write the identity. + // + + // + // For compatibility with the old FacetPath. + // + if(facet_.Length == 0) + { + s.writeStringSeq(null); + } + else + { + string[] facetPath = { facet_ }; + s.writeStringSeq(facetPath); + } + + s.writeByte((byte)mode_); + + s.writeBool(secure_); + + if(!s.getWriteEncoding().Equals(Ice.Util.Encoding_1_0)) + { + protocol_.write__(s); + encoding_.write__(s); + } + + // Derived class writes the remainder of the reference. + } + + // + // Convert the reference to its string form. + // + public override string ToString() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + StringBuilder s = new StringBuilder(); + + // + // If the encoded identity string contains characters which + // the reference parser uses as separators, then we enclose + // the identity string in quotes. + // + string id = instance_.identityToString(identity_); + if(IceUtilInternal.StringUtil.findFirstOf(id, " :@") != -1) + { + s.Append('"'); + s.Append(id); + s.Append('"'); + } + else + { + s.Append(id); + } + + if(facet_.Length > 0) + { + // + // If the encoded facet string contains characters which + // the reference parser uses as separators, then we enclose + // the facet string in quotes. + // + s.Append(" -f "); + string fs = IceUtilInternal.StringUtil.escapeString(facet_, ""); + if(IceUtilInternal.StringUtil.findFirstOf(fs, " :@") != -1) + { + s.Append('"'); + s.Append(fs); + s.Append('"'); + } + else + { + s.Append(fs); + } + } + + switch(mode_) + { + case Mode.ModeTwoway: + { + s.Append(" -t"); + break; + } + + case Mode.ModeOneway: + { + s.Append(" -o"); + break; + } + + case Mode.ModeBatchOneway: + { + s.Append(" -O"); + break; + } + + case Mode.ModeDatagram: + { + s.Append(" -d"); + break; + } + + case Mode.ModeBatchDatagram: + { + s.Append(" -D"); + break; + } + } + + if(secure_) + { + s.Append(" -s"); + } + + if(!protocol_.Equals(Ice.Util.Protocol_1_0)) + { + // + // We only print the protocol if it's not 1.0. It's fine as + // long as we don't add Ice.Default.ProtocolVersion, a + // stringified proxy will convert back to the same proxy with + // stringToProxy. + // + s.Append(" -p "); + s.Append(Ice.Util.protocolVersionToString(protocol_)); + } + + // + // Always print the encoding version to ensure a stringified proxy + // will convert back to a proxy with the same encoding with + // stringToProxy (and won't use Ice.Default.EncodingVersion). + // + s.Append(" -e "); + s.Append(Ice.Util.encodingVersionToString(encoding_)); + + return s.ToString(); + + // Derived class writes the remainder of the string. + } + + public abstract Dictionary<string, string> toProperty(string prefix); + + public abstract RequestHandler getRequestHandler(Ice.ObjectPrxHelperBase proxy); + + public abstract BatchRequestQueue getBatchRequestQueue(); + + public override bool Equals(object obj) + { + // + // Note: if(this == obj) and type test are performed by each non-abstract derived class. + // + + Reference r = (Reference)obj; // Guaranteed to succeed. + + if(mode_ != r.mode_) + { + return false; + } + + if(secure_ != r.secure_) + { + return false; + } + + if(!identity_.Equals(r.identity_)) + { + return false; + } + + if(!Ice.CollectionComparer.Equals(context_, r.context_)) + { + return false; + } + + if(!facet_.Equals(r.facet_)) + { + return false; + } + + if(overrideCompress_ != r.overrideCompress_) + { + return false; + } + if(overrideCompress_ && compress_ != r.compress_) + { + return false; + } + + if(!protocol_.Equals(r.protocol_)) + { + return false; + } + + if(!encoding_.Equals(r.encoding_)) + { + return false; + } + + if(invocationTimeout_ != r.invocationTimeout_) + { + return false; + } + + return true; + } + + public Object Clone() + { + // + // A member-wise copy is safe because the members are immutable. + // + return MemberwiseClone(); + } + + protected int hashValue_; + protected bool hashInitialized_; + private static Dictionary<string, string> _emptyContext = new Dictionary<string, string>(); + + private Instance instance_; + private Ice.Communicator communicator_; + + private Mode mode_; + private Ice.Identity identity_; + private Dictionary<string, string> context_; + private string facet_; + protected bool secure_; + private Ice.ProtocolVersion protocol_; + private Ice.EncodingVersion encoding_; + private int invocationTimeout_; + + protected bool overrideCompress_; + protected bool compress_; // Only used if _overrideCompress == true + + protected Reference(Instance instance, + Ice.Communicator communicator, + Ice.Identity identity, + string facet, + Mode mode, + bool secure, + Ice.ProtocolVersion protocol, + Ice.EncodingVersion encoding, + int invocationTimeout, + Dictionary<string, string> context) + { + // + // Validate string arguments. + // + Debug.Assert(identity.name != null); + Debug.Assert(identity.category != null); + Debug.Assert(facet != null); + + instance_ = instance; + communicator_ = communicator; + mode_ = mode; + identity_ = identity; + context_ = context != null ? new Dictionary<string, string>(context) : _emptyContext; + facet_ = facet; + protocol_ = protocol; + encoding_ = encoding; + invocationTimeout_ = invocationTimeout; + secure_ = secure; + hashInitialized_ = false; + overrideCompress_ = false; + compress_ = false; + } + + protected static System.Random rand_ = new System.Random(unchecked((int)System.DateTime.Now.Ticks)); + } + + public class FixedReference : Reference + { + public FixedReference(Instance instance, + Ice.Communicator communicator, + Ice.Identity identity, + string facet, + Reference.Mode mode, + bool secure, + Ice.EncodingVersion encoding, + Ice.ConnectionI connection) + : base(instance, communicator, identity, facet, mode, secure, Ice.Util.Protocol_1_0, encoding, -1, null) + { + _fixedConnection = connection; + } + + public override EndpointI[] getEndpoints() + { + return _emptyEndpoints; + } + + public override string getAdapterId() + { + return ""; + } + + public override LocatorInfo getLocatorInfo() + { + return null; + } + + public override RouterInfo getRouterInfo() + { + return null; + } + + public override bool getCollocationOptimized() + { + return false; + } + + public override bool getCacheConnection() + { + return true; + } + + public override bool getPreferSecure() + { + return false; + } + + public override Ice.EndpointSelectionType getEndpointSelection() + { + return Ice.EndpointSelectionType.Random; + } + + public override int getLocatorCacheTimeout() + { + return 0; + } + + public override string getConnectionId() + { + return ""; + } + + public override Reference changeEndpoints(EndpointI[] newEndpoints) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeAdapterId(string newAdapterId) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeLocator(Ice.LocatorPrx newLocator) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeRouter(Ice.RouterPrx newRouter) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeCollocationOptimized(bool newCollocationOptimized) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeCacheConnection(bool newCache) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changePreferSecure(bool prefSec) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeEndpointSelection(Ice.EndpointSelectionType newType) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeLocatorCacheTimeout(int newTimeout) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeTimeout(int newTimeout) + { + throw new Ice.FixedProxyException(); + } + + public override Reference changeConnectionId(string connectionId) + { + throw new Ice.FixedProxyException(); + } + + public override bool isIndirect() + { + return false; + } + + public override bool isWellKnown() + { + return false; + } + + public override void streamWrite(BasicStream s) + { + throw new Ice.FixedProxyException(); + } + + public override string ToString() + { + throw new Ice.FixedProxyException(); + } + + public override Dictionary<string, string> toProperty(string prefix) + { + throw new Ice.FixedProxyException(); + } + + public override RequestHandler getRequestHandler(Ice.ObjectPrxHelperBase proxy) + { + switch(getMode()) + { + case Reference.Mode.ModeTwoway: + case Reference.Mode.ModeOneway: + case Reference.Mode.ModeBatchOneway: + { + if(_fixedConnection.endpoint().datagram()) + { + throw new Ice.NoEndpointException(""); + } + break; + } + + case Reference.Mode.ModeDatagram: + case Reference.Mode.ModeBatchDatagram: + { + if(!_fixedConnection.endpoint().datagram()) + { + throw new Ice.NoEndpointException(""); + } + break; + } + } + + // + // If a secure connection is requested or secure overrides is set, + // check if the connection is secure. + // + bool secure; + DefaultsAndOverrides defaultsAndOverrides = getInstance().defaultsAndOverrides(); + if(defaultsAndOverrides.overrideSecure) + { + secure = defaultsAndOverrides.overrideSecureValue; + } + else + { + secure = getSecure(); + } + if(secure && !_fixedConnection.endpoint().secure()) + { + throw new Ice.NoEndpointException(""); + } + + _fixedConnection.throwException(); // Throw in case our connection is already destroyed. + + bool compress; + if(defaultsAndOverrides.overrideCompress) + { + compress = defaultsAndOverrides.overrideCompressValue; + } + else if(overrideCompress_) + { + compress = compress_; + } + else + { + compress = _fixedConnection.endpoint().compress(); + } + + return ((Ice.ObjectPrxHelperBase)proxy).setRequestHandler__(new ConnectionRequestHandler(this, + _fixedConnection, + compress)); + } + + public override BatchRequestQueue getBatchRequestQueue() + { + return _fixedConnection.getBatchRequestQueue(); + } + + public override bool Equals(object obj) + { + if(object.ReferenceEquals(this, obj)) + { + return true; + } + FixedReference rhs = obj as FixedReference; + if(rhs == null) + { + return false; + } + if(!base.Equals(rhs)) + { + return false; + } + return _fixedConnection.Equals(_fixedConnection); + } + + // + // If we override Equals, we must also override GetHashCode. + // + public override int GetHashCode() + { + return base.GetHashCode(); + } + + private Ice.ConnectionI _fixedConnection; + private static EndpointI[] _emptyEndpoints = new EndpointI[0]; + } + + public class RoutableReference : Reference + { + public override EndpointI[] getEndpoints() + { + return _endpoints; + } + + public override string getAdapterId() + { + return _adapterId; + } + + public override LocatorInfo getLocatorInfo() + { + return _locatorInfo; + } + + public override RouterInfo getRouterInfo() + { + return _routerInfo; + } + + public override bool getCollocationOptimized() + { + return _collocationOptimized; + } + + public override bool getCacheConnection() + { + return _cacheConnection; + } + + public override bool getPreferSecure() + { + return _preferSecure; + } + + public override Ice.EndpointSelectionType getEndpointSelection() + { + return _endpointSelection; + } + + public override int getLocatorCacheTimeout() + { + return _locatorCacheTimeout; + } + + public override string getConnectionId() + { + return _connectionId; + } + + public override Reference changeEncoding(Ice.EncodingVersion newEncoding) + { + RoutableReference r = (RoutableReference)base.changeEncoding(newEncoding); + if(r != this) + { + LocatorInfo locInfo = r._locatorInfo; + if(locInfo != null && !locInfo.getLocator().ice_getEncodingVersion().Equals(newEncoding)) + { + r._locatorInfo = getInstance().locatorManager().get( + (Ice.LocatorPrx)locInfo.getLocator().ice_encodingVersion(newEncoding)); + } + } + return r; + } + + public override Reference changeCompress(bool newCompress) + { + RoutableReference r = (RoutableReference)base.changeCompress(newCompress); + if(r != this && _endpoints.Length > 0) // Also override the compress flag on the endpoints if it was updated + { + EndpointI[] newEndpoints = new EndpointI[_endpoints.Length]; + for(int i = 0; i < _endpoints.Length; i++) + { + newEndpoints[i] = _endpoints[i].compress(newCompress); + } + r._endpoints = newEndpoints; + } + return r; + } + + public override Reference changeEndpoints(EndpointI[] newEndpoints) + { + if(Array.Equals(newEndpoints, _endpoints)) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._endpoints = newEndpoints; + r._adapterId = ""; + r.applyOverrides(ref r._endpoints); + return r; + } + + public override Reference changeAdapterId(string newAdapterId) + { + if(_adapterId.Equals(newAdapterId)) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._adapterId = newAdapterId; + r._endpoints = _emptyEndpoints; + return r; + } + + public override Reference changeLocator(Ice.LocatorPrx newLocator) + { + LocatorInfo newLocatorInfo = getInstance().locatorManager().get(newLocator); + if(newLocatorInfo != null && _locatorInfo != null && newLocatorInfo.Equals(_locatorInfo)) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._locatorInfo = newLocatorInfo; + return r; + } + + public override Reference changeRouter(Ice.RouterPrx newRouter) + { + RouterInfo newRouterInfo = getInstance().routerManager().get(newRouter); + if(newRouterInfo != null && _routerInfo != null && newRouterInfo.Equals(_routerInfo)) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._routerInfo = newRouterInfo; + return r; + } + + public override Reference changeCollocationOptimized(bool newCollocationOptimized) + { + if(newCollocationOptimized == _collocationOptimized) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._collocationOptimized = newCollocationOptimized; + return r; + } + + public override Reference changeCacheConnection(bool newCache) + { + if(newCache == _cacheConnection) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._cacheConnection = newCache; + return r; + } + + public override Reference changePreferSecure(bool newPreferSecure) + { + if(newPreferSecure == _preferSecure) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._preferSecure = newPreferSecure; + return r; + } + + public override Reference changeEndpointSelection(Ice.EndpointSelectionType newType) + { + if(newType == _endpointSelection) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._endpointSelection = newType; + return r; + } + + public override Reference changeLocatorCacheTimeout(int newTimeout) + { + if(newTimeout == _locatorCacheTimeout) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._locatorCacheTimeout = newTimeout; + return r; + } + + public override Reference changeTimeout(int newTimeout) + { + if(_overrideTimeout && _timeout == newTimeout) + { + return this; + } + + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._timeout = newTimeout; + r._overrideTimeout = true; + if(_endpoints.Length > 0) + { + EndpointI[] newEndpoints = new EndpointI[_endpoints.Length]; + for(int i = 0; i < _endpoints.Length; i++) + { + newEndpoints[i] = _endpoints[i].timeout(newTimeout); + } + r._endpoints = newEndpoints; + } + return r; + } + + public override Reference changeConnectionId(string id) + { + if(_connectionId.Equals(id)) + { + return this; + } + RoutableReference r = (RoutableReference)getInstance().referenceFactory().copy(this); + r._connectionId = id; + if(_endpoints.Length > 0) + { + EndpointI[] newEndpoints = new EndpointI[_endpoints.Length]; + for(int i = 0; i < _endpoints.Length; i++) + { + newEndpoints[i] = _endpoints[i].connectionId(id); + } + r._endpoints = newEndpoints; + } + return r; + } + + public override bool isIndirect() + { + return _endpoints.Length == 0; + } + + public override bool isWellKnown() + { + return _endpoints.Length == 0 && _adapterId.Length == 0; + } + + public override void streamWrite(BasicStream s) + { + base.streamWrite(s); + + s.writeSize(_endpoints.Length); + if(_endpoints.Length > 0) + { + Debug.Assert(_adapterId.Length == 0); + foreach(EndpointI endpoint in _endpoints) + { + s.writeShort(endpoint.type()); + endpoint.streamWrite(s); + } + } + else + { + s.writeString(_adapterId); // Adapter id. + } + } + + public override string ToString() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + StringBuilder s = new StringBuilder(); + s.Append(base.ToString()); + + if(_endpoints.Length > 0) + { + for(int i = 0; i < _endpoints.Length; i++) + { + string endp = _endpoints[i].ToString(); + if(endp != null && endp.Length > 0) + { + s.Append(':'); + s.Append(endp); + } + } + } + else if(_adapterId.Length > 0) + { + s.Append(" @ "); + + // + // If the encoded adapter id string contains characters which + // the reference parser uses as separators, then we enclose + // the adapter id string in quotes. + // + string a = IceUtilInternal.StringUtil.escapeString(_adapterId, null); + if(IceUtilInternal.StringUtil.findFirstOf(a, " :@") != -1) + { + s.Append('"'); + s.Append(a); + s.Append('"'); + } + else + { + s.Append(a); + } + } + return s.ToString(); + } + + public override Dictionary<string, string> toProperty(string prefix) + { + Dictionary<string, string> properties = new Dictionary<string, string>(); + + properties[prefix] = ToString(); + properties[prefix + ".CollocationOptimized"] = _collocationOptimized ? "1" : "0"; + properties[prefix + ".ConnectionCached"] = _cacheConnection ? "1" : "0"; + properties[prefix + ".PreferSecure"] = _preferSecure ? "1" : "0"; + properties[prefix + ".EndpointSelection"] = + _endpointSelection == Ice.EndpointSelectionType.Random ? "Random" : "Ordered"; + properties[prefix + ".LocatorCacheTimeout"] = _locatorCacheTimeout.ToString(CultureInfo.InvariantCulture); + properties[prefix + ".InvocationTimeout"] = getInvocationTimeout().ToString(CultureInfo.InvariantCulture); + + if(_routerInfo != null) + { + Ice.ObjectPrxHelperBase h = (Ice.ObjectPrxHelperBase)_routerInfo.getRouter(); + Dictionary<String, String> routerProperties = h.reference__().toProperty(prefix + ".Router"); + foreach(KeyValuePair<string, string> entry in routerProperties) + { + properties[entry.Key] = entry.Value; + } + } + + if(_locatorInfo != null) + { + Ice.ObjectPrxHelperBase h = (Ice.ObjectPrxHelperBase)_locatorInfo.getLocator(); + Dictionary<String, String> locatorProperties = h.reference__().toProperty(prefix + ".Locator"); + foreach(KeyValuePair<string, string> entry in locatorProperties) + { + properties[entry.Key] = entry.Value; + } + } + + return properties; + } + + // + // If we override Equals, we must also override GetHashCode. + // + public override int GetHashCode() + { + lock(this) + { + if(!hashInitialized_) + { + int h = base.GetHashCode(); // Initializes hashValue_. + IceInternal.HashUtil.hashAdd(ref h, _adapterId); + hashValue_ = h; + } + return hashValue_; + } + } + + public override bool Equals(object obj) + { + if(Object.ReferenceEquals(this, obj)) + { + return true; + } + + RoutableReference rhs = obj as RoutableReference; + if(rhs == null) + { + return false; + } + + if(!base.Equals(obj)) + { + return false; + } + + if(_locatorInfo == null ? rhs._locatorInfo != null : !_locatorInfo.Equals(rhs._locatorInfo)) + { + return false; + } + if(_routerInfo == null ? rhs._routerInfo != null : !_routerInfo.Equals(rhs._routerInfo)) + { + return false; + } + if(_collocationOptimized != rhs._collocationOptimized) + { + return false; + } + if(_cacheConnection != rhs._cacheConnection) + { + return false; + } + if(_preferSecure != rhs._preferSecure) + { + return false; + } + if(_endpointSelection != rhs._endpointSelection) + { + return false; + } + if(_locatorCacheTimeout != rhs._locatorCacheTimeout) + { + return false; + } + if(_overrideTimeout != rhs._overrideTimeout) + { + return false; + } + if(_overrideTimeout && _timeout != rhs._timeout) + { + return false; + } + if(!_connectionId.Equals(rhs._connectionId)) + { + return false; + } + if(!_adapterId.Equals(rhs._adapterId)) + { + return false; + } + if(!IceUtilInternal.Arrays.Equals(_endpoints, rhs._endpoints)) + { + return false; + } + return true; + } + + + private sealed class RouterEndpointsCallback : RouterInfo.GetClientEndpointsCallback + { + internal RouterEndpointsCallback(RoutableReference ir, GetConnectionCallback cb) + { + _ir = ir; + _cb = cb; + } + + public void setEndpoints(EndpointI[] endpts) + { + if(endpts.Length > 0) + { + _ir.applyOverrides(ref endpts); + _ir.createConnection(endpts, _cb); + } + else + { + _ir.getConnectionNoRouterInfo(_cb); + } + } + + public void setException(Ice.LocalException ex) + { + _cb.setException(ex); + } + + private RoutableReference _ir; + private GetConnectionCallback _cb; + } + + public override RequestHandler getRequestHandler(Ice.ObjectPrxHelperBase proxy) + { + return getInstance().requestHandlerFactory().getRequestHandler(this, proxy); + } + + public override BatchRequestQueue getBatchRequestQueue() + { + return new BatchRequestQueue(getInstance(), getMode() == Reference.Mode.ModeBatchDatagram); + } + + public void getConnection(GetConnectionCallback callback) + { + if(_routerInfo != null) + { + // + // If we route, we send everything to the router's client + // proxy endpoints. + // + _routerInfo.getClientEndpoints(new RouterEndpointsCallback(this, callback)); + } + else + { + getConnectionNoRouterInfo(callback); + } + } + + private sealed class LocatorEndpointsCallback : LocatorInfo.GetEndpointsCallback + { + internal LocatorEndpointsCallback(RoutableReference ir, GetConnectionCallback cb) + { + _ir = ir; + _cb = cb; + } + + public void setEndpoints(EndpointI[] endpoints, bool cached) + { + if(endpoints.Length == 0) + { + _cb.setException(new Ice.NoEndpointException(_ir.ToString())); + return; + } + + _ir.applyOverrides(ref endpoints); + _ir.createConnection(endpoints, new ConnectionCallback(_ir, _cb, cached)); + } + + public void setException(Ice.LocalException ex) + { + _cb.setException(ex); + } + + private RoutableReference _ir; + private GetConnectionCallback _cb; + } + + private sealed class ConnectionCallback : GetConnectionCallback + { + internal ConnectionCallback(RoutableReference ir, GetConnectionCallback cb, bool cached) + { + _ir = ir; + _cb = cb; + _cached = cached; + } + + public void setConnection(Ice.ConnectionI connection, bool compress) + { + _cb.setConnection(connection, compress); + } + + public void setException(Ice.LocalException exc) + { + try + { + throw exc; + } + catch(Ice.NoEndpointException ex) + { + _cb.setException(ex); // No need to retry if there's no endpoints. + } + catch(Ice.LocalException ex) + { + Debug.Assert(_ir._locatorInfo != null); + _ir._locatorInfo.clearCache(_ir); + if(_cached) + { + TraceLevels traceLevels = _ir.getInstance().traceLevels(); + if(traceLevels.retry >= 2) + { + String s = "connection to cached endpoints failed\n" + + "removing endpoints from cache and trying one more time\n" + ex; + _ir.getInstance().initializationData().logger.trace(traceLevels.retryCat, s); + } + _ir.getConnectionNoRouterInfo(_cb); // Retry. + return; + } + _cb.setException(ex); + } + } + + private RoutableReference _ir; + private GetConnectionCallback _cb; + private bool _cached; + } + + private void getConnectionNoRouterInfo(GetConnectionCallback callback) + { + if(_endpoints.Length > 0) + { + createConnection(_endpoints, callback); + return; + } + + if(_locatorInfo != null) + { + _locatorInfo.getEndpoints(this, _locatorCacheTimeout, new LocatorEndpointsCallback(this, callback)); + } + else + { + callback.setException(new Ice.NoEndpointException(ToString())); + } + } + + public RoutableReference(Instance instance, + Ice.Communicator communicator, + Ice.Identity identity, + string facet, + Reference.Mode mode, + bool secure, + Ice.ProtocolVersion protocol, + Ice.EncodingVersion encoding, + EndpointI[] endpoints, + string adapterId, + LocatorInfo locatorInfo, + RouterInfo routerInfo, + bool collocationOptimized, + bool cacheConnection, + bool preferSecure, + Ice.EndpointSelectionType endpointSelection, + int locatorCacheTimeout, + int invocationTimeout, + Dictionary<string, string> context) + : base(instance, communicator, identity, facet, mode, secure, protocol, encoding, invocationTimeout, context) + { + _endpoints = endpoints; + _adapterId = adapterId; + _locatorInfo = locatorInfo; + _routerInfo = routerInfo; + _collocationOptimized = collocationOptimized; + _cacheConnection = cacheConnection; + _preferSecure = preferSecure; + _endpointSelection = endpointSelection; + _locatorCacheTimeout = locatorCacheTimeout; + _overrideTimeout = false; + _timeout = -1; + + if(_endpoints == null) + { + _endpoints = _emptyEndpoints; + } + + if(_adapterId == null) + { + _adapterId = ""; + } + + Debug.Assert(_adapterId.Length == 0 || _endpoints.Length == 0); + } + + protected void applyOverrides(ref EndpointI[] endpts) + { + for(int i = 0; i < endpts.Length; ++i) + { + endpts[i] = endpts[i].connectionId(_connectionId); + if(overrideCompress_) + { + endpts[i] = endpts[i].compress(compress_); + } + if(_overrideTimeout) + { + endpts[i] = endpts[i].timeout(_timeout); + } + } + } + + private EndpointI[] filterEndpoints(EndpointI[] allEndpoints) + { + List<EndpointI> endpoints = new List<EndpointI>(); + + // + // Filter out unknown endpoints. + // + for(int i = 0; i < allEndpoints.Length; i++) + { + if(!(allEndpoints[i] is IceInternal.OpaqueEndpointI)) + { + endpoints.Add(allEndpoints[i]); + } + } + + // + // Filter out endpoints according to the mode of the reference. + // + switch(getMode()) + { + case Reference.Mode.ModeTwoway: + case Reference.Mode.ModeOneway: + case Reference.Mode.ModeBatchOneway: + { + // + // Filter out datagram endpoints. + // + List<EndpointI> tmp = new List<EndpointI>(); + foreach(EndpointI endpoint in endpoints) + { + if(!endpoint.datagram()) + { + tmp.Add(endpoint); + } + } + endpoints = tmp; + break; + } + + case Reference.Mode.ModeDatagram: + case Reference.Mode.ModeBatchDatagram: + { + // + // Filter out non-datagram endpoints. + // + List<EndpointI> tmp = new List<EndpointI>(); + foreach(EndpointI endpoint in endpoints) + { + if(endpoint.datagram()) + { + tmp.Add(endpoint); + } + } + endpoints = tmp; + break; + } + } + + // + // Sort the endpoints according to the endpoint selection type. + // + switch(getEndpointSelection()) + { + case Ice.EndpointSelectionType.Random: + { + lock(rand_) + { + for(int i = 0; i < endpoints.Count - 1; ++i) + { + int r = rand_.Next(endpoints.Count - i) + i; + Debug.Assert(r >= i && r < endpoints.Count); + if(r != i) + { + EndpointI tmp = endpoints[i]; + endpoints[i] = endpoints[r]; + endpoints[r] = tmp; + } + } + } + break; + } + case Ice.EndpointSelectionType.Ordered: + { + // Nothing to do. + break; + } + default: + { + Debug.Assert(false); + break; + } + } + + // + // If a secure connection is requested or secure overrides + // is set, remove all non-secure endpoints. Otherwise make + // non-secure endpoints preferred over secure endpoints by + // partitioning the endpoint vector, so that non-secure + // endpoints come first. + // + DefaultsAndOverrides overrides = getInstance().defaultsAndOverrides(); + if(overrides.overrideSecure ? overrides.overrideSecureValue : getSecure()) + { + List<EndpointI> tmp = new List<EndpointI>(); + foreach(EndpointI endpoint in endpoints) + { + if(endpoint.secure()) + { + tmp.Add(endpoint); + } + } + endpoints = tmp; + } + else if(getPreferSecure()) + { + IceUtilInternal.Collections.Sort(ref endpoints, _preferSecureEndpointComparator); + } + else + { + IceUtilInternal.Collections.Sort(ref endpoints, _preferNonSecureEndpointComparator); + } + + EndpointI[] arr = new EndpointI[endpoints.Count]; + endpoints.CopyTo(arr); + return arr; + } + + private sealed class CreateConnectionCallback : OutgoingConnectionFactory.CreateConnectionCallback + { + internal CreateConnectionCallback(RoutableReference rr, EndpointI[] endpoints, GetConnectionCallback cb) + { + _rr = rr; + _endpoints = endpoints; + _callback = cb; + } + + public void setConnection(Ice.ConnectionI connection, bool compress) + { + // + // If we have a router, set the object adapter for this router + // (if any) to the new connection, so that callbacks from the + // router can be received over this new connection. + // + if(_rr._routerInfo != null && _rr._routerInfo.getAdapter() != null) + { + connection.setAdapter(_rr._routerInfo.getAdapter()); + } + _callback.setConnection(connection, compress); + } + + public void setException(Ice.LocalException ex) + { + if(_exception == null) + { + _exception = ex; + } + + if(_endpoints == null || ++_i == _endpoints.Length) + { + _callback.setException(_exception); + return; + } + + bool more = _i != _endpoints.Length - 1; + EndpointI[] endpoint = new EndpointI[]{ _endpoints[_i] }; + _rr.getInstance().outgoingConnectionFactory().create(endpoint, more, _rr.getEndpointSelection(), this); + } + + private RoutableReference _rr; + private EndpointI[] _endpoints; + private GetConnectionCallback _callback; + private int _i = 0; + private Ice.LocalException _exception = null; + } + + protected void createConnection(EndpointI[] allEndpoints, GetConnectionCallback callback) + { + EndpointI[] endpoints = filterEndpoints(allEndpoints); + if(endpoints.Length == 0) + { + callback.setException(new Ice.NoEndpointException(ToString())); + return; + } + + // + // Finally, create the connection. + // + OutgoingConnectionFactory factory = getInstance().outgoingConnectionFactory(); + if(getCacheConnection() || endpoints.Length == 1) + { + // + // Get an existing connection or create one if there's no + // existing connection to one of the given endpoints. + // + factory.create(endpoints, false, getEndpointSelection(), + new CreateConnectionCallback(this, null, callback)); + } + else + { + // + // Go through the list of endpoints and try to create the + // connection until it succeeds. This is different from just + // calling create() with the given endpoints since this might + // create a new connection even if there's an existing + // connection for one of the endpoints. + // + + factory.create(new EndpointI[]{ endpoints[0] }, true, getEndpointSelection(), + new CreateConnectionCallback(this, endpoints, callback)); + } + } + + private class EndpointComparator : IComparer<IceInternal.EndpointI> + { + public EndpointComparator(bool preferSecure) + { + _preferSecure = preferSecure; + } + + public int Compare(IceInternal.EndpointI le, IceInternal.EndpointI re) + { + bool ls = le.secure(); + bool rs = re.secure(); + if((ls && rs) || (!ls && !rs)) + { + return 0; + } + else if(!ls && rs) + { + if(_preferSecure) + { + return 1; + } + else + { + return -1; + } + } + else + { + if(_preferSecure) + { + return -1; + } + else + { + return 1; + } + } + } + + private bool _preferSecure; + } + + private static EndpointComparator _preferNonSecureEndpointComparator = new EndpointComparator(false); + private static EndpointComparator _preferSecureEndpointComparator = new EndpointComparator(true); + private static EndpointI[] _emptyEndpoints = new EndpointI[0]; + + private EndpointI[] _endpoints; + private string _adapterId; + private LocatorInfo _locatorInfo; // Null if no locator is used. + private RouterInfo _routerInfo; // Null if no router is used. + private bool _collocationOptimized; + private bool _cacheConnection; + private bool _preferSecure; + private Ice.EndpointSelectionType _endpointSelection; + private int _locatorCacheTimeout; + + private bool _overrideTimeout; + private int _timeout; // Only used if _overrideTimeout == true + private string _connectionId = ""; + } +} diff --git a/csharp/src/Ice/ReferenceFactory.cs b/csharp/src/Ice/ReferenceFactory.cs new file mode 100644 index 00000000000..3d8e23287a4 --- /dev/null +++ b/csharp/src/Ice/ReferenceFactory.cs @@ -0,0 +1,926 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Text.RegularExpressions; + +namespace IceInternal +{ + + public class ReferenceFactory + { + public Reference + create(Ice.Identity ident, string facet, Reference tmpl, EndpointI[] endpoints) + { + if(ident.name.Length == 0 && ident.category.Length == 0) + { + return null; + } + + return create(ident, facet, tmpl.getMode(), tmpl.getSecure(), tmpl.getProtocol(), tmpl.getEncoding(), + endpoints, null, null); + } + + public Reference + create(Ice.Identity ident, string facet, Reference tmpl, string adapterId) + { + if(ident.name.Length == 0 && ident.category.Length == 0) + { + return null; + } + + // + // Create new reference + // + return create(ident, facet, tmpl.getMode(), tmpl.getSecure(), tmpl.getProtocol(), tmpl.getEncoding(), + null, adapterId, null); + } + + public Reference create(Ice.Identity ident, Ice.ConnectionI connection) + { + if(ident.name.Length == 0 && ident.category.Length == 0) + { + return null; + } + + // + // Create new reference + // + return new FixedReference( + instance_, + _communicator, + ident, + "", // Facet + connection.endpoint().datagram() ? Reference.Mode.ModeDatagram : Reference.Mode.ModeTwoway, + connection.endpoint().secure(), + instance_.defaultsAndOverrides().defaultEncoding, + connection); + } + + public Reference copy(Reference r) + { + Ice.Identity ident = r.getIdentity(); + if(ident.name.Length == 0 && ident.category.Length == 0) + { + return null; + } + return (Reference)r.Clone(); + } + + public Reference create(string s, string propertyPrefix) + { + if(s.Length == 0) + { + return null; + } + + const string delim = " \t\n\r"; + + int beg; + int end = 0; + + beg = IceUtilInternal.StringUtil.findFirstNotOf(s, delim, end); + if(beg == -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "no non-whitespace characters found in `" + s + "'"; + throw e; + } + + // + // Extract the identity, which may be enclosed in single + // or double quotation marks. + // + string idstr = null; + end = IceUtilInternal.StringUtil.checkQuote(s, beg); + if(end == -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "mismatched quotes around identity in `" + s + "'"; + throw e; + } + else if(end == 0) + { + end = IceUtilInternal.StringUtil.findFirstOf(s, delim + ":@", beg); + if(end == -1) + { + end = s.Length; + } + idstr = s.Substring(beg, end - beg); + } + else + { + beg++; // Skip leading quote + idstr = s.Substring(beg, end - beg); + end++; // Skip trailing quote + } + + if(beg == end) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "no identity in `" + s + "'"; + throw e; + } + + // + // Parsing the identity may raise IdentityParseException. + // + Ice.Identity ident = instance_.stringToIdentity(idstr); + + if(ident.name.Length == 0) + { + // + // An identity with an empty name and a non-empty + // category is illegal. + // + if(ident.category.Length > 0) + { + Ice.IllegalIdentityException e = new Ice.IllegalIdentityException(); + e.id = ident; + throw e; + } + // + // Treat a stringified proxy containing two double + // quotes ("") the same as an empty string, i.e., + // a null proxy, but only if nothing follows the + // quotes. + // + else if(IceUtilInternal.StringUtil.findFirstNotOf(s, delim, end) != -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "invalid characters after identity in `" + s + "'"; + throw e; + } + else + { + return null; + } + } + + string facet = ""; + Reference.Mode mode = Reference.Mode.ModeTwoway; + bool secure = false; + Ice.EncodingVersion encoding = instance_.defaultsAndOverrides().defaultEncoding; + Ice.ProtocolVersion protocol = Ice.Util.Protocol_1_0; + string adapter = ""; + + while(true) + { + beg = IceUtilInternal.StringUtil.findFirstNotOf(s, delim, end); + if(beg == -1) + { + break; + } + + if(s[beg] == ':' || s[beg] == '@') + { + break; + } + + end = IceUtilInternal.StringUtil.findFirstOf(s, delim + ":@", beg); + if(end == -1) + { + end = s.Length; + } + + if(beg == end) + { + break; + } + + string option = s.Substring(beg, end - beg); + if(option.Length != 2 || option[0] != '-') + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "expected a proxy option but found `" + option + "' in `" + s + "'"; + throw e; + } + + // + // Check for the presence of an option argument. The + // argument may be enclosed in single or double + // quotation marks. + // + string argument = null; + int argumentBeg = IceUtilInternal.StringUtil.findFirstNotOf(s, delim, end); + if(argumentBeg != -1) + { + char ch = s[argumentBeg]; + if(ch != '@' && ch != ':' && ch != '-') + { + beg = argumentBeg; + end = IceUtilInternal.StringUtil.checkQuote(s, beg); + if(end == -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "mismatched quotes around value for " + option + " option in `" + s + "'"; + throw e; + } + else if(end == 0) + { + end = IceUtilInternal.StringUtil.findFirstOf(s, delim + ":@", beg); + if(end == -1) + { + end = s.Length; + } + argument = s.Substring(beg, end - beg); + } + else + { + beg++; // Skip leading quote + argument = s.Substring(beg, end - beg); + end++; // Skip trailing quote + } + } + } + + // + // If any new options are added here, + // IceInternal::Reference::toString() and its derived classes must be updated as well. + // + switch(option[1]) + { + case 'f': + { + if(argument == null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "no argument provided for -f option in `" + s + "'"; + throw e; + } + + try + { + facet = IceUtilInternal.StringUtil.unescapeString(argument, 0, argument.Length); + } + catch(System.ArgumentException argEx) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "invalid facet in `" + s + "': " + argEx.Message; + throw e; + } + break; + } + + case 't': + { + if(argument != null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unexpected argument `" + argument + "' provided for -t option in `" + s + "'"; + throw e; + } + mode = Reference.Mode.ModeTwoway; + break; + } + + case 'o': + { + if(argument != null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unexpected argument `" + argument + "' provided for -o option in `" + s + "'"; + throw e; + } + mode = Reference.Mode.ModeOneway; + break; + } + + case 'O': + { + if(argument != null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unexpected argument `" + argument + "' provided for -O option in `" + s + "'"; + throw e; + } + mode = Reference.Mode.ModeBatchOneway; + break; + } + + case 'd': + { + if(argument != null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unexpected argument `" + argument + "' provided for -d option in `" + s + "'"; + throw e; + } + mode = Reference.Mode.ModeDatagram; + break; + } + + case 'D': + { + if(argument != null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unexpected argument `" + argument + "' provided for -D option in `" + s + "'"; + throw e; + } + mode = Reference.Mode.ModeBatchDatagram; + break; + } + + case 's': + { + if(argument != null) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unexpected argument `" + argument + "' provided for -s option in `" + s + "'"; + throw e; + } + secure = true; + break; + } + + case 'e': + { + if(argument == null) + { + throw new Ice.ProxyParseException("no argument provided for -e option `" + s + "'"); + } + + try + { + encoding = Ice.Util.stringToEncodingVersion(argument); + } + catch(Ice.VersionParseException e) + { + throw new Ice.ProxyParseException("invalid encoding version `" + argument + "' in `" + s + + "':\n" + e.str); + } + break; + } + + case 'p': + { + if(argument == null) + { + throw new Ice.ProxyParseException("no argument provided for -p option `" + s + "'"); + } + + try + { + protocol = Ice.Util.stringToProtocolVersion(argument); + } + catch(Ice.VersionParseException e) + { + throw new Ice.ProxyParseException("invalid protocol version `" + argument + "' in `" + s + + "':\n" + e.str); + } + break; + } + + default: + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "unknown option `" + option + "' in `" + s + "'"; + throw e; + } + } + } + + if(beg == -1) + { + return create(ident, facet, mode, secure, protocol, encoding, null, null, propertyPrefix); + } + + List<EndpointI> endpoints = new List<EndpointI>(); + + if(s[beg] == ':') + { + List<string> unknownEndpoints = new List<string>(); + end = beg; + + while(end < s.Length && s[end] == ':') + { + beg = end + 1; + + end = beg; + while(true) + { + end = s.IndexOf(':', end); + if(end == -1) + { + end = s.Length; + break; + } + else + { + bool quoted = false; + int quote = beg; + while(true) + { + quote = s.IndexOf((System.Char) '\"', quote); + if(quote == -1 || end < quote) + { + break; + } + else + { + quote = s.IndexOf((System.Char) '\"', ++quote); + if(quote == -1) + { + break; + } + else if(end < quote) + { + quoted = true; + break; + } + ++quote; + } + } + if(!quoted) + { + break; + } + ++end; + } + } + + string es = s.Substring(beg, end - beg); + EndpointI endp = instance_.endpointFactoryManager().create(es, false); + if(endp != null) + { + endpoints.Add(endp); + } + else + { + unknownEndpoints.Add(es); + } + } + if(endpoints.Count == 0) + { + Debug.Assert(unknownEndpoints.Count > 0); + Ice.EndpointParseException e2 = new Ice.EndpointParseException(); + e2.str = "invalid endpoint `" + unknownEndpoints[0] + "' in `" + s + "'"; + throw e2; + } + else if(unknownEndpoints.Count != 0 && + instance_.initializationData().properties.getPropertyAsIntWithDefault( + "Ice.Warn.Endpoints", 1) > 0) + { + StringBuilder msg = new StringBuilder("Proxy contains unknown endpoints:"); + int sz = unknownEndpoints.Count; + for(int idx = 0; idx < sz; ++idx) + { + msg.Append(" `"); + msg.Append((string)unknownEndpoints[idx]); + msg.Append("'"); + } + instance_.initializationData().logger.warning(msg.ToString()); + } + + EndpointI[] ep = endpoints.ToArray(); + return create(ident, facet, mode, secure, protocol, encoding, ep, null, propertyPrefix); + } + else if(s[beg] == '@') + { + beg = IceUtilInternal.StringUtil.findFirstNotOf(s, delim, beg + 1); + if(beg == -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "missing adapter id in `" + s + "'"; + throw e; + } + + string adapterstr = null; + end = IceUtilInternal.StringUtil.checkQuote(s, beg); + if(end == -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "mismatched quotes around adapter id in `" + s + "'"; + throw e; + } + else if(end == 0) + { + end = IceUtilInternal.StringUtil.findFirstOf(s, delim, beg); + if(end == -1) + { + end = s.Length; + } + adapterstr = s.Substring(beg, end - beg); + } + else + { + beg++; // Skip leading quote + adapterstr = s.Substring(beg, end - beg); + end++; // Skip trailing quote + } + + if(end != s.Length && IceUtilInternal.StringUtil.findFirstNotOf(s, delim, end) != -1) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "invalid trailing characters after `" + s.Substring(0, end + 1) + "' in `" + s + "'"; + throw e; + } + + try + { + adapter = IceUtilInternal.StringUtil.unescapeString(adapterstr, 0, adapterstr.Length); + } + catch(System.ArgumentException argEx) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "invalid adapter id in `" + s + "': " + argEx.Message; + throw e; + } + if(adapter.Length == 0) + { + Ice.ProxyParseException e = new Ice.ProxyParseException(); + e.str = "empty adapter id in `" + s + "'"; + throw e; + } + return create(ident, facet, mode, secure, protocol, encoding, null, adapter, propertyPrefix); + } + + Ice.ProxyParseException ex = new Ice.ProxyParseException(); + ex.str = "malformed proxy `" + s + "'"; + throw ex; + } + + public Reference create(Ice.Identity ident, BasicStream s) + { + // + // Don't read the identity here. Operations calling this + // constructor read the identity, and pass it as a parameter. + // + + if(ident.name.Length == 0 && ident.category.Length == 0) + { + return null; + } + + // + // For compatibility with the old FacetPath. + // + string[] facetPath = s.readStringSeq(); + string facet; + if(facetPath.Length > 0) + { + if(facetPath.Length > 1) + { + throw new Ice.ProxyUnmarshalException(); + } + facet = facetPath[0]; + } + else + { + facet = ""; + } + + int mode = (int)s.readByte(); + if(mode < 0 || mode > (int)Reference.Mode.ModeLast) + { + throw new Ice.ProxyUnmarshalException(); + } + + bool secure = s.readBool(); + + Ice.ProtocolVersion protocol; + Ice.EncodingVersion encoding; + if(!s.getReadEncoding().Equals(Ice.Util.Encoding_1_0)) + { + protocol = new Ice.ProtocolVersion(); + protocol.read__(s); + encoding = new Ice.EncodingVersion(); + encoding.read__(s); + } + else + { + protocol = Ice.Util.Protocol_1_0; + encoding = Ice.Util.Encoding_1_0; + } + + EndpointI[] endpoints = null; + string adapterId = ""; + + int sz = s.readSize(); + if(sz > 0) + { + endpoints = new EndpointI[sz]; + for(int i = 0; i < sz; i++) + { + endpoints[i] = instance_.endpointFactoryManager().read(s); + } + } + else + { + adapterId = s.readString(); + } + + return create(ident, facet, (Reference.Mode)mode, secure, protocol, encoding, endpoints, adapterId, null); + } + + public ReferenceFactory setDefaultRouter(Ice.RouterPrx defaultRouter) + { + if(_defaultRouter == null ? defaultRouter == null : _defaultRouter.Equals(defaultRouter)) + { + return this; + } + + ReferenceFactory factory = new ReferenceFactory(instance_, _communicator); + factory._defaultLocator = _defaultLocator; + factory._defaultRouter = defaultRouter; + return factory; + } + + public Ice.RouterPrx getDefaultRouter() + { + return _defaultRouter; + } + + public ReferenceFactory setDefaultLocator(Ice.LocatorPrx defaultLocator) + { + if(_defaultLocator == null ? defaultLocator == null : _defaultLocator.Equals(defaultLocator)) + { + return this; + } + + ReferenceFactory factory = new ReferenceFactory(instance_, _communicator); + factory._defaultLocator = defaultLocator; + factory._defaultRouter = _defaultRouter; + return factory; + } + + public Ice.LocatorPrx getDefaultLocator() + { + return _defaultLocator; + } + + // + // Only for use by Instance + // + internal ReferenceFactory(Instance instance, Ice.Communicator communicator) + { + instance_ = instance; + _communicator = communicator; + } + + static private readonly string[] _suffixes = + { + "EndpointSelection", + "ConnectionCached", + "PreferSecure", + "LocatorCacheTimeout", + "InvocationTimeout", + "Locator", + "Router", + "CollocationOptimized", + "Context\\..*" + }; + + private void + checkForUnknownProperties(String prefix) + { + // + // Do not warn about unknown properties if Ice prefix, ie Ice, Glacier2, etc + // + for(int i = 0; IceInternal.PropertyNames.clPropNames[i] != null; ++i) + { + if(prefix.StartsWith(IceInternal.PropertyNames.clPropNames[i] + ".", StringComparison.Ordinal)) + { + return; + } + } + + List<string> unknownProps = new List<string>(); + Dictionary<string, string> props + = instance_.initializationData().properties.getPropertiesForPrefix(prefix + "."); + foreach(String prop in props.Keys) + { + bool valid = false; + for(int i = 0; i < _suffixes.Length; ++i) + { + string pattern = "^" + Regex.Escape(prefix + ".") + _suffixes[i] + "$"; + if(new Regex(pattern).Match(prop).Success) + { + valid = true; + break; + } + } + + if(!valid) + { + unknownProps.Add(prop); + } + } + + if(unknownProps.Count != 0) + { + StringBuilder message = new StringBuilder("found unknown properties for proxy '"); + message.Append(prefix); + message.Append("':"); + foreach(string s in unknownProps) + { + message.Append("\n "); + message.Append(s); + } + instance_.initializationData().logger.warning(message.ToString()); + } + } + + private Reference create(Ice.Identity ident, + string facet, + Reference.Mode mode, + bool secure, + Ice.ProtocolVersion protocol, + Ice.EncodingVersion encoding, + EndpointI[] endpoints, + string adapterId, + string propertyPrefix) + { + DefaultsAndOverrides defaultsAndOverrides = instance_.defaultsAndOverrides(); + + // + // Default local proxy options. + // + LocatorInfo locatorInfo = null; + if(_defaultLocator != null) + { + if(!((Ice.ObjectPrxHelperBase)_defaultLocator).reference__().getEncoding().Equals(encoding)) + { + locatorInfo = instance_.locatorManager().get( + (Ice.LocatorPrx)_defaultLocator.ice_encodingVersion(encoding)); + } + else + { + locatorInfo = instance_.locatorManager().get(_defaultLocator); + } + } + RouterInfo routerInfo = instance_.routerManager().get(_defaultRouter); + bool collocOptimized = defaultsAndOverrides.defaultCollocationOptimization; + bool cacheConnection = true; + bool preferSecure = defaultsAndOverrides.defaultPreferSecure; + Ice.EndpointSelectionType endpointSelection = defaultsAndOverrides.defaultEndpointSelection; + int locatorCacheTimeout = defaultsAndOverrides.defaultLocatorCacheTimeout; + int invocationTimeout = defaultsAndOverrides.defaultInvocationTimeout; + Dictionary<string, string> context = null; + + // + // Override the defaults with the proxy properties if a property prefix is defined. + // + if(propertyPrefix != null && propertyPrefix.Length > 0) + { + Ice.Properties properties = instance_.initializationData().properties; + + // + // Warn about unknown properties. + // + if(properties.getPropertyAsIntWithDefault("Ice.Warn.UnknownProperties", 1) > 0) + { + checkForUnknownProperties(propertyPrefix); + } + + string property; + + property = propertyPrefix + ".Locator"; + Ice.LocatorPrx locator = Ice.LocatorPrxHelper.uncheckedCast(_communicator.propertyToProxy(property)); + if(locator != null) + { + if(!((Ice.ObjectPrxHelperBase)locator).reference__().getEncoding().Equals(encoding)) + { + locatorInfo = instance_.locatorManager().get( + (Ice.LocatorPrx)locator.ice_encodingVersion(encoding)); + } + else + { + locatorInfo = instance_.locatorManager().get(locator); + } + } + + property = propertyPrefix + ".Router"; + Ice.RouterPrx router = Ice.RouterPrxHelper.uncheckedCast(_communicator.propertyToProxy(property)); + if(router != null) + { + if(propertyPrefix.EndsWith(".Router", StringComparison.Ordinal)) + { + string s = "`" + property + "=" + properties.getProperty(property) + + "': cannot set a router on a router; setting ignored"; + instance_.initializationData().logger.warning(s); + } + else + { + routerInfo = instance_.routerManager().get(router); + } + } + + property = propertyPrefix + ".CollocationOptimized"; + collocOptimized = properties.getPropertyAsIntWithDefault(property, collocOptimized ? 1 : 0) > 0; + + property = propertyPrefix + ".ConnectionCached"; + cacheConnection = properties.getPropertyAsIntWithDefault(property, cacheConnection ? 1 : 0) > 0; + + property = propertyPrefix + ".PreferSecure"; + preferSecure = properties.getPropertyAsIntWithDefault(property, preferSecure ? 1 : 0) > 0; + + property = propertyPrefix + ".EndpointSelection"; + if(properties.getProperty(property).Length > 0) + { + string type = properties.getProperty(property); + if(type.Equals("Random")) + { + endpointSelection = Ice.EndpointSelectionType.Random; + } + else if(type.Equals("Ordered")) + { + endpointSelection = Ice.EndpointSelectionType.Ordered; + } + else + { + throw new Ice.EndpointSelectionTypeParseException("illegal value `" + type + + "'; expected `Random' or `Ordered'"); + } + } + + property = propertyPrefix + ".LocatorCacheTimeout"; + string val = properties.getProperty(property); + if(val.Length > 0) + { + locatorCacheTimeout = properties.getPropertyAsIntWithDefault(property, locatorCacheTimeout); + if(locatorCacheTimeout < -1) + { + locatorCacheTimeout = -1; + + StringBuilder msg = new StringBuilder("invalid value for "); + msg.Append(property); + msg.Append(" `"); + msg.Append(properties.getProperty(property)); + msg.Append("': defaulting to -1"); + instance_.initializationData().logger.warning(msg.ToString()); + } + } + + property = propertyPrefix + ".InvocationTimeout"; + val = properties.getProperty(property); + if(val.Length > 0) + { + invocationTimeout = properties.getPropertyAsIntWithDefault(property, invocationTimeout); + if(invocationTimeout < 1 && invocationTimeout != -1) + { + invocationTimeout = -1; + + StringBuilder msg = new StringBuilder("invalid value for "); + msg.Append(property); + msg.Append(" `"); + msg.Append(properties.getProperty(property)); + msg.Append("': defaulting to -1"); + instance_.initializationData().logger.warning(msg.ToString()); + } + } + + property = propertyPrefix + ".Context."; + Dictionary<string, string> contexts = properties.getPropertiesForPrefix(property); + if(contexts.Count != 0) + { + context = new Dictionary<string, string>(); + foreach(KeyValuePair<string, string> e in contexts) + { + context.Add(e.Key.Substring(property.Length), e.Value); + } + } + } + + // + // Create new reference + // + return new RoutableReference(instance_, + _communicator, + ident, + facet, + mode, + secure, + protocol, + encoding, + endpoints, + adapterId, + locatorInfo, + routerInfo, + collocOptimized, + cacheConnection, + preferSecure, + endpointSelection, + locatorCacheTimeout, + invocationTimeout, + context); + } + + private Instance instance_; + private Ice.Communicator _communicator; + private Ice.RouterPrx _defaultRouter; + private Ice.LocatorPrx _defaultLocator; + } + +} diff --git a/csharp/src/Ice/ReplyStatus.cs b/csharp/src/Ice/ReplyStatus.cs new file mode 100644 index 00000000000..5ad5c6cb7fe --- /dev/null +++ b/csharp/src/Ice/ReplyStatus.cs @@ -0,0 +1,27 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + sealed class ReplyStatus + { + public const byte replyOK = 0; + public const byte replyUserException = 1; + public const byte replyObjectNotExist = 2; + public const byte replyFacetNotExist = 3; + public const byte replyOperationNotExist = 4; + public const byte replyUnknownLocalException = 5; + public const byte replyUnknownUserException = 6; + public const byte replyUnknownException = 7; + + private ReplyStatus() + { + } + } +} diff --git a/csharp/src/Ice/RequestHandler.cs b/csharp/src/Ice/RequestHandler.cs new file mode 100644 index 00000000000..4593fd35fe3 --- /dev/null +++ b/csharp/src/Ice/RequestHandler.cs @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections.Generic; +using Ice.Instrumentation; + +namespace IceInternal +{ + public interface CancellationHandler + { + void asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex); + } + + public interface RequestHandler : CancellationHandler + { + RequestHandler update(RequestHandler previousHandler, RequestHandler newHandler); + + bool sendAsyncRequest(ProxyOutgoingAsyncBase @out, out Ice.AsyncCallback cb); + + Reference getReference(); + + Ice.ConnectionI getConnection(); + } +} diff --git a/csharp/src/Ice/RequestHandlerFactory.cs b/csharp/src/Ice/RequestHandlerFactory.cs new file mode 100644 index 00000000000..cc611ccba47 --- /dev/null +++ b/csharp/src/Ice/RequestHandlerFactory.cs @@ -0,0 +1,81 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections.Generic; +using System.Diagnostics; + +namespace IceInternal +{ + public class RequestHandlerFactory + { + internal RequestHandlerFactory(Instance instance) + { + _instance = instance; + } + + public RequestHandler + getRequestHandler(RoutableReference rf, Ice.ObjectPrxHelperBase proxy) + { + if(rf.getCollocationOptimized()) + { + Ice.ObjectAdapter adapter = _instance.objectAdapterFactory().findObjectAdapter(proxy); + if(adapter != null) + { + return proxy.setRequestHandler__(new CollocatedRequestHandler(rf, adapter)); + } + } + + bool connect = false; + ConnectRequestHandler handler; + if(rf.getCacheConnection()) + { + lock(this) + { + if(!_handlers.TryGetValue(rf, out handler)) + { + handler = new ConnectRequestHandler(rf, proxy); + _handlers.Add(rf, handler); + connect = true; + } + } + } + else + { + handler = new ConnectRequestHandler(rf, proxy); + connect = true; + } + + if(connect) + { + rf.getConnection(handler); + } + return proxy.setRequestHandler__(handler.connect(proxy)); + } + + internal void + removeRequestHandler(Reference rf, RequestHandler handler) + { + if(rf.getCacheConnection()) + { + lock(this) + { + ConnectRequestHandler h; + if(_handlers.TryGetValue(rf, out h) && h == handler) + { + _handlers.Remove(rf); + } + } + } + } + + readonly Instance _instance; + readonly Dictionary<Reference, ConnectRequestHandler> _handlers = + new Dictionary<Reference, ConnectRequestHandler>(); + } +} diff --git a/csharp/src/Ice/ResponseHandler.cs b/csharp/src/Ice/ResponseHandler.cs new file mode 100644 index 00000000000..e39fbfe5d64 --- /dev/null +++ b/csharp/src/Ice/ResponseHandler.cs @@ -0,0 +1,22 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Collections.Generic; +using Ice.Instrumentation; + +namespace IceInternal +{ + public interface ResponseHandler + { + void sendResponse(int requestId, BasicStream os, byte status, bool amd); + void sendNoResponse(); + bool systemException(int requestId, Ice.SystemException ex, bool amd); + void invokeException(int requestId, Ice.LocalException ex, int invokeNum, bool amd); + }; +} diff --git a/csharp/src/Ice/RetryQueue.cs b/csharp/src/Ice/RetryQueue.cs new file mode 100644 index 00000000000..427bceb5f9e --- /dev/null +++ b/csharp/src/Ice/RetryQueue.cs @@ -0,0 +1,154 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Collections.Generic; + using System.Diagnostics; + + public class RetryTask : TimerTask, CancellationHandler + { + public RetryTask(Instance instance, RetryQueue retryQueue, ProxyOutgoingAsyncBase outAsync) + { + _instance = instance; + _retryQueue = retryQueue; + _outAsync = outAsync; + } + + public void runTimerTask() + { + _outAsync.retry(); + + // + // NOTE: this must be called last, destroy() blocks until all task + // are removed to prevent the client thread pool to be destroyed + // (we still need the client thread pool at this point to call + // exception callbacks with CommunicatorDestroyedException). + // + _retryQueue.remove(this); + } + + public void asyncRequestCanceled(OutgoingAsyncBase outAsync, Ice.LocalException ex) + { + Debug.Assert(_outAsync == outAsync); + if(_retryQueue.cancel(this)) + { + if(_instance.traceLevels().retry >= 1) + { + string s = "operation retry canceled\n" + ex; + _instance.initializationData().logger.trace(_instance.traceLevels().retryCat, s); + } + Ice.AsyncCallback cb = _outAsync.completed(ex); + if(cb != null) + { + _outAsync.invokeCompletedAsync(cb); + } + } + } + + public void destroy() + { + try + { + _outAsync.abort(new Ice.CommunicatorDestroyedException()); + } + catch(Ice.CommunicatorDestroyedException) + { + // Abort can throw if there's no callback, just ignore in this case + } + } + + private Instance _instance; + private RetryQueue _retryQueue; + private ProxyOutgoingAsyncBase _outAsync; + } + + public class RetryQueue + { + public RetryQueue(Instance instance) + { + _instance = instance; + } + + public void add(ProxyOutgoingAsyncBase outAsync, int interval) + { + lock(this) + { + if(_instance == null) + { + throw new Ice.CommunicatorDestroyedException(); + } + RetryTask task = new RetryTask(_instance, this, outAsync); + outAsync.cancelable(task); // This will throw if the request is canceled. + _instance.timer().schedule(task, interval); + _requests.Add(task, null); + } + } + + public void destroy() + { + lock(this) + { + Dictionary<RetryTask, object> keep = new Dictionary<RetryTask, object>(); + foreach(RetryTask task in _requests.Keys) + { + if(_instance.timer().cancel(task)) + { + task.destroy(); + } + else + { + keep.Add(task, null); + } + } + _requests = keep; + _instance = null; + while(_requests.Count > 0) + { + System.Threading.Monitor.Wait(this); + } + } + } + + public void remove(RetryTask task) + { + lock(this) + { + if(_requests.Remove(task)) + { + if(_instance == null && _requests.Count == 0) + { + // If we are destroying the queue, destroy is probably waiting on the queue to be empty. + System.Threading.Monitor.Pulse(this); + } + } + } + } + + public bool cancel(RetryTask task) + { + lock(this) + { + if(_requests.Remove(task)) + { + if(_instance == null && _requests.Count == 0) + { + // If we are destroying the queue, destroy is probably waiting on the queue to be empty. + System.Threading.Monitor.Pulse(this); + } + return _instance.timer().cancel(task); + } + return false; + } + } + + private Instance _instance; + private Dictionary<RetryTask, object> _requests = new Dictionary<RetryTask, object>(); + } +} diff --git a/csharp/src/Ice/RouterInfo.cs b/csharp/src/Ice/RouterInfo.cs new file mode 100644 index 00000000000..bac2c3e2e9b --- /dev/null +++ b/csharp/src/Ice/RouterInfo.cs @@ -0,0 +1,369 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + + public sealed class RouterInfo + { + public interface GetClientEndpointsCallback + { + void setEndpoints(EndpointI[] endpoints); + void setException(Ice.LocalException ex); + } + + public interface AddProxyCallback + { + void addedProxy(); + void setException(Ice.LocalException ex); + } + + internal RouterInfo(Ice.RouterPrx router) + { + _router = router; + + Debug.Assert(_router != null); + } + + public void destroy() + { + lock(this) + { + _clientEndpoints = new EndpointI[0]; + _serverEndpoints = new EndpointI[0]; + _adapter = null; + _identities.Clear(); + } + } + + public override bool Equals(System.Object obj) + { + if(object.ReferenceEquals(this, obj)) + { + return true; + } + + RouterInfo rhs = obj as RouterInfo; + return rhs == null ? false : _router.Equals(rhs._router); + } + + public override int GetHashCode() + { + return _router.GetHashCode(); + } + + public Ice.RouterPrx getRouter() + { + // + // No mutex lock necessary, _router is immutable. + // + return _router; + } + + public EndpointI[] getClientEndpoints() + { + lock(this) + { + if(_clientEndpoints != null) // Lazy initialization. + { + return _clientEndpoints; + } + } + + return setClientEndpoints(_router.getClientProxy()); + } + + public void getClientEndpoints(GetClientEndpointsCallback callback) + { + EndpointI[] clientEndpoints = null; + lock(this) + { + clientEndpoints = _clientEndpoints; + } + + if(clientEndpoints != null) // Lazy initialization. + { + callback.setEndpoints(clientEndpoints); + return; + } + + _router.begin_getClientProxy().whenCompleted( + (Ice.ObjectPrx proxy) => + { + callback.setEndpoints(setClientEndpoints(proxy)); + }, + (Ice.Exception ex) => + { + Debug.Assert(ex is Ice.LocalException); + callback.setException((Ice.LocalException)ex); + }); + } + + public EndpointI[] getServerEndpoints() + { + lock(this) + { + if(_serverEndpoints != null) // Lazy initialization. + { + return _serverEndpoints; + } + + } + + return setServerEndpoints(_router.getServerProxy()); + } + + public void addProxy(Ice.ObjectPrx proxy) + { + Debug.Assert(proxy != null); + lock(this) + { + if(_identities.Contains(proxy.ice_getIdentity())) + { + // + // Only add the proxy to the router if it's not already in our local map. + // + return; + } + } + + addAndEvictProxies(proxy, _router.addProxies(new Ice.ObjectPrx[] { proxy })); + } + + public bool addProxy(Ice.ObjectPrx proxy, AddProxyCallback callback) + { + Debug.Assert(proxy != null); + lock(this) + { + if(_identities.Contains(proxy.ice_getIdentity())) + { + // + // Only add the proxy to the router if it's not already in our local map. + // + return true; + } + } + _router.begin_addProxies(new Ice.ObjectPrx[] { proxy }).whenCompleted( + (Ice.ObjectPrx[] evictedProxies) => + { + addAndEvictProxies(proxy, evictedProxies); + callback.addedProxy(); + }, + (Ice.Exception ex) => + { + Debug.Assert(ex is Ice.LocalException); + callback.setException((Ice.LocalException)ex); + }); + + return false; + } + + public void setAdapter(Ice.ObjectAdapter adapter) + { + lock(this) + { + _adapter = adapter; + } + } + + public Ice.ObjectAdapter getAdapter() + { + lock(this) + { + return _adapter; + } + } + + public void clearCache(Reference @ref) + { + lock(this) + { + _identities.Remove(@ref.getIdentity()); + } + } + + private EndpointI[] setClientEndpoints(Ice.ObjectPrx clientProxy) + { + lock(this) + { + if(_clientEndpoints == null) + { + if(clientProxy == null) + { + // + // If getClientProxy() return nil, use router endpoints. + // + _clientEndpoints = ((Ice.ObjectPrxHelperBase)_router).reference__().getEndpoints(); + } + else + { + clientProxy = clientProxy.ice_router(null); // The client proxy cannot be routed. + + // + // In order to avoid creating a new connection to the + // router, we must use the same timeout as the already + // existing connection. + // + if(_router.ice_getConnection() != null) + { + clientProxy = clientProxy.ice_timeout(_router.ice_getConnection().timeout()); + } + + _clientEndpoints = ((Ice.ObjectPrxHelperBase)clientProxy).reference__().getEndpoints(); + } + } + return _clientEndpoints; + } + } + + private EndpointI[] setServerEndpoints(Ice.ObjectPrx serverProxy) + { + lock(this) + { + if(serverProxy == null) + { + throw new Ice.NoEndpointException(); + } + + serverProxy = serverProxy.ice_router(null); // The server proxy cannot be routed. + _serverEndpoints = ((Ice.ObjectPrxHelperBase)serverProxy).reference__().getEndpoints(); + return _serverEndpoints; + } + } + + private void addAndEvictProxies(Ice.ObjectPrx proxy, Ice.ObjectPrx[] evictedProxies) + { + lock(this) + { + // + // Check if the proxy hasn't already been evicted by a + // concurrent addProxies call. If it's the case, don't + // add it to our local map. + // + int index = _evictedIdentities.IndexOf(proxy.ice_getIdentity()); + if(index >= 0) + { + _evictedIdentities.RemoveAt(index); + } + else + { + // + // If we successfully added the proxy to the router, + // we add it to our local map. + // + _identities.Add(proxy.ice_getIdentity()); + } + + // + // We also must remove whatever proxies the router evicted. + // + for(int i = 0; i < evictedProxies.Length; ++i) + { + if(!_identities.Remove(evictedProxies[i].ice_getIdentity())) + { + // + // It's possible for the proxy to not have been + // added yet in the local map if two threads + // concurrently call addProxies. + // + _evictedIdentities.Add(evictedProxies[i].ice_getIdentity()); + } + } + } + } + + private readonly Ice.RouterPrx _router; + private EndpointI[] _clientEndpoints; + private EndpointI[] _serverEndpoints; + private Ice.ObjectAdapter _adapter; + private HashSet<Ice.Identity> _identities = new HashSet<Ice.Identity>(); + private List<Ice.Identity> _evictedIdentities = new List<Ice.Identity>(); + } + + public sealed class RouterManager + { + internal RouterManager() + { + _table = new Dictionary<Ice.RouterPrx, RouterInfo>(); + } + + internal void destroy() + { + lock(this) + { + foreach(RouterInfo i in _table.Values) + { + i.destroy(); + } + _table.Clear(); + } + } + + // + // Returns router info for a given router. Automatically creates + // the router info if it doesn't exist yet. + // + public RouterInfo get(Ice.RouterPrx rtr) + { + if(rtr == null) + { + return null; + } + + // + // The router cannot be routed. + // + Ice.RouterPrx router = Ice.RouterPrxHelper.uncheckedCast(rtr.ice_router(null)); + + lock(this) + { + RouterInfo info = null; + if(!_table.TryGetValue(router, out info)) + { + info = new RouterInfo(router); + _table.Add(router, info); + } + + return info; + } + } + + // + // Returns router info for a given router. Automatically creates + // the router info if it doesn't exist yet. + // + public RouterInfo erase(Ice.RouterPrx rtr) + { + RouterInfo info = null; + if(rtr != null) + { + // + // The router cannot be routed. + // + Ice.RouterPrx router = Ice.RouterPrxHelper.uncheckedCast(rtr.ice_router(null)); + + lock(this) + { + if(_table.TryGetValue(router, out info)) + { + _table.Remove(router); + } + } + } + return info; + } + + private Dictionary<Ice.RouterPrx, RouterInfo> _table; + } + +} diff --git a/csharp/src/Ice/ServantManager.cs b/csharp/src/Ice/ServantManager.cs new file mode 100644 index 00000000000..c96d23f9c88 --- /dev/null +++ b/csharp/src/Ice/ServantManager.cs @@ -0,0 +1,375 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + +using System.Collections.Generic; +using System.Diagnostics; + +public sealed class ServantManager +{ + public void addServant(Ice.Object servant, Ice.Identity ident, string facet) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + if(facet == null) + { + facet = ""; + } + + Dictionary<string, Ice.Object> m; + _servantMapMap.TryGetValue(ident, out m); + if(m == null) + { + _servantMapMap[ident] = (m = new Dictionary<string, Ice.Object>()); + } + else + { + if(m.ContainsKey(facet)) + { + Ice.AlreadyRegisteredException ex = new Ice.AlreadyRegisteredException(); + ex.id = instance_.identityToString(ident); + ex.kindOfObject = "servant"; + if(facet.Length > 0) + { + ex.id += " -f " + IceUtilInternal.StringUtil.escapeString(facet, ""); + } + throw ex; + } + } + + m[facet] = servant; + } + } + + public void addDefaultServant(Ice.Object servant, string category) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + Ice.Object obj = null; + _defaultServantMap.TryGetValue(category, out obj); + if(obj != null) + { + Ice.AlreadyRegisteredException ex = new Ice.AlreadyRegisteredException(); + ex.kindOfObject = "default servant"; + ex.id = category; + throw ex; + } + + _defaultServantMap[category] = servant; + } + } + + public Ice.Object removeServant(Ice.Identity ident, string facet) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + if(facet == null) + { + facet = ""; + } + + Dictionary<string, Ice.Object> m; + _servantMapMap.TryGetValue(ident, out m); + Ice.Object obj = null; + if(m == null || !m.ContainsKey(facet)) + { + Ice.NotRegisteredException ex = new Ice.NotRegisteredException(); + ex.id = Ice.Util.identityToString(ident); + ex.kindOfObject = "servant"; + if(facet.Length > 0) + { + ex.id += " -f " + IceUtilInternal.StringUtil.escapeString(facet, ""); + } + throw ex; + } + obj = m[facet]; + m.Remove(facet); + + if(m.Count == 0) + { + _servantMapMap.Remove(ident); + } + return obj; + } + } + + public Ice.Object removeDefaultServant(string category) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + Ice.Object obj = null; + _defaultServantMap.TryGetValue(category, out obj); + if(obj == null) + { + Ice.NotRegisteredException ex = new Ice.NotRegisteredException(); + ex.kindOfObject = "default servant"; + ex.id = category; + throw ex; + } + + _defaultServantMap.Remove(category); + return obj; + } + } + + public Dictionary<string, Ice.Object> removeAllFacets(Ice.Identity ident) + { + lock(this) + { + Debug.Assert(instance_ != null); + + Dictionary<string, Ice.Object> m; + _servantMapMap.TryGetValue(ident, out m); + if(m == null) + { + Ice.NotRegisteredException ex = new Ice.NotRegisteredException(); + ex.id = Ice.Util.identityToString(ident); + ex.kindOfObject = "servant"; + throw ex; + } + _servantMapMap.Remove(ident); + + return m; + } + } + + public Ice.Object findServant(Ice.Identity ident, string facet) + { + lock(this) + { + // + // This assert is not valid if the adapter dispatch incoming + // requests from bidir connections. This method might be called if + // requests are received over the bidir connection after the + // adapter was deactivated. + // + //Debug.Assert(instance_ != null); // Must not be called after destruction. + + if(facet == null) + { + facet = ""; + } + + Dictionary<string, Ice.Object> m; + _servantMapMap.TryGetValue(ident, out m); + Ice.Object obj = null; + if(m == null) + { + _defaultServantMap.TryGetValue(ident.category, out obj); + if(obj == null) + { + _defaultServantMap.TryGetValue("", out obj); + } + } + else + { + m.TryGetValue(facet, out obj); + } + + return obj; + } + } + + public Ice.Object findDefaultServant(string category) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + Ice.Object obj = null; + _defaultServantMap.TryGetValue(category, out obj); + return obj; + } + } + + public Dictionary<string, Ice.Object> findAllFacets(Ice.Identity ident) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + Dictionary<string, Ice.Object> m = _servantMapMap[ident]; + if(m != null) + { + return new Dictionary<string, Ice.Object>(m); + } + + return new Dictionary<string, Ice.Object>(); + } + } + + public bool hasServant(Ice.Identity ident) + { + lock(this) + { + // + // This assert is not valid if the adapter dispatch incoming + // requests from bidir connections. This method might be called if + // requests are received over the bidir connection after the + // adapter was deactivated. + // + // + //Debug.Assert(instance_ != null); // Must not be called after destruction. + + Dictionary<string, Ice.Object> m; + _servantMapMap.TryGetValue(ident, out m); + if(m == null) + { + return false; + } + else + { + Debug.Assert(m.Count != 0); + return true; + } + } + } + + public void addServantLocator(Ice.ServantLocator locator, string category) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + Ice.ServantLocator l; + _locatorMap.TryGetValue(category, out l); + if(l != null) + { + Ice.AlreadyRegisteredException ex = new Ice.AlreadyRegisteredException(); + ex.id = IceUtilInternal.StringUtil.escapeString(category, ""); + ex.kindOfObject = "servant locator"; + throw ex; + } + + _locatorMap[category] = locator; + } + } + + public Ice.ServantLocator removeServantLocator(string category) + { + lock(this) + { + Debug.Assert(instance_ != null); // Must not be called after destruction. + + Ice.ServantLocator l; + _locatorMap.TryGetValue(category, out l); + if(l == null) + { + Ice.NotRegisteredException ex = new Ice.NotRegisteredException(); + ex.id = IceUtilInternal.StringUtil.escapeString(category, ""); + ex.kindOfObject = "servant locator"; + throw ex; + } + _locatorMap.Remove(category); + return l; + } + } + + public Ice.ServantLocator findServantLocator(string category) + { + lock(this) + { + // + // This assert is not valid if the adapter dispatch incoming + // requests from bidir connections. This method might be called if + // requests are received over the bidir connection after the + // adapter was deactivated. + // + // + //Debug.Assert(instance_ != null); // Must not be called after destruction. + + Ice.ServantLocator result; + _locatorMap.TryGetValue(category, out result); + return result; + } + } + + // + // Only for use by Ice.ObjectAdapterI. + // + public ServantManager(Instance instance, string adapterName) + { + instance_ = instance; + _adapterName = adapterName; + } + + /* + ~ServantManager() + { + // + // Don't check whether destroy() has been called. It might have + // not been called if the associated object adapter was not + // properly deactivated. + // + //lock(this) + //{ + //IceUtil.Assert.FinalizerAssert(instance_ == null); + //} + } + */ + + // + // Only for use by Ice.ObjectAdapterI. + // + public void destroy() + { + Dictionary<string, Ice.ServantLocator> locatorMap = null; + Ice.Logger logger = null; + lock(this) + { + // + // If the ServantManager has already been destroyed, we're done. + // + if(instance_ == null) + { + return; + } + + logger = instance_.initializationData().logger; + _servantMapMap.Clear(); + + locatorMap = new Dictionary<string, Ice.ServantLocator>(_locatorMap); + _locatorMap.Clear(); + instance_ = null; + } + + foreach(KeyValuePair<string, Ice.ServantLocator> p in locatorMap) + { + Ice.ServantLocator locator = p.Value; + try + { + locator.deactivate(p.Key); + } + catch(System.Exception ex) + { + string s = "exception during locator deactivation:\n" + "object adapter: `" + + _adapterName + "'\n" + "locator category: `" + p.Key + "'\n" + ex; + logger.error(s); + } + } + } + + private Instance instance_; + private readonly string _adapterName; + private Dictionary <Ice.Identity, Dictionary<string, Ice.Object>> _servantMapMap + = new Dictionary<Ice.Identity, Dictionary<string, Ice.Object>>(); + private Dictionary <string, Ice.Object> _defaultServantMap = new Dictionary<string, Ice.Object>(); + private Dictionary<string, Ice.ServantLocator> _locatorMap = new Dictionary<string, Ice.ServantLocator>(); +} + +} diff --git a/csharp/src/Ice/SliceChecksums.cs b/csharp/src/Ice/SliceChecksums.cs new file mode 100644 index 00000000000..9c600402643 --- /dev/null +++ b/csharp/src/Ice/SliceChecksums.cs @@ -0,0 +1,39 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + + using System; + using System.Collections; + using System.Collections.Generic; + using System.Reflection; + + public sealed class SliceChecksums + { + public static Dictionary<string, string> checksums = new Dictionary<string, string>(); + +#if !COMPACT && !SILVERLIGHT + static SliceChecksums() + { + Type[] types = IceInternal.AssemblyUtil.findTypesWithPrefix("IceInternal.SliceChecksums"); + foreach(Type t in types) + { + FieldInfo f = t.GetField("map", BindingFlags.Public | BindingFlags.Static); + Hashtable map = (Hashtable)f.GetValue(null); + foreach(DictionaryEntry entry in map) + { + checksums.Add((string)entry.Key, (string)entry.Value); + } + } + } +#endif + } + +} diff --git a/csharp/src/Ice/SlicedData.cs b/csharp/src/Ice/SlicedData.cs new file mode 100644 index 00000000000..13ba4de17e2 --- /dev/null +++ b/csharp/src/Ice/SlicedData.cs @@ -0,0 +1,63 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// SlicedData holds the slices of unknown class or exception types. + /// </summary> + public class SlicedData + { + public SlicedData(SliceInfo[] slices) + { + this.slices = slices; + } + + /** + * The details of each slice, in order of most-derived to least-derived. + **/ + public SliceInfo[] slices; + } + + /// <summary> + /// SliceInfo encapsulates the details of a slice for an unknown class or exception type. + /// </summary> + public class SliceInfo + { + /// <summary> + /// The Slice type ID for this slice. + /// </summary> + public string typeId; + + /// <summary> + /// The Slice compact type ID for this slice. + /// </summary> + public int compactId; + + /// <summary> + /// The encoded bytes for this slice, including the leading size integer. + /// </summary> + public byte[] bytes; + + /// <summary> + /// The Ice objects referenced by this slice. + /// </summary> + public Ice.Object[] objects; + + /// <summary> + /// Whether or not the slice contains optional members. + /// </summary> + public bool hasOptionalMembers; + + /// <summary> + /// Whether or not this is the last slice. + /// </summary> + public bool isLastSlice; + } +} diff --git a/csharp/src/Ice/SocketOperation.cs b/csharp/src/Ice/SocketOperation.cs new file mode 100644 index 00000000000..51c71a6b351 --- /dev/null +++ b/csharp/src/Ice/SocketOperation.cs @@ -0,0 +1,19 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + public sealed class SocketOperation + { + public const int None = 0; + public const int Read = 1; + public const int Write = 2; + public const int Connect = 2; + } +} diff --git a/csharp/src/Ice/Stream.cs b/csharp/src/Ice/Stream.cs new file mode 100644 index 00000000000..0802c0edcfe --- /dev/null +++ b/csharp/src/Ice/Stream.cs @@ -0,0 +1,705 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Diagnostics; + +namespace Ice +{ + /// <summary> + /// Callback class to inform an application when a Slice class has been unmarshaled + /// from an input stream. + /// </summary> + public interface ReadObjectCallback + { + /// <summary> + /// The Ice run time calls this method when it has fully unmarshaled the state + /// of a Slice class. + /// </summary> + /// <param name="obj">The unmarshaled Slice class.</param> + void invoke(Ice.Object obj); + } + + /// <summary> + /// Interface for input streams used to extract Slice types from a sequence of bytes. + /// </summary> + public interface InputStream + { + /// <summary> + /// Returns the communicator for this input stream. + /// </summary> + /// <returns>The communicator.</returns> + Communicator communicator(); + + /// <summary> + /// Determines the behavior of the stream when extracting Slice objects. + /// A Slice object is "sliced" when a factory cannot be found for a Slice type ID. + /// </summary> + /// <param name="slice">If true (the default), slicing is enabled; if false, + /// slicing is disabled. If slicing is disabled and the stream encounters a Slice type ID + /// during decoding for which no object factory is installed, it raises NoObjectFactoryException.</param> + void sliceObjects(bool slice); + + /// <summary> + /// Extracts a boolean value from the stream. + /// </summary> + /// <returns>The extracted boolean.</returns> + bool readBool(); + + /// <summary> + /// Extracts a sequence of boolean values from the stream. + /// </summary> + /// <returns>The extracted boolean sequence.</returns> + bool[] readBoolSeq(); + + /// <summary> + /// Extracts a byte value from the stream. + /// </summary> + /// <returns>The extracted byte.</returns> + byte readByte(); + + /// <summary> + /// Extracts a sequence of byte values from the stream. + /// </summary> + /// <returns>The extracted byte sequence.</returns> + byte[] readByteSeq(); + + /// <summary> + /// Extracts a serializable .NET object from the stream. + /// </summary> + /// <returns>The deserialized .NET object.</returns> + object readSerializable(); + + /// <summary> + /// Extracts a short value from the stream. + /// </summary> + /// <returns>The extracted short value.</returns> + short readShort(); + + /// <summary> + /// Extracts a sequence of short values from the stream. + /// </summary> + /// <returns>The extracted short sequence.</returns> + short[] readShortSeq(); + + /// <summary> + /// Extracts an integer value from the stream. + /// </summary> + /// <returns>The extracted integer value.</returns> + int readInt(); + + /// <summary> + /// Extracts a sequence of integer values from the stream. + /// </summary> + /// <returns>The extracted integer sequence.</returns> + int[] readIntSeq(); + + /// <summary> + /// Extracts a long value from the stream. + /// </summary> + /// <returns>The extracted long value.</returns> + long readLong(); + + /// <summary> + /// Extracts a sequence of long values from the stream. + /// </summary> + /// <returns>The extracted long sequence.</returns> + long[] readLongSeq(); + + /// <summary> + /// Extracts a float value from the stream. + /// </summary> + /// <returns>The extracted float value.</returns> + float readFloat(); + + /// <summary> + /// Extracts a sequence of float values from the stream. + /// </summary> + /// <returns>The extracted float sequence.</returns> + float[] readFloatSeq(); + + /// <summary> + /// Extracts a double value from the stream. + /// </summary> + /// <returns>The extracted double value.</returns> + double readDouble(); + + /// <summary> + /// Extracts a sequence of double values from the stream. + /// </summary> + /// <returns>The extracted double sequence.</returns> + double[] readDoubleSeq(); + + /// <summary> + /// Extracts a string from the stream. + /// </summary> + /// <returns>The extracted double value.</returns> + string readString(); + + /// <summary> + /// Extracts a sequence of strings from the stream. + /// </summary> + /// <returns>The extracted string sequence.</returns> + string[] readStringSeq(); + + /// <summary> + /// Extracts a size from the stream. + /// </summary> + /// <returns>The extracted size.</returns> + int readSize(); + + /// <summary> + /// Extracts and check a sequence size from the stream. The check ensures not too much memory will + /// be pre-allocated for the sequence. + /// </summary> + /// <param name="minSize">The minimum size of an element of the sequence.</param> + /// <returns>The extracted size.</returns> + int readAndCheckSeqSize(int minSize); + + /// <summary> + /// Extracts a proxy from the stream. + /// </summary> + /// <returns>The extracted proxy.</returns> + ObjectPrx readProxy(); + + /// <summary> + /// Extracts the index of a Slice class from the stream. + /// </summary> + /// <param name="cb">The callback to notify the application when the extracted instance is available. + /// The Ice run time extracts Slice classes in stages. The Ice run time calls ReadObjectCallback.invoke + /// when the corresponding instance has been fully unmarshaled.</param> + void readObject(ReadObjectCallback cb); + + /// <summary> + /// Read an enumerated value. + /// </summary> + /// + /// <param name="maxValue">The maximum enumerator value in the definition.</param> + /// <returns>The enumerator.</returns> + int readEnum(int maxValue); + + /// <summary> + /// Extracts a user exception from the stream and throws it. + /// </summary> + void throwException(); + + /// <summary> + /// Extracts a user exception from the stream and throws it. + /// Extracts a user exception from the stream and throws it, using the supplied + /// factory to instantiate a UserExceptionReader. + /// </summary> + /// <param name="factory">A factory that creates UserExceptionReader instances.</param> + void throwException(UserExceptionReaderFactory factory); + + /// <summary> + /// Marks the start of an Ice object. + /// </summary> + void startObject(); + + /// <summary> + /// Marks the end of an Ice object. + /// </summary> + /// <param name="preserve">True if unknown slices should be preserved, false otherwise.</param> + /// <returns>A SlicedData object containing the preserved slices for unknown types.</returns> + SlicedData endObject(bool preserve); + + /// <summary> + /// Marks the start of a user exception. + /// </summary> + void startException(); + + /// <summary> + /// Marks the end of a user exception. + /// </summary> + /// <param name="preserve">True if unknown slices should be preserved, false otherwise.</param> + /// <returns>A SlicedData object containing the preserved slices for unknown types.</returns> + SlicedData endException(bool preserve); + + /// <summary> + /// Reads the start of an object or exception slice. + /// </summary> + /// <returns>The Slice type ID for this slice.</returns> + string startSlice(); + + /// <summary> + /// Indicates that the end of an object or exception slice has been reached. + /// </summary> + void endSlice(); + + /// <summary> + /// Skips over an object or exception slice. + /// </summary> + void skipSlice(); + + /// <summary> + /// Reads the start of an encapsulation. + /// </summary> + /// <returns>The encapsulation encoding version.</returns> + EncodingVersion startEncapsulation(); + + /// <summary> + /// Indicates that the end of an encapsulation has been reached. + /// </summary> + void endEncapsulation(); + + /// <summary> + /// Skips over an encapsulation. + /// </summary> + /// <returns>The encapsulation encoding version.</returns> + EncodingVersion skipEncapsulation(); + + /// <summary> + /// Determines the current encoding version. + /// </summary> + /// <returns>The encoding version.</returns> + EncodingVersion getEncoding(); + + /// <summary> + /// Indicates that unmarshaling is complete, except for any Slice objects. The application must + /// call this method only if the stream actually contains Slice objects. Calling readPendingObjects + /// triggers the calls to ReadObjectCallback.invoke that inform the application that unmarshaling + /// of a Slice object is complete. + /// </summary> + void readPendingObjects(); + + /// <summary> + /// Resets the read position of the stream to the beginning. + /// </summary> + void rewind(); + + /// <summary> + /// Skips ahead in the stream. + /// </summary> + /// <param name="sz">The number of bytes to skip.</param> + void skip(int sz); + + /// <summary> + /// Skips over a size value. + /// </summary> + void skipSize(); + + /// <summary> + /// Determine if an optional value is available for reading. + /// </summary> + /// <param name="tag">The tag associated with the value.</param> + /// <param name="format">The optional format for the value.</param> + /// <returns>True if the value is present, false otherwise.</returns> + bool readOptional(int tag, OptionalFormat format); + + /// <summary> + /// Determine the current position in the stream. + /// </summary> + /// <returns>The current position.</returns> + int pos(); + + /// <summary> + /// Destroys the stream and its associated resources. The application must call destroy prior + /// to releasing the last reference to a stream; failure to do so may result in resource leaks. + /// </summary> + void destroy(); + } + + /// <summary> + /// Interface for output streams used to write Slice types to a sequence + /// of bytes. + /// </summary> + public interface OutputStream + { + /// <summary> + /// Returns the communicator for this output stream. + /// </summary> + Communicator communicator(); + + /// <summary> + /// Writes a boolean to the stream. + /// </summary> + /// <param name="v">The boolean to write to the stream.</param> + void writeBool(bool v); + + /// <summary> + /// Writes a sequence of booleans to the stream. + /// </summary> + /// <param name="v">The sequence of booleans to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeBoolSeq(bool[] v); + + /// <summary> + /// Writes a byte to the stream. + /// </summary> + /// <param name="v">The byte to write to the stream.</param> + void writeByte(byte v); + + /// <summary> + /// Writes a sequence of bytes to the stream. + /// </summary> + /// <param name="v">The sequence of bytes to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeByteSeq(byte[] v); + + /// <summary> + /// Writes a serializable .NET object to the stream. + /// </summary> + /// <param name="v">The serializable object to write.</param> + void writeSerializable(object v); + + /// <summary> + /// Writes a short to the stream. + /// </summary> + /// <param name="v">The short to write to the stream.</param> + void writeShort(short v); + + /// <summary> + /// Writes a sequence of shorts to the stream. + /// </summary> + /// <param name="v">The sequence of shorts to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeShortSeq(short[] v); + + /// <summary> + /// Writes an integer to the stream. + /// </summary> + /// <param name="v">The integer to write to the stream.</param> + void writeInt(int v); + + /// <summary> + /// Writes a sequence of integers to the stream. + /// </summary> + /// <param name="v">The sequence of integers to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeIntSeq(int[] v); + + /// <summary> + /// Writes a long to the stream. + /// </summary> + /// <param name="v">The long to write to the stream.</param> + void writeLong(long v); + + /// <summary> + /// Writes a sequence of longs to the stream. + /// </summary> + /// <param name="v">The sequence of longs to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeLongSeq(long[] v); + + /// <summary> + /// Writes a float to the stream. + /// </summary> + /// <param name="v">The float to write to the stream.</param> + void writeFloat(float v); + + /// <summary> + /// Writes a sequence of floats to the stream. + /// </summary> + /// <param name="v">The sequence of floats to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeFloatSeq(float[] v); + + /// <summary> + /// Writes a double to the stream. + /// </summary> + /// <param name="v">The double to write to the stream.</param> + void writeDouble(double v); + + /// <summary> + /// Writes a sequence of doubles to the stream. + /// </summary> + /// <param name="v">The sequence of doubles to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeDoubleSeq(double[] v); + + /// <summary> + /// Writes a string to the stream. + /// </summary> + /// <param name="v">The string to write to the stream. + /// Passing null causes an empty string to be written to the stream.</param> + void writeString(string v); + + /// <summary> + /// Writes a sequence of strings to the stream. + /// </summary> + /// <param name="v">The sequence of strings to write. + /// Passing null causes an empty sequence to be written to the stream.</param> + void writeStringSeq(string[] v); + + /// <summary> + /// Writes a size to the stream. + /// </summary> + /// <param name="sz">The size to write.</param> + void writeSize(int sz); + + /// <summary> + /// Writes a proxy to the stream. + /// </summary> + /// <param name="v">The proxy to write.</param> + void writeProxy(ObjectPrx v); + + /// <summary> + /// Writes a Slice class to the stream. + /// </summary> + /// <param name="v">The class to write. This method writes the index of a Slice class; the state of the + /// class is written once writePendingObjects is called.</param> + void writeObject(Ice.Object v); + + /// <summary> + /// Write an enumerated value. + /// </summary> + /// <param name="v">The enumerator.</param> + /// <param name="limit">The number of enumerators in the definition.</param> + void writeEnum(int v, int limit); + + /// <summary> + /// Writes a user exception to the stream. + /// </summary> + /// <param name="ex">The user exception to write.</param> + void writeException(UserException ex); + + /// <summary> + /// Marks the start of an Ice object. + /// </summary> + /// <param name="slicedData">Preserved slices for this object, or null.</param> + void startObject(SlicedData slicedData); + + /// <summary> + /// Marks the end of an Ice object. + /// </summary> + void endObject(); + + /// <summary> + /// Marks the start of a user exception. + /// </summary> + /// <param name="slicedData">Preserved slices for this object, or null.</param> + void startException(SlicedData slicedData); + + /// <summary> + /// Marks the end of a user exception. + /// </summary> + void endException(); + + /// <summary> + /// Marks the start of a new slice for an Ice object or user exception. + /// </summary> + /// <param name="typeId">The Slice type ID corresponding to this slice.</param> + /// <param name="compactId">The Slice compact type ID corresponding to this slice.</param> + /// <param name="last">True if this is the last slice, false otherwise.</param> + void startSlice(string typeId, int compactId, bool last); + + /// <summary> + /// Marks the end of a slice for an Ice object or user exception. + /// </summary> + void endSlice(); + + /// <summary> + /// Writes the start of an encapsulation to the stream. + /// </summary> + /// <param name="encoding">The encoding version of the encapsulation.</param> + /// <param name="format">The format to use for encoding objects and user exceptions.</param> + void startEncapsulation(EncodingVersion encoding, FormatType format); + + /// <summary> + /// Writes the start of an encapsulation to the stream. + /// </summary> + void startEncapsulation(); + + /// <summary> + /// Ends the previous encapsulation. + /// </summary> + void endEncapsulation(); + + /// <summary> + /// Determines the current encoding version. + /// </summary> + /// <returns>The encoding version.</returns> + EncodingVersion getEncoding(); + + /// <summary> + /// Writes the state of Slice classes whose index was previously + /// written with writeObject to the stream. + /// </summary> + void writePendingObjects(); + + /// <summary> + /// Write the header information for an optional value. + /// </summary> + /// <param name="tag">The numeric tag associated with the value.</param> + /// <param name="format">The optional format of the value.</param> + /// <returns>True if the optional should be written, false otherwise.</returns> + bool writeOptional(int tag, OptionalFormat format); + + /// <summary> + /// Determines the current position in the stream. + /// </summary> + /// <returns>The current position.</returns> + int pos(); + + /// <summary> + /// Inserts a fixed 32-bit size value into the stream at the given position. + /// </summary> + /// <param name="sz">The 32-bit size value.</param> + /// <param name="pos">The position at which to write the value.</param> + void rewrite(int sz, int pos); + + /// <summary> + /// Returns the current position and allocates four bytes for a fixed-length (32-bit) + /// size value. + /// </summary> + /// <returns>The current position.</returns> + int startSize(); + + /// <summary> + /// Computes the amount of data written since the previous call to startSize and + /// writes that value at the saved position. + /// </summary> + /// <param name="pos">The saved position at which to write the size.</param> + void endSize(int pos); + + /// <summary> + /// Indicates that the marshaling of a request or reply is finished. + /// </summary> + /// <returns>The byte sequence containing the encoded request or reply.</returns> + byte[] finished(); + + /// <summary> + /// Resets this output stream. This method allows the stream to be reused, to avoid creating + /// unnecessary garbage. + /// </summary> + /// + /// <param name="clearBuffer">If true, the stream's internal buffer becomes eligible for + /// garbage collection; if false, the stream's internal buffer is retained, to avoid + /// creating unnecessary garbage. If retained, the internal buffer may be resized to a smaller + /// capacity. Either way, reset resets the stream's writing position to zero.</param> + void reset(bool clearBuffer); + + /// <summary> + /// Destroys the stream and its associated resources. The application must call destroy prior + /// to releasing the last reference to a stream; failure to do so may result in resource leaks. + /// </summary> + void destroy(); + } + + /// <summary> + /// Base class for extracting objects from an input stream. + /// </summary> + public abstract class ObjectReader : ObjectImpl + { + /// <summary> + /// Read the object's data members. + /// </summary> + /// <param name="inStream">The input stream to read from.</param> + public abstract void read(InputStream inStream); + + public override void write__(IceInternal.BasicStream os) + { + Debug.Assert(false); + } + + public override void read__(IceInternal.BasicStream istr) + { + InputStream stream = (InputStream)istr.closure(); + read(stream); + } + } + + /// <summary> + /// Base class for writing objects to an output stream. + /// </summary> + public abstract class ObjectWriter : ObjectImpl + { + /// <summary> + /// Writes the state of this Slice class to an output stream. + /// </summary> + /// <param name="outStream">The stream to write to.</param> + public abstract void write(OutputStream outStream); + + public override void write__(IceInternal.BasicStream os) + { + OutputStream stream = (OutputStream)os.closure(); + write(stream); + } + + public override void read__(IceInternal.BasicStream istr) + { + Debug.Assert(false); + } + } + + public abstract class UserExceptionReader : UserException + { + protected UserExceptionReader(Communicator communicator) + { + communicator_ = communicator; + } + + public abstract void read(Ice.InputStream istr); + + public override void write__(IceInternal.BasicStream ostr) + { + Debug.Assert(false); + } + + public override void read__(IceInternal.BasicStream istr) + { + InputStream stream = (InputStream)istr.closure(); + Debug.Assert(stream != null); + read(stream); + } + + public override void write__(Ice.OutputStream ostr) + { + Debug.Assert(false); + } + + public override void read__(Ice.InputStream istr) + { + read(istr); + } + + protected Communicator communicator_; + } + + public interface UserExceptionReaderFactory + { + void createAndThrow(string typeId); + } + + public abstract class UserExceptionWriter : UserException + { + public UserExceptionWriter(Communicator communicator) + { + communicator_ = communicator; + } + + public abstract void write(OutputStream os); + + public override void write__(IceInternal.BasicStream os) + { + OutputStream stream = (OutputStream)os.closure(); + if(stream == null) + { + stream = new OutputStreamI(communicator_, os); + } + write(stream); + } + + public override void read__(IceInternal.BasicStream istr) + { + Debug.Assert(false); + } + + public override void write__(Ice.OutputStream ostr) + { + write(ostr); + } + + public override void read__(Ice.InputStream istr) + { + Debug.Assert(false); + } + + protected Communicator communicator_; + } +} diff --git a/csharp/src/Ice/StreamI.cs b/csharp/src/Ice/StreamI.cs new file mode 100644 index 00000000000..a9f528c8c8b --- /dev/null +++ b/csharp/src/Ice/StreamI.cs @@ -0,0 +1,570 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + public class InputStreamI : InputStream + { + public InputStreamI(Communicator communicator, byte[] data, bool copyData) + { + _communicator = communicator; + IceInternal.Instance instance = IceInternal.Util.getInstance(communicator); + initialize(instance, data, instance.defaultsAndOverrides().defaultEncoding, copyData); + } + + public InputStreamI(Communicator communicator, byte[] data, EncodingVersion v, bool copyData) + { + _communicator = communicator; + initialize(IceInternal.Util.getInstance(communicator), data, v, copyData); + } + + private void initialize(IceInternal.Instance instance, byte[] data, EncodingVersion v, bool copyData) + { + if(copyData) + { + _is = new IceInternal.BasicStream(instance, v); + _is.resize(data.Length, true); + IceInternal.Buffer buf = _is.getBuffer(); + buf.b.position(0); + buf.b.put(data); + buf.b.position(0); + } + else + { + _is = new IceInternal.BasicStream(instance, v, data); + } + _is.closure(this); + } + + public Communicator communicator() + { + return _communicator; + } + + public void sliceObjects(bool slice) + { + _is.sliceObjects(slice); + } + + public bool readBool() + { + return _is.readBool(); + } + + public bool[] readBoolSeq() + { + return _is.readBoolSeq(); + } + + public byte readByte() + { + return _is.readByte(); + } + + public byte[] readByteSeq() + { + return _is.readByteSeq(); + } + + public object readSerializable() + { + return _is.readSerializable(); + } + + public short readShort() + { + return _is.readShort(); + } + + public short[] readShortSeq() + { + return _is.readShortSeq(); + } + + public int readInt() + { + return _is.readInt(); + } + + public int[] readIntSeq() + { + return _is.readIntSeq(); + } + + public long readLong() + { + return _is.readLong(); + } + + public long[] readLongSeq() + { + return _is.readLongSeq(); + } + + public float readFloat() + { + return _is.readFloat(); + } + + public float[] readFloatSeq() + { + return _is.readFloatSeq(); + } + + public double readDouble() + { + return _is.readDouble(); + } + + public double[] readDoubleSeq() + { + return _is.readDoubleSeq(); + } + + public string readString() + { + return _is.readString(); + } + + public string[] readStringSeq() + { + return _is.readStringSeq(); + } + + public int readSize() + { + return _is.readSize(); + } + + public int readAndCheckSeqSize(int minSize) + { + return _is.readAndCheckSeqSize(minSize); + } + + public ObjectPrx readProxy() + { + return _is.readProxy(); + } + + private class Patcher<T> : IceInternal.Patcher + { + public Patcher(ReadObjectCallback cb) : base("unknown") + { + _cb = cb; + } + + public override void patch(Ice.Object v) + { + _cb.invoke(v); + } + + ReadObjectCallback _cb; + } + + public void readObject(ReadObjectCallback cb) + { + _is.readObject(new Patcher<Ice.Object>(cb)); + } + + public int readEnum(int maxValue) + { + return _is.readEnum(maxValue); + } + + public void throwException() + { + _is.throwException(null); + } + + internal class UserExceptionFactoryI : IceInternal.UserExceptionFactory + { + internal UserExceptionFactoryI(UserExceptionReaderFactory factory) + { + _factory = factory; + } + + public void createAndThrow(string id) + { + _factory.createAndThrow(id); + } + + public void destroy() + { + } + + private UserExceptionReaderFactory _factory; + } + + public void throwException(UserExceptionReaderFactory factory) + { + _is.throwException(new UserExceptionFactoryI(factory)); + } + + public void startObject() + { + _is.startReadObject(); + } + + public SlicedData endObject(bool preserve) + { + return _is.endReadObject(preserve); + } + + public void startException() + { + _is.startReadException(); + } + + public SlicedData endException(bool preserve) + { + return _is.endReadException(preserve); + } + + public string startSlice() + { + return _is.startReadSlice(); + } + + public void endSlice() + { + _is.endReadSlice(); + } + + public void skipSlice() + { + _is.skipSlice(); + } + + public EncodingVersion startEncapsulation() + { + return _is.startReadEncaps(); + } + + public void endEncapsulation() + { + _is.endReadEncapsChecked(); + } + + public EncodingVersion skipEncapsulation() + { + return _is.skipEncaps(); + } + + public EncodingVersion getEncoding() + { + return _is.getReadEncoding(); + } + + public void readPendingObjects() + { + _is.readPendingObjects(); + } + + public void rewind() + { + _is.clear(); + _is.getBuffer().b.position(0); + } + + public void skip(int sz) + { + _is.skip(sz); + } + + public void skipSize() + { + _is.skipSize(); + } + + public bool readOptional(int tag, OptionalFormat format) + { + return _is.readOpt(tag, format); + } + + public int pos() + { + return _is.pos(); + } + + public void destroy() + { + if(_is != null) + { + _is = null; + } + } + + private Communicator _communicator; + private IceInternal.BasicStream _is; + } + + public class OutputStreamI : OutputStream + { + public OutputStreamI(Communicator communicator) + { + _communicator = communicator; + + IceInternal.Instance instance = IceInternal.Util.getInstance(communicator); + _os = new IceInternal.BasicStream(instance, instance.defaultsAndOverrides().defaultEncoding); + _os.closure(this); + } + + public OutputStreamI(Communicator communicator, EncodingVersion v) + { + _communicator = communicator; + + IceInternal.Instance instance = IceInternal.Util.getInstance(communicator); + _os = new IceInternal.BasicStream(instance, v); + _os.closure(this); + } + + public OutputStreamI(Communicator communicator, IceInternal.BasicStream os) + { + _communicator = communicator; + _os = os; + _os.closure(this); + } + + public Communicator communicator() + { + return _communicator; + } + + public void writeBool(bool v) + { + _os.writeBool(v); + } + + public void writeBoolSeq(bool[] v) + { + _os.writeBoolSeq(v); + } + + public void writeByte(byte v) + { + _os.writeByte(v); + } + + public void writeByteSeq(byte[] v) + { + _os.writeByteSeq(v); + } + + public void writeSerializable(object v) + { + _os.writeSerializable(v); + } + + public void writeShort(short v) + { + _os.writeShort(v); + } + + public void writeShortSeq(short[] v) + { + _os.writeShortSeq(v); + } + + public void writeInt(int v) + { + _os.writeInt(v); + } + + public void writeIntSeq(int[] v) + { + _os.writeIntSeq(v); + } + + public void writeLong(long v) + { + _os.writeLong(v); + } + + public void writeLongSeq(long[] v) + { + _os.writeLongSeq(v); + } + + public void writeFloat(float v) + { + _os.writeFloat(v); + } + + public void writeFloatSeq(float[] v) + { + _os.writeFloatSeq(v); + } + + public void writeDouble(double v) + { + _os.writeDouble(v); + } + + public void writeDoubleSeq(double[] v) + { + _os.writeDoubleSeq(v); + } + + public void writeString(string v) + { + _os.writeString(v); + } + + public void writeStringSeq(string[] v) + { + _os.writeStringSeq(v); + } + + public void writeSize(int sz) + { + if(sz < 0) + { + throw new MarshalException(); + } + + _os.writeSize(sz); + } + + public void writeProxy(ObjectPrx v) + { + _os.writeProxy(v); + } + + public void writeObject(Ice.Object v) + { + _os.writeObject(v); + } + + public void writeEnum(int v, int maxValue) + { + _os.writeEnum(v, maxValue); + } + + public void writeException(UserException v) + { + _os.writeUserException(v); + } + + public void startObject(SlicedData slicedData) + { + _os.startWriteObject(slicedData); + } + + public void endObject() + { + _os.endWriteObject(); + } + + public void startException(SlicedData slicedData) + { + _os.startWriteException(slicedData); + } + + public void endException() + { + _os.endWriteException(); + } + + public void startSlice(string typeId, int compactId, bool last) + { + _os.startWriteSlice(typeId, compactId, last); + } + + public void endSlice() + { + _os.endWriteSlice(); + } + + public void startEncapsulation(EncodingVersion encoding, FormatType format) + { + _os.startWriteEncaps(encoding, format); + } + + public void startEncapsulation() + { + _os.startWriteEncaps(); + } + + public void endEncapsulation() + { + _os.endWriteEncapsChecked(); + } + + public EncodingVersion getEncoding() + { + return _os.getWriteEncoding(); + } + + public void writePendingObjects() + { + _os.writePendingObjects(); + } + + public bool writeOptional(int tag, OptionalFormat format) + { + return _os.writeOpt(tag, format); + } + + public int pos() + { + return _os.pos(); + } + + public void rewrite(int sz, int pos) + { + _os.rewriteInt(sz, pos); + } + + public int startSize() + { + return _os.startSize(); + } + + public void endSize(int pos) + { + _os.endSize(pos); + } + + public byte[] finished() + { + IceInternal.Buffer buf = _os.prepareWrite(); + byte[] result = new byte[buf.b.limit()]; + buf.b.get(result); + + return result; + } + + public void reset(bool clearBuffer) + { + _os.clear(); + + IceInternal.Buffer buf = _os.getBuffer(); + if(clearBuffer) + { + buf.clear(); + } + else + { + buf.reset(); + } + buf.b.position(0); + } + + public void destroy() + { + if(_os != null) + { + _os = null; + } + } + + private Communicator _communicator; + private IceInternal.BasicStream _os; + } +} diff --git a/csharp/src/Ice/StreamSocket.cs b/csharp/src/Ice/StreamSocket.cs new file mode 100644 index 00000000000..10c1f6488dc --- /dev/null +++ b/csharp/src/Ice/StreamSocket.cs @@ -0,0 +1,644 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// .NET and Silverlight use the new socket asynchronous APIs whereas +// the compact framework and mono still use the old Begin/End APIs. +// +#if !COMPACT && !__MonoCS__ && !UNITY +#define ICE_SOCKET_ASYNC_API +#endif + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Net; + using System.Net.Sockets; + + public sealed class StreamSocket + { + public StreamSocket(ProtocolInstance instance, NetworkProxy proxy, EndPoint addr, EndPoint sourceAddr) + { + _instance = instance; + _proxy = proxy; + _addr = addr; + _sourceAddr = sourceAddr; + _fd = Network.createSocket(false, (_proxy != null ? _proxy.getAddress() : _addr).AddressFamily); + _state = StateNeedConnect; + + init(); + } + + public StreamSocket(ProtocolInstance instance, Socket fd) + { + _instance = instance; + _fd = fd; + _state = StateConnected; + _desc = IceInternal.Network.fdToString(_fd); + init(); + } + +#if !SILVERLIGHT + public void setBlock(bool block) + { + Network.setBlock(_fd, block); + } +#endif + + public int connect(Buffer readBuffer, Buffer writeBuffer, ref bool moreData) + { + if(_state == StateNeedConnect) + { + _state = StateConnectPending; + return SocketOperation.Connect; + } + else if(_state <= StateConnectPending) + { +#if ICE_SOCKET_ASYNC_API + if(_writeEventArgs.SocketError != SocketError.Success) + { + SocketException ex = new SocketException((int)_writeEventArgs.SocketError); + if(Network.connectionRefused(ex)) + { + throw new Ice.ConnectionRefusedException(ex); + } + else + { + throw new Ice.ConnectFailedException(ex); + } + } +#else + Network.doFinishConnectAsync(_fd, _writeResult); + _writeResult = null; +#endif + _desc = Network.fdToString(_fd, _proxy, _addr); + _state = _proxy != null ? StateProxyWrite : StateConnected; + } + + if(_state == StateProxyWrite) + { + _proxy.beginWrite(_addr, writeBuffer); + return SocketOperation.Write; + } + else if(_state == StateProxyRead) + { + _proxy.beginRead(readBuffer); + return SocketOperation.Read; + } + else if(_state == StateProxyConnected) + { + _proxy.finish(readBuffer, writeBuffer); + + readBuffer.clear(); + writeBuffer.clear(); + + _state = StateConnected; + } + + Debug.Assert(_state == StateConnected); + return SocketOperation.None; + } + + public bool isConnected() + { + return _state == StateConnected; + } + + public Socket fd() + { + return _fd; + } + + public int getSendPacketSize(int length) + { + return _maxSendPacketSize > 0 ? System.Math.Min(length, _maxSendPacketSize) : length; + } + + public int getRecvPacketSize(int length) + { + return _maxRecvPacketSize > 0 ? System.Math.Min(length, _maxRecvPacketSize) : length; + } + + public void setBufferSize(int rcvSize, int sndSize) + { + Network.setTcpBufSize(_fd, rcvSize, sndSize, _instance); + } + + public int read(Buffer buf) + { + if(_state == StateProxyRead) + { + while(true) + { + int ret = read(buf.b); + if(ret == 0) + { + return SocketOperation.Read; + } + + _state = toState(_proxy.endRead(buf)); + if(_state != StateProxyRead) + { + return SocketOperation.None; + } + } + } + read(buf.b); + return buf.b.hasRemaining() ? SocketOperation.Read : SocketOperation.None; + } + + public int write(Buffer buf) + { + if(_state == StateProxyWrite) + { + while(true) + { + int ret = write(buf.b); + if(ret == 0) + { + return SocketOperation.Write; + } + _state = toState(_proxy.endWrite(buf)); + if(_state != StateProxyWrite) + { + return SocketOperation.None; + } + } + } + write(buf.b); + return buf.b.hasRemaining() ? SocketOperation.Write : SocketOperation.None; + } + + public bool startRead(Buffer buf, AsyncCallback callback, object state) + { +#if ICE_SOCKET_ASYNC_API + Debug.Assert(_fd != null && _readEventArgs != null); +#else + Debug.Assert(_fd != null && _readResult == null); +#endif + + int packetSize = getRecvPacketSize(buf.b.remaining()); + try + { + _readCallback = callback; +#if ICE_SOCKET_ASYNC_API + _readEventArgs.UserToken = state; + _readEventArgs.SetBuffer(buf.b.rawBytes(), buf.b.position(), packetSize); + return !_fd.ReceiveAsync(_readEventArgs); +#else + _readResult = _fd.BeginReceive(buf.b.rawBytes(), buf.b.position(), packetSize, SocketFlags.None, + readCompleted, state); + return _readResult.CompletedSynchronously; +#endif + } + catch(SocketException ex) + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + throw new Ice.SocketException(ex); + } + } + + public void finishRead(Buffer buf) + { + if(_fd == null) // Transceiver was closed + { +#if !ICE_SOCKET_ASYNC_API + _readResult = null; +#endif + return; + } + +#if ICE_SOCKET_ASYNC_API + Debug.Assert(_fd != null && _readEventArgs != null); +#else + Debug.Assert(_fd != null && _readResult != null); +#endif + try + { +#if ICE_SOCKET_ASYNC_API + if(_readEventArgs.SocketError != SocketError.Success) + { + throw new SocketException((int)_readEventArgs.SocketError); + } + int ret = _readEventArgs.BytesTransferred; +#else + int ret = _fd.EndReceive(_readResult); + _readResult = null; +#endif + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + + Debug.Assert(ret > 0); + buf.b.position(buf.b.position() + ret); + + if(_state == StateProxyRead) + { + _state = toState(_proxy.endRead(buf)); + } + } + catch(SocketException ex) + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + } + + public bool startWrite(Buffer buf, AsyncCallback callback, object state, out bool completed) + { +#if ICE_SOCKET_ASYNC_API + Debug.Assert(_fd != null && _writeEventArgs != null); +#else + Debug.Assert(_fd != null && _writeResult == null); +#endif + + if(_state == StateConnectPending) + { + completed = false; + _writeCallback = callback; + try + { + EndPoint addr = _proxy != null ? _proxy.getAddress() : _addr; +#if ICE_SOCKET_ASYNC_API + _writeEventArgs.RemoteEndPoint = addr; + _writeEventArgs.UserToken = state; + return !_fd.ConnectAsync(_writeEventArgs); +#else + _writeResult = Network.doConnectAsync(_fd, addr, _sourceAddr, callback, state); + return _writeResult.CompletedSynchronously; +#endif + } + catch(Exception ex) + { + throw new Ice.SocketException(ex); + } + } + + int packetSize = getSendPacketSize(buf.b.remaining()); + try + { + _writeCallback = callback; +#if ICE_SOCKET_ASYNC_API + _writeEventArgs.UserToken = state; + _writeEventArgs.SetBuffer(buf.b.rawBytes(), buf.b.position(), packetSize); + bool completedSynchronously = !_fd.SendAsync(_writeEventArgs); +#else + _writeResult = _fd.BeginSend(buf.b.rawBytes(), buf.b.position(), packetSize, SocketFlags.None, + writeCompleted, state); + bool completedSynchronously = _writeResult.CompletedSynchronously; +#endif + completed = packetSize == buf.b.remaining(); + return completedSynchronously; + } + catch(SocketException ex) + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + } + + public void finishWrite(Buffer buf) + { + if(_fd == null) // Transceiver was closed + { + if(buf.size() - buf.b.position() < _maxSendPacketSize) + { + buf.b.position(buf.b.limit()); // Assume all the data was sent for at-most-once semantics. + } +#if !ICE_SOCKET_ASYNC_API + _writeResult = null; +#endif + return; + } + +#if ICE_SOCKET_ASYNC_API + Debug.Assert(_fd != null && _writeEventArgs != null); +#else + Debug.Assert(_fd != null && _writeResult != null); +#endif + + if(_state < StateConnected && _state != StateProxyWrite) + { + return; + } + + try + { +#if ICE_SOCKET_ASYNC_API + if(_writeEventArgs.SocketError != SocketError.Success) + { + throw new SocketException((int)_writeEventArgs.SocketError); + } + int ret = _writeEventArgs.BytesTransferred; +#else + int ret = _fd.EndSend(_writeResult); + _writeResult = null; +#endif + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + + Debug.Assert(ret > 0); + buf.b.position(buf.b.position() + ret); + + if(_state == StateProxyWrite) + { + _state = toState(_proxy.endWrite(buf)); + } + } + catch(SocketException ex) + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + } + + public void close() + { + Debug.Assert(_fd != null); + try + { + _fd.Close(); + } + catch(SocketException ex) + { + throw new Ice.SocketException(ex); + } + finally + { + _fd = null; + } + } + + public void destroy() + { +#if ICE_SOCKET_ASYNC_API + Debug.Assert(_readEventArgs != null && _writeEventArgs != null); + _readEventArgs.Dispose(); + _writeEventArgs.Dispose(); +#endif + } + + public override string ToString() + { + return _desc; + } + + private int read(ByteBuffer buf) + { + Debug.Assert(_fd != null); + +#if COMPACT || SILVERLIGHT + // + // Silverlight and the Compact .NET Framework don't + // support the use of synchronous socket operations on a + // non-blocking socket. Returning 0 here forces the caller + // to schedule an asynchronous operation. + // + return 0; +#else + int read = 0; + while(buf.hasRemaining()) + { + try + { + int ret = _fd.Receive(buf.rawBytes(), buf.position(), buf.remaining(), SocketFlags.None); + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + read += ret; + buf.position(buf.position() + ret); + } + catch(SocketException ex) + { + if(Network.wouldBlock(ex)) + { + return read; + } + else if(Network.interrupted(ex)) + { + continue; + } + else if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + + throw new Ice.SocketException(ex); + } + } + return read; +#endif + } + + private int write(ByteBuffer buf) + { + Debug.Assert(_fd != null); + +#if COMPACT || SILVERLIGHT + // + // Silverlight and the Compact .NET Frameworks don't + // support the use of synchronous socket operations on a + // non-blocking socket. Returning 0 here forces the caller + // to schedule an asynchronous operation. + // + return 0; +#else + + int packetSize = buf.remaining(); + if(AssemblyUtil.platform_ == AssemblyUtil.Platform.Windows) + { + // + // On Windows, limiting the buffer size is important to prevent + // poor throughput performances when transfering large amount of + // data. See Microsoft KB article KB823764. + // + if(_maxSendPacketSize > 0 && packetSize > _maxSendPacketSize / 2) + { + packetSize = _maxSendPacketSize / 2; + } + } + + int sent = 0; + while(buf.hasRemaining()) + { + try + { + int ret = _fd.Send(buf.rawBytes(), buf.position(), packetSize, SocketFlags.None); + Debug.Assert(ret > 0); + + sent += ret; + buf.position(buf.position() + ret); + if(packetSize > buf.remaining()) + { + packetSize = buf.remaining(); + } + } + catch(SocketException ex) + { + if(Network.wouldBlock(ex)) + { + return sent; + } + else if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + throw new Ice.SocketException(ex); + } + } + return sent; +#endif + } + +#if ICE_SOCKET_ASYNC_API + private void ioCompleted(object sender, SocketAsyncEventArgs e) + { + switch (e.LastOperation) + { + case SocketAsyncOperation.Receive: + _readCallback(e.UserToken); + break; + case SocketAsyncOperation.Send: + case SocketAsyncOperation.Connect: + _writeCallback(e.UserToken); + break; + default: + throw new ArgumentException("The last operation completed on the socket was not a receive or send"); + } + } +#else + private void readCompleted(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + _readCallback(result.AsyncState); + } + } + + private void writeCompleted(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + _writeCallback(result.AsyncState); + } + } +#endif + + private void init() + { +#if !SILVERLIGHT + Network.setBlock(_fd, false); +#endif + Network.setTcpBufSize(_fd, _instance); + +#if ICE_SOCKET_ASYNC_API + _readEventArgs = new SocketAsyncEventArgs(); + _readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ioCompleted); + + _writeEventArgs = new SocketAsyncEventArgs(); + _writeEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ioCompleted); +# if SILVERLIGHT + String policy = _instance.properties().getProperty("Ice.ClientAccessPolicyProtocol"); + if(policy.Equals("Http")) + { + _readEventArgs.SocketClientAccessPolicyProtocol = SocketClientAccessPolicyProtocol.Http; + _writeEventArgs.SocketClientAccessPolicyProtocol = SocketClientAccessPolicyProtocol.Http; + } + else if(!String.IsNullOrEmpty(policy)) + { + _instance.logger().warning("Ignoring invalid Ice.ClientAccessPolicyProtocol value `" + policy + "'"); + } +# endif +#endif + + // + // For timeouts to work properly, we need to receive/send + // the data in several chunks. Otherwise, we would only be + // notified when all the data is received/written. The + // connection timeout could easily be triggered when + // receiging/sending large messages. + // + _maxSendPacketSize = System.Math.Max(512, Network.getSendBufferSize(_fd)); + _maxRecvPacketSize = System.Math.Max(512, Network.getRecvBufferSize(_fd)); + } + + private int toState(int operation) + { + switch(operation) + { + case SocketOperation.Read: + return StateProxyRead; + case SocketOperation.Write: + return StateProxyWrite; + default: + return StateProxyConnected; + } + } + + private readonly ProtocolInstance _instance; + private readonly IceInternal.NetworkProxy _proxy; + private readonly EndPoint _addr; + private readonly EndPoint _sourceAddr; + + private Socket _fd; + private int _maxSendPacketSize; + private int _maxRecvPacketSize; + private int _state; + private string _desc; + +#if ICE_SOCKET_ASYNC_API + private SocketAsyncEventArgs _writeEventArgs; + private SocketAsyncEventArgs _readEventArgs; +#else + private IAsyncResult _writeResult; + private IAsyncResult _readResult; +#endif + + AsyncCallback _writeCallback; + AsyncCallback _readCallback; + + private const int StateNeedConnect = 0; + private const int StateConnectPending = 1; + private const int StateProxyWrite = 2; + private const int StateProxyRead = 3; + private const int StateProxyConnected = 4; + private const int StateConnected = 5; + } + +} diff --git a/csharp/src/Ice/StreamWrapper.cs b/csharp/src/Ice/StreamWrapper.cs new file mode 100644 index 00000000000..7ff9c8f3c97 --- /dev/null +++ b/csharp/src/Ice/StreamWrapper.cs @@ -0,0 +1,311 @@ + +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Diagnostics; + using System.IO; + using System.Text; + + // + // Class to provide a System.IO.Stream interface on top of a BasicStream. + // We use this to serialize arbitrary .NET serializable classes into + // a Slice byte sequence. + // + // For input streams, this class is a wrapper around the BasicStream + // class that passes all methods through. + // + // For output streams, we use a different stragegy: + // Slice sequences are encoded on the wire as a count of elements, followed + // by the sequence contents. For arbitrary .NET classes, we do not know how + // big the sequence that is eventually written will be. To avoid excessive + // data copying, this class mantains a private bytes_ array of 254 bytes and, + // initially, writes data into that array. If more than 254 bytes end up being + // written, we write a dummy sequence size of 255 (which occupies five bytes + // on the wire) into the BasicStream and, once this class is disposed, patch + // that size to match the actual size. Otherwise, if the bytes_ buffer contains + // fewer than 255 bytes when this class is disposed, we write the sequence size + // as a single byte, followed by the contents of the bytes_ buffer. + // + + enum StreamType { Read, Write }; + + public class StreamWrapper : System.IO.Stream, System.IDisposable + { + // + // Writeable stream constructor + // + public StreamWrapper(BasicStream s) + { + type_ = StreamType.Write; + s_ = s; + spos_ = s.pos(); + bytes_ = new byte[254]; + pos_ = 0; + length_ = 0; + } + + // + // Readable stream constructor + // + public StreamWrapper(int size, BasicStream s) + { + type_ = StreamType.Read; + s_ = s; + spos_ = 0; + bytes_ = null; + pos_ = 0; + length_ = size; + } + + public override int Read(byte[] buffer, int offset, int count) + { + Debug.Assert(buffer != null && offset >= 0 && count >= 0 && offset + count <= buffer.Length); + try + { + s_.getBuffer().b.get(buffer, offset, count); + } + catch(System.Exception ex) + { + throw new IOException("could not read from stream", ex); + } + return count; + } + + public override int ReadByte() + { + try + { + return s_.getBuffer().b.get(); + } + catch(System.Exception ex) + { + throw new IOException("could not read from stream", ex); + } + } + + public override void Write(byte[] array, int offset, int count) + { + Debug.Assert(type_ == StreamType.Write); + Debug.Assert(array != null && offset >= 0 && count >= 0 && offset + count <= array.Length); + try + { + if(bytes_ != null) + { + // + // If we can fit the data into the first 254 bytes, write it to bytes_. + // + if(count <= bytes_.Length - pos_) + { + System.Buffer.BlockCopy(array, offset, bytes_, pos_, count); + pos_ += count; + return; + } + + s_.writeSize(255); // Dummy size, until we know how big the stream + // really is and can patch the size. + if(pos_ > 0) + { + // + // Write the current contents of bytes_. + // + s_.expand(pos_); + s_.getBuffer().b.put(bytes_, 0, pos_); + } + + bytes_ = null; + } + + // + // Write data passed by caller. + // + s_.expand(count); + s_.getBuffer().b.put(array, offset, count); + pos_ += count; + } + catch(System.Exception ex) + { + throw new IOException("could not write to stream", ex); + } + } + + public override void WriteByte(byte value) + { + Debug.Assert(type_ == StreamType.Write); + try + { + if(bytes_ != null) + { + // + // If we can fit the data into the first 254 bytes, write it to bytes_. + // + if(pos_ < bytes_.Length) + { + bytes_[pos_++] = value; + return; + } + + s_.writeSize(255); // Dummy size, until we know how big the stream + // really is and can patch the size. + if(pos_ > 0) + { + // + // Write the current contents of bytes_. + // + s_.expand(pos_); + s_.getBuffer().b.put(bytes_, 0, pos_); + } + + bytes_ = null; + } + + // + // Write data passed by caller. + // + s_.expand(1); + s_.getBuffer().b.put(value); + pos_ += 1; + } + catch(System.Exception ex) + { + throw new IOException("could not write to stream", ex); + } + } + + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanWrite + { + get + { + return type_ == StreamType.Write; + } + } + + public override bool CanSeek + { + get + { + if(AssemblyUtil.runtime_ == AssemblyUtil.Runtime.Mono) + { + // + // The Mono deserialization implementation has a bug that causes a call to Seek() such + // that the reading position is set to -1. + // + return false; + } + else + { + return type_ == StreamType.Read; + } + } + } + + public override void Flush() + { + try + { + if(bytes_ != null) + { + Debug.Assert(pos_ <= bytes_.Length); + s_.pos(spos_); + s_.writeSize(pos_); + s_.expand(pos_); + s_.getBuffer().b.put(bytes_, 0, pos_); + } + else + { + int currentPos = s_.pos(); + s_.pos(spos_); + s_.writeSize(pos_); // Patch previously-written dummy value. + s_.pos(currentPos); + } + } + catch(System.Exception ex) + { + throw new IOException("could not flush stream", ex); + } + } + + public override long Length + { + get + { + return length_; + } + } + + public override long Position + { + get + { + return pos_; + } + + set + { + Seek(value, SeekOrigin.Begin); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + Debug.Assert(type_ != StreamType.Write); + + // Deliberately no size check here--positioning beyond the limit of the stream is legal. + switch(origin) + { + case SeekOrigin.Begin: + { + pos_ = (int)offset; + break; + } + case SeekOrigin.Current: + { + pos_ += (int)offset; + break; + } + case SeekOrigin.End: + { + pos_ = (int)length_ + (int)offset; + break; + } + default: + { + Debug.Assert(false); + break; + } + } + s_.pos(pos_); + return pos_; + } + + public override void SetLength(long value) + { + Debug.Assert(type_ == StreamType.Write && value >= 0); + length_ = value; + } + + private StreamType type_; + private BasicStream s_; + private int spos_; + private byte[] bytes_; + private int pos_; + private long length_; + } +} diff --git a/csharp/src/Ice/StringUtil.cs b/csharp/src/Ice/StringUtil.cs new file mode 100644 index 00000000000..32ce2d4b55e --- /dev/null +++ b/csharp/src/Ice/StringUtil.cs @@ -0,0 +1,515 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Text; +using System.Diagnostics; +using System.Collections.Generic; + +namespace IceUtilInternal +{ + + public sealed class StringUtil + { + // + // Return the index of the first character in str to + // appear in match, starting from 0. Returns -1 if none is + // found. + // + public static int findFirstOf(string str, string match) + { + return findFirstOf(str, match, 0); + } + + // + // Return the index of the first character in str to + // appear in match, starting from start. Returns -1 if none is + // found. + // + public static int findFirstOf(string str, string match, int start) + { + int len = str.Length; + for(int i = start; i < len; i++) + { + char ch = str[i]; + if(match.IndexOf((char) ch) != -1) + { + return i; + } + } + + return -1; + } + + // + // Return the index of the first character in str which does + // not appear in match, starting from 0. Returns -1 if none is + // found. + // + public static int findFirstNotOf(string str, string match) + { + return findFirstNotOf(str, match, 0); + } + + // + // Return the index of the first character in str which does + // not appear in match, starting from start. Returns -1 if none is + // found. + // + public static int findFirstNotOf(string str, string match, int start) + { + int len = str.Length; + for(int i = start; i < len; i++) + { + char ch = str[i]; + if(match.IndexOf((char) ch) == -1) + { + return i; + } + } + + return -1; + } + + // + // Write the byte b as an escape sequence if it isn't a printable ASCII + // character and append the escape sequence to sb. Additional characters + // that should be escaped can be passed in special. If b is any of these + // characters, b is preceded by a backslash in sb. + // + private static void encodeChar(byte b, StringBuilder sb, string special) + { + switch((char)b) + { + case '\\': + { + sb.Append("\\\\"); + break; + } + + case '\'': + { + sb.Append("\\'"); + break; + } + + case '"': + { + sb.Append("\\\""); + break; + } + + case '\b': + { + sb.Append("\\b"); + break; + } + + case '\f': + { + sb.Append("\\f"); + break; + } + + case '\n': + { + sb.Append("\\n"); + break; + } + + case '\r': + { + sb.Append("\\r"); + break; + } + + case '\t': + { + sb.Append("\\t"); + break; + } + + default: + { + if(!(b >= 32 && b <= 126)) + { + sb.Append('\\'); + string octal = System.Convert.ToString(b, 8); + // + // Add leading zeroes so that we avoid problems during + // decoding. For example, consider the encoded string + // \0013 (i.e., a character with value 1 followed by + // the character '3'). If the leading zeroes were omitted, + // the result would be incorrectly interpreted by the + // decoder as a single character with value 11. + // + for(int j = octal.Length; j < 3; j++) + { + sb.Append('0'); + } + sb.Append(octal); + } + else if(special != null && special.IndexOf((char)b) != -1) + { + sb.Append('\\'); + sb.Append((char)b); + } + else + { + sb.Append((char)b); + } + } + break; + } + } + + // + // Add escape sequences (such as "\n", or "\007") to make a string + // readable in ASCII. Any characters that appear in special are + // prefixed with a backslash in the returned string. + // + public static string escapeString(string s, string special) + { + if(special != null) + { + for(int i = 0; i < special.Length; ++i) + { + if((int)special[i] < 32 || (int)special[i] > 126) + { + throw new System.ArgumentException("special characters must be in ASCII range 32-126", + "special"); + } + } + } + + UTF8Encoding utf8 = new UTF8Encoding(); + byte[] bytes = utf8.GetBytes(s); + + StringBuilder result = new StringBuilder(bytes.Length); + for(int i = 0; i < bytes.Length; i++) + { + encodeChar(bytes[i], result, special); + } + + return result.ToString(); + } + + private static char checkChar(string s, int pos) + { + char c = s[pos]; + if(!(c >= 32 && c <= 126)) + { + string msg; + if(pos > 0) + { + msg = "character after `" + s.Substring(0, pos) + "'"; + } + else + { + msg = "first character"; + } + msg += " is not a printable ASCII character (ordinal " + (int)c + ")"; + throw new System.ArgumentException(msg); + } + return c; + } + + // + // Decode the character or escape sequence starting at start and return it. + // end marks the one-past-the-end position of the substring to be scanned. + // nextStart is set to the index of the first character following the decoded + // character or escape sequence. + // + private static char decodeChar(string s, int start, int end, out int nextStart) + { + Debug.Assert(start >= 0); + Debug.Assert(start < end); + Debug.Assert(end <= s.Length); + + char c; + + if(s[start] != '\\') + { + c = checkChar(s, start++); + } + else + { + if(start + 1 == end) + { + throw new System.ArgumentException("trailing backslash"); + } + switch(s[++start]) + { + case '\\': + case '\'': + case '"': + { + c = s[start++]; + break; + } + case 'b': + { + ++start; + c = '\b'; + break; + } + case 'f': + { + ++start; + c = '\f'; + break; + } + case 'n': + { + ++start; + c = '\n'; + break; + } + case 'r': + { + ++start; + c = '\r'; + break; + } + case 't': + { + ++start; + c = '\t'; + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int val = 0; + for(int j = 0; j < 3 && start < end; ++j) + { + int charVal = s[start++] - '0'; + if(charVal < 0 || charVal > 7) + { + --start; + break; + } + val = val * 8 + charVal; + } + if(val > 255) + { + string msg = "octal value \\" + System.Convert.ToString(val, 8) + " (" + val + + ") is out of range"; + throw new System.ArgumentException(msg, "s"); + } + c = System.Convert.ToChar(val); + break; + } + default: + { + c = checkChar(s, start++); + break; + } + } + } + nextStart = start; + return c; + } + + // + // Remove escape sequences from s and append the result to sb. + // Return true if successful, false otherwise. + // + private static void decodeString(string s, int start, int end, StringBuilder sb) + { + while(start < end) + { + sb.Append(decodeChar(s, start, end, out start)); + } + } + + // + // Remove escape sequences added by escapeString. Throws System.ArgumentException + // for an invalid input string. + // + public static string unescapeString(string s, int start, int end) + { + Debug.Assert(start >= 0 && start <= end && end <= s.Length); + + StringBuilder sb = new StringBuilder(); + decodeString(s, start, end, sb); + string decodedString = sb.ToString(); + + byte[] arr = new byte[decodedString.Length]; + for(int i = 0; i < arr.Length; ++i) + { + arr[i] = (byte)decodedString[i]; + } + + UTF8Encoding utf8 = new UTF8Encoding(false, true); + return utf8.GetString(arr, 0, arr.Length); // May raise ArgumentException. + } + + // + // Split string helper; returns null for unmatched quotes + // + static public string[] splitString(string str, string delim) + { + List<string> l = new List<string>(); + char[] arr = new char[str.Length]; + int pos = 0; + + int n = 0; + char quoteChar = '\0'; + while(pos < str.Length) + { + if(quoteChar == '\0' && (str[pos] == '"' || str[pos] == '\'')) + { + quoteChar = str[pos++]; + continue; // Skip the quote. + } + else if(quoteChar == '\0' && str[pos] == '\\' && pos + 1 < str.Length && + (str[pos + 1] == '\'' || str[pos + 1] == '"')) + { + ++pos; // Skip the backslash + } + else if(quoteChar != '\0' && str[pos] == '\\' && pos + 1 < str.Length && str[pos + 1] == quoteChar) + { + ++pos; // Skip the backslash + } + else if(quoteChar != '\0' && str[pos] == quoteChar) + { + ++pos; + quoteChar = '\0'; + continue; // Skip the quote. + } + else if(delim.IndexOf(str[pos]) != -1) + { + if(quoteChar == '\0') + { + ++pos; + if(n > 0) + { + l.Add(new string(arr, 0, n)); + n = 0; + } + continue; + } + } + + if(pos < str.Length) + { + arr[n++] = str[pos++]; + } + + } + + if(n > 0) + { + l.Add(new string(arr, 0, n)); + } + if(quoteChar != '\0') + { + return null; // Unmatched quote. + } + return l.ToArray(); + } + + public static int checkQuote(string s) + { + return checkQuote(s, 0); + } + + // + // If a single or double quotation mark is found at the start position, + // then the position of the matching closing quote is returned. If no + // quotation mark is found at the start position, then 0 is returned. + // If no matching closing quote is found, then -1 is returned. + // + public static int checkQuote(string s, int start) + { + char quoteChar = s[start]; + if(quoteChar == '"' || quoteChar == '\'') + { + start++; + int len = s.Length; + int pos; + while(start < len && (pos = s.IndexOf(quoteChar, start)) != -1) + { + if(s[pos - 1] != '\\') + { + return pos; + } + start = pos + 1; + } + return -1; // Unmatched quote + } + return 0; // Not quoted + } + + public static bool match(string s, string pat, bool emptyMatch) + { + Debug.Assert(s.Length > 0); + Debug.Assert(pat.Length > 0); + + // + // If pattern does not contain a wildcard just compare strings. + // + int beginIndex = pat.IndexOf('*'); + if(beginIndex < 0) + { + return s.Equals(pat); + } + + // + // Make sure start of the strings match + // + if(beginIndex > s.Length || !s.Substring(0, beginIndex).Equals(pat.Substring(0, beginIndex))) + { + return false; + } + + // + // Make sure there is something present in the middle to match the + // wildcard. If emptyMatch is true, allow a match of "". + // + int endLength = pat.Length - beginIndex - 1; + if(endLength > s.Length) + { + return false; + } + int endIndex = s.Length - endLength; + if(endIndex < beginIndex || (!emptyMatch && endIndex == beginIndex)) + { + return false; + } + + // + // Make sure end of the strings match + // + if(!s.Substring(endIndex, s.Length - endIndex).Equals( + pat.Substring(beginIndex + 1, pat.Length - beginIndex - 1))) + { + return false; + } + + return true; + } + + private class OrdinalStringComparerImpl : System.Collections.Generic.IComparer<string> + { + public int Compare(string l, string r) + { + return string.CompareOrdinal(l, r); + } + } + public static System.Collections.Generic.IComparer<string> OrdinalStringComparer = + new OrdinalStringComparerImpl(); + } +} diff --git a/csharp/src/Ice/SysLoggerI.cs b/csharp/src/Ice/SysLoggerI.cs new file mode 100644 index 00000000000..b8cfa08df57 --- /dev/null +++ b/csharp/src/Ice/SysLoggerI.cs @@ -0,0 +1,234 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#if !SILVERLIGHT +using System.Net.Sockets; + +namespace Ice +{ + + public sealed class SysLoggerI : Logger + { + public SysLoggerI(string prefix, string facilityString) + { + int facility; + if(facilityString.Equals("LOG_KERN")) + { + facility = LOG_KERN; + } + else if(facilityString.Equals("LOG_USER")) + { + facility = LOG_USER; + } + else if(facilityString.Equals("LOG_MAIL")) + { + facility = LOG_MAIL; + } + else if(facilityString.Equals("LOG_DAEMON")) + { + facility = LOG_DAEMON; + } + else if(facilityString.Equals("LOG_AUTH")) + { + facility = LOG_AUTH; + } + else if(facilityString.Equals("LOG_SYSLOG")) + { + facility = LOG_SYSLOG; + } + else if(facilityString.Equals("LOG_LPR")) + { + facility = LOG_LPR; + } + else if(facilityString.Equals("LOG_NEWS")) + { + facility = LOG_NEWS; + } + else if(facilityString.Equals("LOG_UUCP")) + { + facility = LOG_UUCP; + } + else if(facilityString.Equals("LOG_CRON")) + { + facility = LOG_CRON; + } + else if(facilityString.Equals("LOG_AUTHPRIV")) + { + facility = LOG_AUTHPRIV; + } + else if(facilityString.Equals("LOG_FTP")) + { + facility = LOG_FTP; + } + else if(facilityString.Equals("LOG_LOCAL0")) + { + facility = LOG_LOCAL0; + } + else if(facilityString.Equals("LOG_LOCAL1")) + { + facility = LOG_LOCAL1; + } + else if(facilityString.Equals("LOG_LOCAL2")) + { + facility = LOG_LOCAL2; + } + else if(facilityString.Equals("LOG_LOCAL3")) + { + facility = LOG_LOCAL3; + } + else if(facilityString.Equals("LOG_LOCAL4")) + { + facility = LOG_LOCAL4; + } + else if(facilityString.Equals("LOG_LOCAL5")) + { + facility = LOG_LOCAL5; + } + else if(facilityString.Equals("LOG_LOCAL6")) + { + facility = LOG_LOCAL6; + } + else if(facilityString.Equals("LOG_LOCAL7")) + { + facility = LOG_LOCAL7; + } + else + { + throw new Ice.InitializationException("Invalid value for Ice.SyslogFacility: " + facilityString); + } + initialize(prefix, facility); + } + + private SysLoggerI(string prefix, int facility) + { + initialize(prefix, facility); + } + + private void initialize(string prefix, int facility) + { + _prefix = prefix; + _facility = facility; + + // + // Open a datagram socket to communicate with the localhost + // syslog daemon. + // + try + { + _host = ((System.Net.IPEndPoint)IceInternal.Network.getAddressForServer( + System.Net.Dns.GetHostName(), _port, IceInternal.Network.EnableBoth, false)).Address; + _socket = new UdpClient(); + _socket.Connect(_host, _port); + } + catch(System.Exception ex) + { + throw new Ice.DNSException(ex); + } + } + + public void print(string message) + { + log(LOG_INFO, message); + } + + public void trace(string category, string message) + { + log(LOG_INFO, category + ": " + message); + } + + public void warning(string message) + { + log(LOG_WARNING, message); + } + + public void error(string message) + { + log(LOG_ERR, message); + } + + public string getPrefix() + { + return _prefix; + } + + public Logger cloneWithPrefix(string prefix) + { + return new SysLoggerI(prefix, _facility); + } + + private void log(int severity, string message) + { + try + { + // + // Create a syslog message as defined by the RFC 3164: + // <PRI>HEADER MSG. PRI is the priority and is calculated + // from the facility and the severity. We don't specify + // the HEADER. MSG contains the identifier followed by a + // colon character and the message. + // + + int priority = (_facility << 3) | severity; + + string msg = '<' + priority + '>' + _prefix + ": " + message; + + byte[] buf = new byte[msg.Length]; + for(int i = 0; i < msg.Length; i++) + { + buf[i] = (byte)msg[i]; + } + _socket.Send(buf, buf.Length); + } + catch(System.IO.IOException ex) + { + Ice.SocketException se = new Ice.SocketException(ex); + throw se; + } + } + + private string _prefix; + private int _facility; + private UdpClient _socket; + private System.Net.IPAddress _host; + private static int _port = 514; + + // + // Syslog facilities (as defined in syslog.h) + // + private const int LOG_KERN = 0; + private const int LOG_USER = 1; + private const int LOG_MAIL = 2; + private const int LOG_DAEMON = 3; + private const int LOG_AUTH = 4; + private const int LOG_SYSLOG = 5; + private const int LOG_LPR = 6; + private const int LOG_NEWS = 7; + private const int LOG_UUCP = 8; + private const int LOG_CRON = 9; + private const int LOG_AUTHPRIV = 10; + private const int LOG_FTP = 11; + private const int LOG_LOCAL0 = 16; + private const int LOG_LOCAL1 = 17; + private const int LOG_LOCAL2 = 18; + private const int LOG_LOCAL3 = 19; + private const int LOG_LOCAL4 = 20; + private const int LOG_LOCAL5 = 21; + private const int LOG_LOCAL6 = 22; + private const int LOG_LOCAL7 = 23; + + // + // Syslog priorities (as defined in syslog.h) + // + private const int LOG_ERR = 3; + private const int LOG_WARNING = 4; + private const int LOG_INFO = 6; + } + +} +#endif diff --git a/csharp/src/Ice/TcpAcceptor.cs b/csharp/src/Ice/TcpAcceptor.cs new file mode 100644 index 00000000000..d896d4367df --- /dev/null +++ b/csharp/src/Ice/TcpAcceptor.cs @@ -0,0 +1,176 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#if !SILVERLIGHT + +namespace IceInternal +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Net; + using System.Net.Sockets; + using System.Text; + + class TcpAcceptor : Acceptor + { + public virtual void close() + { + Debug.Assert(_acceptFd == null); + if(_fd != null) + { + Network.closeSocketNoThrow(_fd); + _fd = null; + } + } + + public virtual EndpointI listen() + { + try + { + _addr = Network.doBind(_fd, _addr); + Network.doListen(_fd, _backlog); + } + catch(SystemException) + { + _fd = null; + throw; + } + _endpoint = _endpoint.endpoint(this); + return _endpoint; + } + + public virtual bool startAccept(AsyncCallback callback, object state) + { + try + { + _result = _fd.BeginAccept(delegate(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + callback(result.AsyncState); + } + }, state); + } + catch(SocketException ex) + { + throw new Ice.SocketException(ex); + } + return _result.CompletedSynchronously; + } + + public virtual void finishAccept() + { + if(_fd != null) + { + _acceptFd = null; + try + { + _acceptFd = _fd.EndAccept(_result); + } + catch(SocketException ex) + { + _acceptError = ex; + } + } + } + + public virtual Transceiver accept() + { + if(_acceptFd == null) + { + throw _acceptError; + } + + Socket acceptFd = _acceptFd; + _acceptFd = null; + _acceptError = null; + return new TcpTransceiver(_instance, new StreamSocket(_instance, acceptFd)); + } + + public string protocol() + { + return _instance.protocol(); + } + + public override string ToString() + { + return Network.addrToString(_addr); + } + + public string toDetailedString() + { + StringBuilder s = new StringBuilder("local address = "); + s.Append(ToString()); + + List<string> intfs = + Network.getHostsForEndpointExpand(_addr.Address.ToString(), _instance.protocolSupport(), true); + if(intfs.Count != 0) + { + s.Append("\nlocal interfaces = "); + s.Append(String.Join(", ", intfs.ToArray())); + } + return s.ToString(); + } + + internal int effectivePort() + { + return _addr.Port; + } + + internal TcpAcceptor(TcpEndpointI endpoint, ProtocolInstance instance, string host, int port) + { + _endpoint = endpoint; + _instance = instance; + _backlog = instance.properties().getPropertyAsIntWithDefault("Ice.TCP.Backlog", 511); + + try + { + int protocol = _instance.protocolSupport(); + _addr = (IPEndPoint)Network.getAddressForServer(host, port, protocol, _instance.preferIPv6()); + _fd = Network.createServerSocket(false, _addr.AddressFamily, protocol); + Network.setBlock(_fd, false); +# if !COMPACT + Network.setTcpBufSize(_fd, _instance); +# endif + if(AssemblyUtil.platform_ != AssemblyUtil.Platform.Windows) + { + // + // Enable SO_REUSEADDR on Unix platforms to allow re-using the + // socket even if it's in the TIME_WAIT state. On Windows, + // this doesn't appear to be necessary and enabling + // SO_REUSEADDR would actually not be a good thing since it + // allows a second process to bind to an address even it's + // already bound by another process. + // + // TODO: using SO_EXCLUSIVEADDRUSE on Windows would probably + // be better but it's only supported by recent Windows + // versions (XP SP2, Windows Server 2003). + // + Network.setReuseAddress(_fd, true); + } + } + catch(System.Exception) + { + _fd = null; + throw; + } + } + + private TcpEndpointI _endpoint; + private ProtocolInstance _instance; + private Socket _fd; + private Socket _acceptFd; + private System.Exception _acceptError; + private int _backlog; + private IPEndPoint _addr; + private IAsyncResult _result; + } +} +#endif diff --git a/csharp/src/Ice/TcpConnector.cs b/csharp/src/Ice/TcpConnector.cs new file mode 100644 index 00000000000..c061171a9de --- /dev/null +++ b/csharp/src/Ice/TcpConnector.cs @@ -0,0 +1,101 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Net; + using System.Net.Sockets; + + sealed class TcpConnector : Connector + { + public Transceiver connect() + { + return new TcpTransceiver(_instance, new StreamSocket(_instance, _proxy, _addr, _sourceAddr)); + } + + public short type() + { + return _instance.type(); + } + + // + // Only for use by TcpEndpoint + // + internal TcpConnector(ProtocolInstance instance, EndPoint addr, NetworkProxy proxy, EndPoint sourceAddr, + int timeout, string connectionId) + { + _instance = instance; + _addr = addr; + _proxy = proxy; + _sourceAddr = sourceAddr; + _timeout = timeout; + _connectionId = connectionId; + + _hashCode = 5381; + IceInternal.HashUtil.hashAdd(ref _hashCode, _addr); + if(_sourceAddr != null) + { + IceInternal.HashUtil.hashAdd(ref _hashCode, _sourceAddr); + } + IceInternal.HashUtil.hashAdd(ref _hashCode, _timeout); + IceInternal.HashUtil.hashAdd(ref _hashCode, _connectionId); + } + + public override bool Equals(object obj) + { + if(!(obj is TcpConnector)) + { + return false; + } + + if(this == obj) + { + return true; + } + + TcpConnector p = (TcpConnector)obj; + if(_timeout != p._timeout) + { + return false; + } + + if(!Network.addressEquals(_sourceAddr, p._sourceAddr)) + { + return false; + } + + if(!_connectionId.Equals(p._connectionId)) + { + return false; + } + + return _addr.Equals(p._addr); + } + + public override string ToString() + { + return Network.addrToString(_proxy == null ? _addr : _proxy.getAddress()); + } + + public override int GetHashCode() + { + return _hashCode; + } + + private ProtocolInstance _instance; + private EndPoint _addr; + private NetworkProxy _proxy; + private EndPoint _sourceAddr; + private int _timeout; + private string _connectionId; + private int _hashCode; + } +} diff --git a/csharp/src/Ice/TcpEndpointI.cs b/csharp/src/Ice/TcpEndpointI.cs new file mode 100644 index 00000000000..fcfa84ab0c3 --- /dev/null +++ b/csharp/src/Ice/TcpEndpointI.cs @@ -0,0 +1,338 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Diagnostics; + using System.Collections.Generic; + using System.Net; + using System; + using System.Globalization; + + sealed class TcpEndpointI : IPEndpointI + { + public TcpEndpointI(ProtocolInstance instance, string ho, int po, EndPoint sourceAddr, int ti, string conId, + bool co) : + base(instance, ho, po, sourceAddr, conId) + { + _timeout = ti; + _compress = co; + } + + public TcpEndpointI(ProtocolInstance instance) : + base(instance) + { + _timeout = instance.defaultTimeout(); + _compress = false; + } + + public TcpEndpointI(ProtocolInstance instance, BasicStream s) : + base(instance, s) + { + _timeout = s.readInt(); + _compress = s.readBool(); + } + + private sealed class InfoI : Ice.TCPEndpointInfo + { + public InfoI(IPEndpointI e) + { + _endpoint = e; + } + + public override short type() + { + return _endpoint.type(); + } + + public override bool datagram() + { + return _endpoint.datagram(); + } + + public override bool secure() + { + return _endpoint.secure(); + } + + private IPEndpointI _endpoint; + } + + public override Ice.EndpointInfo getInfo() + { + InfoI info = new InfoI(this); + fillEndpointInfo(info); + return info; + } + + public override int timeout() + { + return _timeout; + } + + public override EndpointI timeout(int timeout) + { + if(timeout == _timeout) + { + return this; + } + else + { + return new TcpEndpointI(instance_, host_, port_, sourceAddr_, timeout, connectionId_, _compress); + } + } + + public override bool compress() + { + return _compress; + } + + public override EndpointI compress(bool compress) + { + if(compress == _compress) + { + return this; + } + else + { + return new TcpEndpointI(instance_, host_, port_, sourceAddr_, _timeout, connectionId_, compress); + } + } + + public override bool datagram() + { + return false; + } + + public override Transceiver transceiver() + { + return null; + } + + public override Acceptor acceptor(string adapterName) + { +#if SILVERLIGHT + throw new Ice.FeatureNotSupportedException("server endpoint not supported for `" + ToString() + "'"); +#else + return new TcpAcceptor(this, instance_, host_, port_); +#endif + } + +#if !SILVERLIGHT + public TcpEndpointI endpoint(TcpAcceptor acceptor) + { + return new TcpEndpointI(instance_, host_, acceptor.effectivePort(), sourceAddr_, _timeout, connectionId_, + _compress); + } +#endif + + public override string options() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + string s = base.options(); + + if(_timeout == -1) + { + s += " -t infinite"; + } + else + { + s += " -t " + _timeout; + } + + if(_compress) + { + s += " -z"; + } + + return s; + } + + public override int CompareTo(EndpointI obj) + { + if(!(obj is TcpEndpointI)) + { + return type() < obj.type() ? -1 : 1; + } + + TcpEndpointI p = (TcpEndpointI)obj; + if(this == p) + { + return 0; + } + + if(_timeout < p._timeout) + { + return -1; + } + else if(p._timeout < _timeout) + { + return 1; + } + + if(!_compress && p._compress) + { + return -1; + } + else if(!p._compress && _compress) + { + return 1; + } + + return base.CompareTo(p); + } + + public override void streamWriteImpl(BasicStream s) + { + base.streamWriteImpl(s); + s.writeInt(_timeout); + s.writeBool(_compress); + } + + public override void hashInit(ref int h) + { + base.hashInit(ref h); + IceInternal.HashUtil.hashAdd(ref h, _timeout); + IceInternal.HashUtil.hashAdd(ref h, _compress); + } + + public override void fillEndpointInfo(Ice.IPEndpointInfo info) + { + base.fillEndpointInfo(info); + info.timeout = _timeout; + info.compress = _compress; + } + + protected override bool checkOption(string option, string argument, string endpoint) + { + if(base.checkOption(option, argument, endpoint)) + { + return true; + } + + switch(option[1]) + { + case 't': + { + if(argument == null) + { + throw new Ice.EndpointParseException("no argument provided for -t option in endpoint " + + endpoint); + } + + if(argument.Equals("infinite")) + { + _timeout = -1; + } + else + { + try + { + _timeout = System.Int32.Parse(argument, CultureInfo.InvariantCulture); + if(_timeout < 1) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint; + throw e; + } + } + catch(System.FormatException ex) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(ex); + e.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint; + throw e; + } + } + + return true; + } + + case 'z': + { + if(argument != null) + { + throw new Ice.EndpointParseException("unexpected argument `" + argument + + "' provided for -z option in " + endpoint); + } + + _compress = true; + + return true; + } + + default: + { + return false; + } + } + } + + protected override Connector createConnector(EndPoint addr, NetworkProxy proxy) + { + return new TcpConnector(instance_, addr, proxy, sourceAddr_, _timeout, connectionId_); + } + + protected override IPEndpointI createEndpoint(string host, int port, string connectionId) + { + return new TcpEndpointI(instance_, host, port, sourceAddr_, _timeout, connectionId, _compress); + } + + private int _timeout; + private bool _compress; + } + + sealed class TcpEndpointFactory : EndpointFactory + { + internal TcpEndpointFactory(ProtocolInstance instance) + { + _instance = instance; + } + + public short type() + { + return _instance.type(); + } + + public string protocol() + { + return _instance.protocol(); + } + + public EndpointI create(List<string> args, bool oaEndpoint) + { + IPEndpointI endpt = new TcpEndpointI(_instance); + endpt.initWithOptions(args, oaEndpoint); + return endpt; + } + + public EndpointI read(BasicStream s) + { + return new TcpEndpointI(_instance, s); + } + + public void destroy() + { + _instance = null; + } + + public EndpointFactory clone(ProtocolInstance instance) + { + return new TcpEndpointFactory(instance); + } + + private ProtocolInstance _instance; + } + +} diff --git a/csharp/src/Ice/TcpTransceiver.cs b/csharp/src/Ice/TcpTransceiver.cs new file mode 100644 index 00000000000..11f7fd49578 --- /dev/null +++ b/csharp/src/Ice/TcpTransceiver.cs @@ -0,0 +1,136 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Net; + using System.Net.Sockets; + + sealed class TcpTransceiver : Transceiver + { + public Socket fd() + { + return _stream.fd(); + } + + public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData) + { + return _stream.connect(readBuffer, writeBuffer, ref hasMoreData); + } + + public int closing(bool initiator, Ice.LocalException ex) + { + // If we are initiating the connection closure, wait for the peer + // to close the TCP/IP connection. Otherwise, close immediately. + return initiator ? SocketOperation.Read : SocketOperation.None; + } + + public void close() + { + _stream.close(); + } + + public EndpointI bind() + { + Debug.Assert(false); + return null; + } + + public void destroy() + { + _stream.destroy(); + } + + public int write(Buffer buf) + { + return _stream.write(buf); + } + + public int read(Buffer buf, ref bool hasMoreData) + { + return _stream.read(buf); + } + + public bool startRead(Buffer buf, AsyncCallback callback, object state) + { + return _stream.startRead(buf, callback, state); + } + + public void finishRead(Buffer buf) + { + _stream.finishRead(buf); + } + + public bool startWrite(Buffer buf, AsyncCallback callback, object state, out bool completed) + { + return _stream.startWrite(buf, callback, state, out completed); + } + + public void finishWrite(Buffer buf) + { + _stream.finishWrite(buf); + } + + public string protocol() + { + return _instance.protocol(); + } + + public Ice.ConnectionInfo getInfo() + { + Ice.TCPConnectionInfo info = new Ice.TCPConnectionInfo(); + if(_stream.fd() != null) + { + EndPoint localEndpoint = Network.getLocalAddress(_stream.fd()); + info.localAddress = Network.endpointAddressToString(localEndpoint); + info.localPort = Network.endpointPort(localEndpoint); + EndPoint remoteEndpoint = Network.getRemoteAddress(_stream.fd()); + info.remoteAddress = Network.endpointAddressToString(remoteEndpoint); + info.remotePort = Network.endpointPort(remoteEndpoint); + info.rcvSize = Network.getRecvBufferSize(_stream.fd()); + info.sndSize = Network.getSendBufferSize(_stream.fd()); + } + return info; + } + + public void checkSendSize(Buffer buf) + { + } + + public void setBufferSize(int rcvSize, int sndSize) + { + _stream.setBufferSize(rcvSize, sndSize); + } + + public override string ToString() + { + return _stream.ToString(); + } + + public string toDetailedString() + { + return ToString(); + } + + // + // Only for use by TcpConnector, TcpAcceptor + // + internal TcpTransceiver(ProtocolInstance instance, StreamSocket stream) + { + _instance = instance; + _stream = stream; + } + + private readonly ProtocolInstance _instance; + private readonly StreamSocket _stream; + } +} diff --git a/csharp/src/Ice/ThreadHookPlugin.cs b/csharp/src/Ice/ThreadHookPlugin.cs new file mode 100644 index 00000000000..35ca94759b5 --- /dev/null +++ b/csharp/src/Ice/ThreadHookPlugin.cs @@ -0,0 +1,59 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// Class to support thread notification hooks. Applications using + /// thread notification hooks instantiate a ThreadHookPlugin with a + /// thread notification hook and return the instance from their + /// PluginFactory implementation. + /// </summary> + public class ThreadHookPlugin : Ice.Plugin + { + /// <summary> + /// Installs a custom logger for a communicator. + /// </summary> + /// <param name="communicator">The communicator using the thread notification hook.</param> + /// <param name="threadHook">The thread notification hook for the communicator.</param> + public + ThreadHookPlugin(Communicator communicator, ThreadNotification threadHook) + { + if(communicator == null) + { + PluginInitializationException ex = new PluginInitializationException(); + ex.reason = "Communicator cannot be null"; + throw ex; + } + + IceInternal.Instance instance = IceInternal.Util.getInstance(communicator); + instance.setThreadHook(threadHook); + } + + /// <summary> + /// Called by the Ice run time during communicator initialization. The derived class + /// can override this method to perform any initialization that might be required + /// by the thread notification hook. + /// </summary> + public void + initialize() + { + } + + /// <summary> + /// Called by the Ice run time when the communicator is destroyed. The derived class + /// can override this method to perform any finalization that might be required + /// by thread notification hook. + /// </summary> + public void + destroy() + { + } + } +} diff --git a/csharp/src/Ice/ThreadPool.cs b/csharp/src/Ice/ThreadPool.cs new file mode 100644 index 00000000000..1690d8ccb5c --- /dev/null +++ b/csharp/src/Ice/ThreadPool.cs @@ -0,0 +1,876 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + + public delegate void ThreadPoolWorkItem(); + public delegate void AsyncCallback(object state); + + internal struct ThreadPoolMessage + { + public ThreadPoolMessage(object mutex) + { + _mutex = mutex; + _finish = false; + _finishWithIO = false; + } + + public bool startIOScope(ref ThreadPoolCurrent current) + { + // This must be called with the handler locked. + _finishWithIO = current.startMessage(); + return _finishWithIO; + } + + public void finishIOScope(ref ThreadPoolCurrent current) + { + if(_finishWithIO) + { + lock(_mutex) + { + current.finishMessage(true); + } + } + } + + public void completed(ref ThreadPoolCurrent current) + { + // + // Call finishMessage once IO is completed only if serialization is not enabled. + // Otherwise, finishMessage will be called when the event handler is done with + // the message (it will be called from destroy below). + // + Debug.Assert(_finishWithIO); + if(current.ioCompleted()) + { + _finishWithIO = false; + _finish = true; + } + } + + public void destroy(ref ThreadPoolCurrent current) + { + if(_finish) + { + // + // A ThreadPoolMessage instance must be created outside the synchronization + // of the event handler. We need to lock the event handler here to call + // finishMessage. + // + lock(_mutex) + { + current.finishMessage(false); + Debug.Assert(!current.completedSynchronously); + } + } + } + + private object _mutex; + private bool _finish; + private bool _finishWithIO; + } + + public struct ThreadPoolCurrent + { + public ThreadPoolCurrent(ThreadPool threadPool, EventHandler handler, int op) + { + _threadPool = threadPool; + _handler = handler; + operation = op; + completedSynchronously = false; + } + + public readonly int operation; + public bool completedSynchronously; + + public bool ioCompleted() + { + return _threadPool.serialize(); + } + + public bool startMessage() + { + return _threadPool.startMessage(ref this); + } + + public void finishMessage(bool fromIOThread) + { + _threadPool.finishMessage(ref this, fromIOThread); + } + + internal readonly ThreadPool _threadPool; + internal readonly EventHandler _handler; + } + + public sealed class ThreadPool + { + public ThreadPool(Instance instance, string prefix, int timeout) + { + Ice.Properties properties = instance.initializationData().properties; + + _instance = instance; + _dispatcher = instance.initializationData().dispatcher; + _destroyed = false; + _prefix = prefix; + _threadIndex = 0; + _inUse = 0; + _serialize = properties.getPropertyAsInt(_prefix + ".Serialize") > 0; + _serverIdleTime = timeout; + + string programName = properties.getProperty("Ice.ProgramName"); + if(programName.Length > 0) + { + _threadPrefix = programName + "-" + _prefix; + } + else + { + _threadPrefix = _prefix; + } + + // + // We use just one thread as the default. This is the fastest + // possible setting, still allows one level of nesting, and + // doesn't require to make the servants thread safe. + // + int size = properties.getPropertyAsIntWithDefault(_prefix + ".Size", 1); + if(size < 1) + { + string s = _prefix + ".Size < 1; Size adjusted to 1"; + _instance.initializationData().logger.warning(s); + size = 1; + } + + int sizeMax = properties.getPropertyAsIntWithDefault(_prefix + ".SizeMax", size); + if(sizeMax < size) + { + string s = _prefix + ".SizeMax < " + _prefix + ".Size; SizeMax adjusted to Size (" + size + ")"; + _instance.initializationData().logger.warning(s); + sizeMax = size; + } + + int sizeWarn = properties.getPropertyAsInt(_prefix + ".SizeWarn"); + if(sizeWarn != 0 && sizeWarn < size) + { + string s = _prefix + ".SizeWarn < " + _prefix + ".Size; adjusted SizeWarn to Size (" + size + ")"; + _instance.initializationData().logger.warning(s); + sizeWarn = size; + } + else if(sizeWarn > sizeMax) + { + string s = _prefix + ".SizeWarn > " + _prefix + ".SizeMax; adjusted SizeWarn to SizeMax (" + + sizeMax + ")"; + _instance.initializationData().logger.warning(s); + sizeWarn = sizeMax; + } + + int threadIdleTime = properties.getPropertyAsIntWithDefault(_prefix + ".ThreadIdleTime", 60); + if(threadIdleTime < 0) + { + string s = _prefix + ".ThreadIdleTime < 0; ThreadIdleTime adjusted to 0"; + _instance.initializationData().logger.warning(s); + threadIdleTime = 0; + } + + _size = size; + _sizeMax = sizeMax; + _sizeWarn = sizeWarn; + _threadIdleTime = threadIdleTime; + + int stackSize = properties.getPropertyAsInt(_prefix + ".StackSize"); + if(stackSize < 0) + { + string s = _prefix + ".StackSize < 0; Size adjusted to OS default"; + _instance.initializationData().logger.warning(s); + stackSize = 0; + } + _stackSize = stackSize; + +#if !SILVERLIGHT + _hasPriority = properties.getProperty(_prefix + ".ThreadPriority").Length > 0; + _priority = IceInternal.Util.stringToThreadPriority(properties.getProperty(_prefix + ".ThreadPriority")); + if(!_hasPriority) + { + _hasPriority = properties.getProperty("Ice.ThreadPriority").Length > 0; + _priority = IceInternal.Util.stringToThreadPriority(properties.getProperty("Ice.ThreadPriority")); + } +#endif + + if(_instance.traceLevels().threadPool >= 1) + { + string s = "creating " + _prefix + ": Size = " + _size + ", SizeMax = " + _sizeMax + ", SizeWarn = " + + _sizeWarn; + _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s); + } + + _workItems = new Queue<ThreadPoolWorkItem>(); + + try + { + _threads = new List<WorkerThread>(); + for(int i = 0; i < _size; ++i) + { + WorkerThread thread = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++); +#if !SILVERLIGHT + if(_hasPriority) + { + thread.start(_priority); + } + else + { + thread.start(ThreadPriority.Normal); + } +#else + thread.start(); +#endif + _threads.Add(thread); + } + } + catch(System.Exception ex) + { + string s = "cannot create thread for `" + _prefix + "':\n" + ex; + _instance.initializationData().logger.error(s); + + destroy(); + joinWithAllThreads(); + throw; + } + } + + public void destroy() + { + lock(this) + { + if(_destroyed) + { + return; + } + _destroyed = true; + System.Threading.Monitor.PulseAll(this); + } + } + + public void updateObservers() + { + lock(this) + { + foreach(WorkerThread t in _threads) + { + t.updateObserver(); + } + } + } + + public void initialize(EventHandler handler) + { + // Nothing to do. + } + + public void register(EventHandler handler, int op) + { + update(handler, SocketOperation.None, op); + } + + public void update(EventHandler handler, int remove, int add) + { + lock(this) + { + Debug.Assert(!_destroyed); + + // Don't remove what needs to be added + remove &= ~add; + + // Don't remove/add if already un-registered or registered + remove &= handler._registered; + add &= ~handler._registered; + if(remove == add) + { + return; + } + + handler._registered &= ~remove; + handler._registered |= add; + + if((add & SocketOperation.Read) != 0 && (handler._pending & SocketOperation.Read) == 0) + { + handler._pending |= SocketOperation.Read; + executeNonBlocking(() => + { + messageCallback(new ThreadPoolCurrent(this, handler, SocketOperation.Read)); + }); + } + else if((add & SocketOperation.Write) != 0 && (handler._pending & SocketOperation.Write) == 0) + { + handler._pending |= SocketOperation.Write; + executeNonBlocking(() => + { + messageCallback(new ThreadPoolCurrent(this, handler, SocketOperation.Write)); + }); + } + } + } + + public void unregister(EventHandler handler, int op) + { + update(handler, op, SocketOperation.None); + } + + public void finish(EventHandler handler) + { + lock(this) + { + Debug.Assert(!_destroyed); + + // + // If there are no pending asynchronous operations, we can call finish on the handler now. + // + if(handler._pending == 0) + { + handler._registered = SocketOperation.None; + executeNonBlocking(() => + { + ThreadPoolCurrent current = new ThreadPoolCurrent(this, handler, SocketOperation.None); + handler.finished(ref current); + }); + } + else + { + handler._finish = true; + } + } + } + +#if COMPACT + public void dispatchFromThisThread(Ice.VoidAction call, Ice.Connection con) +#else + public void dispatchFromThisThread(System.Action call, Ice.Connection con) +#endif + { + if(_dispatcher != null) + { + try + { + _dispatcher(call, con); + } + catch(System.Exception ex) + { + if(_instance.initializationData().properties.getPropertyAsIntWithDefault( + "Ice.Warn.Dispatch", 1) > 1) + { + _instance.initializationData().logger.warning("dispatch exception:\n" + ex); + } + } + } + else + { + call(); + } + } + +#if COMPACT + public void dispatch(Ice.VoidAction call, Ice.Connection con) +#else + public void dispatch(System.Action call, Ice.Connection con) +#endif + { + lock(this) + { + if(_destroyed) + { + throw new Ice.CommunicatorDestroyedException(); + } + + _workItems.Enqueue(() => + { + dispatchFromThisThread(call, con); + }); + System.Threading.Monitor.Pulse(this); + + // + // If this is a dynamic thread pool which can still grow and if all threads are + // currently busy dispatching or about to dispatch, we spawn a new thread to + // execute this new work item right away. + // + if(_threads.Count < _sizeMax && + (_inUse + _workItems.Count) > _threads.Count && + !_destroyed) + { + if(_instance.traceLevels().threadPool >= 1) + { + string s = "growing " + _prefix + ": Size = " + (_threads.Count + 1); + _instance.initializationData().logger.trace(_instance.traceLevels().threadPoolCat, s); + } + + try + { + WorkerThread t = new WorkerThread(this, _threadPrefix + "-" + _threadIndex++); +#if !SILVERLIGHT + if(_hasPriority) + { + t.start(_priority); + } + else + { + t.start(ThreadPriority.Normal); + } +#else + t.start(); +#endif + _threads.Add(t); + } + catch(System.Exception ex) + { + string s = "cannot create thread for `" + _prefix + "':\n" + ex; + _instance.initializationData().logger.error(s); + } + } + } + } + + public void executeNonBlocking(ThreadPoolWorkItem workItem) + { + lock(this) + { + Debug.Assert(!_destroyed); + _instance.asyncIOThread().queue(workItem); + } + } + + public void joinWithAllThreads() + { + // + // _threads is immutable after destroy() has been called, + // therefore no synchronization is needed. (Synchronization + // wouldn't be possible here anyway, because otherwise the + // other threads would never terminate.) + // + Debug.Assert(_destroyed); + foreach(WorkerThread thread in _threads) + { + thread.join(); + } + } + + public string prefix() + { + return _prefix; + } + + public bool serialize() + { + return _serialize; + } + + private void run(WorkerThread thread) + { + ThreadPoolWorkItem workItem = null; + while(true) + { + lock(this) + { + if(workItem != null) + { + Debug.Assert(_inUse > 0); + --_inUse; + if(_workItems.Count == 0) + { + thread.setState(Ice.Instrumentation.ThreadState.ThreadStateIdle); + } + } + + workItem = null; + + while(_workItems.Count == 0) + { + if(_destroyed) + { + return; + } + + if(_threadIdleTime > 0) + { + if(!System.Threading.Monitor.Wait(this, _threadIdleTime * 1000) && _workItems.Count == 0) // If timeout + { + if(_destroyed) + { + return; + } + else if(_serverIdleTime == 0 || _threads.Count > 1) + { + // + // If not the last thread or if server idle time isn't configured, + // we can exit. Unlike C++/Java, there's no need to have a thread + // always spawned in the thread pool because all the IO is done + // by the .NET thread pool threads. Instead, we'll just spawn a + // new thread when needed (i.e.: when a new work item is queued). + // + if(_instance.traceLevels().threadPool >= 1) + { + string s = "shrinking " + _prefix + ": Size=" + (_threads.Count - 1); + _instance.initializationData().logger.trace( + _instance.traceLevels().threadPoolCat, s); + } + + _threads.Remove(thread); + _instance.asyncIOThread().queue(() => + { + thread.join(); + }); + return; + } + else + { + Debug.Assert(_serverIdleTime > 0 && _inUse == 0 && _threads.Count == 1); + if(!System.Threading.Monitor.Wait(this, _serverIdleTime * 1000) && + _workItems.Count == 0) + { + if(!_destroyed) + { + _workItems.Enqueue(() => + { + try + { + _instance.objectAdapterFactory().shutdown(); + } + catch(Ice.CommunicatorDestroyedException) + { + } + }); + } + } + } + } + } + else + { + System.Threading.Monitor.Wait(this); + } + } + + Debug.Assert(_workItems.Count > 0); + workItem = _workItems.Dequeue(); + + Debug.Assert(_inUse >= 0); + ++_inUse; + + thread.setState(Ice.Instrumentation.ThreadState.ThreadStateInUseForUser); + + if(_sizeMax > 1 && _inUse == _sizeWarn) + { + string s = "thread pool `" + _prefix + "' is running low on threads\n" + + "Size=" + _size + ", " + "SizeMax=" + _sizeMax + ", " + "SizeWarn=" + _sizeWarn; + _instance.initializationData().logger.warning(s); + } + } + + try + { + workItem(); + } + catch(System.Exception ex) + { + string s = "exception in `" + _prefix + "' while calling on work item:\n" + ex + '\n'; + _instance.initializationData().logger.error(s); + } + } + } + + public bool startMessage(ref ThreadPoolCurrent current) + { + Debug.Assert((current._handler._pending & current.operation) != 0); + + if((current._handler._started & current.operation) != 0) + { + Debug.Assert((current._handler._ready & current.operation) == 0); + current._handler._ready |= current.operation; + current._handler._started &= ~current.operation; + if(!current._handler.finishAsync(current.operation)) // Returns false if the handler is finished. + { + current._handler._pending &= ~current.operation; + if(current._handler._pending == 0 && current._handler._finish) + { + finish(current._handler); + } + return false; + } + } + else if((current._handler._ready & current.operation) == 0 && + (current._handler._registered & current.operation) != 0) + { + Debug.Assert((current._handler._started & current.operation) == 0); + bool completed = false; + if(!current._handler.startAsync(current.operation, getCallback(current.operation), ref completed)) + { + current._handler._pending &= ~current.operation; + if(current._handler._pending == 0 && current._handler._finish) + { + finish(current._handler); + } + return false; + } + else + { + current.completedSynchronously = completed; + current._handler._started |= current.operation; + return false; + } + } + + if((current._handler._registered & current.operation) != 0) + { + Debug.Assert((current._handler._ready & current.operation) != 0); + current._handler._ready &= ~current.operation; + return true; + } + else + { + current._handler._pending &= ~current.operation; + if(current._handler._pending == 0 && current._handler._finish) + { + finish(current._handler); + } + return false; + } + } + + public void finishMessage(ref ThreadPoolCurrent current, bool fromIOThread) + { + if((current._handler._registered & current.operation) != 0) + { + if(fromIOThread) + { + Debug.Assert((current._handler._ready & current.operation) == 0); + bool completed = false; + if(!current._handler.startAsync(current.operation, getCallback(current.operation), ref completed)) + { + current._handler._pending &= ~current.operation; + } + else + { + Debug.Assert((current._handler._pending & current.operation) != 0); + current.completedSynchronously = completed; + current._handler._started |= current.operation; + } + } + else + { + ThreadPoolCurrent c = current; + executeNonBlocking(() => + { + messageCallback(c); + }); + } + } + else + { + current._handler._pending &= ~current.operation; + } + + if(current._handler._pending == 0 && current._handler._finish) + { + // There are no more pending async operations, it's time to call finish. + finish(current._handler); + } + } + + public void asyncReadCallback(object state) + { + messageCallback(new ThreadPoolCurrent(this, (EventHandler)state, SocketOperation.Read)); + } + + public void asyncWriteCallback(object state) + { + messageCallback(new ThreadPoolCurrent(this, (EventHandler)state, SocketOperation.Write)); + } + + public void messageCallback(ThreadPoolCurrent current) + { + try + { + do + { + current.completedSynchronously = false; + current._handler.message(ref current); + } + while(current.completedSynchronously); + } + catch(System.Exception ex) + { + string s = "exception in `" + _prefix + "':\n" + ex + "\nevent handler: " + current._handler.ToString(); + _instance.initializationData().logger.error(s); + } + } + + private AsyncCallback getCallback(int operation) + { + switch(operation) + { + case SocketOperation.Read: + return asyncReadCallback; + case SocketOperation.Write: + return asyncWriteCallback; + default: + Debug.Assert(false); + return null; + } + } + + private Instance _instance; + private Ice.Dispatcher _dispatcher; + private bool _destroyed; + private readonly string _prefix; + private readonly string _threadPrefix; + + private sealed class WorkerThread + { + private ThreadPool _threadPool; + private Ice.Instrumentation.ThreadObserver _observer; + private Ice.Instrumentation.ThreadState _state; + + internal WorkerThread(ThreadPool threadPool, string name) : base() + { + _threadPool = threadPool; + _name = name; + _state = Ice.Instrumentation.ThreadState.ThreadStateIdle; + updateObserver(); + } + + public void updateObserver() + { + // Must be called with the thread pool mutex locked + Ice.Instrumentation.CommunicatorObserver obsv = _threadPool._instance.initializationData().observer; + if(obsv != null) + { + _observer = obsv.getThreadObserver(_threadPool._prefix, _name, _state, _observer); + if(_observer != null) + { + _observer.attach(); + } + } + } + + public void setState(Ice.Instrumentation.ThreadState s) + { + // Must be called with the thread pool mutex locked + if(_observer != null) + { + if(_state != s) + { + _observer.stateChanged(_state, s); + } + } + _state = s; + } + + public void join() + { + _thread.Join(); + } + +#if !SILVERLIGHT + public void start(ThreadPriority priority) + { + if(_threadPool._stackSize == 0) + { + _thread = new Thread(new ThreadStart(Run)); + } + else + { + _thread = new Thread(new ThreadStart(Run), _threadPool._stackSize); + } + _thread.IsBackground = true; + _thread.Name = _name; + _thread.Priority = priority; + _thread.Start(); + } +#else + public void start() + { + _thread = new Thread(new ThreadStart(Run)); + _thread.IsBackground = true; + _thread.Name = _name; + _thread.Start(); + } +#endif + + public void Run() + { + if(_threadPool._instance.initializationData().threadHook != null) + { + try + { + _threadPool._instance.initializationData().threadHook.start(); + } + catch(System.Exception ex) + { + string s = "thread hook start() method raised an unexpected exception in `"; + s += _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex; + _threadPool._instance.initializationData().logger.error(s); + } + } + + try + { + _threadPool.run(this); + } + catch(System.Exception ex) + { + string s = "exception in `" + _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex; + _threadPool._instance.initializationData().logger.error(s); + } + + if(_observer != null) + { + _observer.detach(); + } + + if(_threadPool._instance.initializationData().threadHook != null) + { + try + { + _threadPool._instance.initializationData().threadHook.stop(); + } + catch(System.Exception ex) + { + string s = "thread hook stop() method raised an unexpected exception in `"; + s += _threadPool._prefix + "' thread " + _thread.Name + ":\n" + ex; + _threadPool._instance.initializationData().logger.error(s); + } + } + } + + private readonly string _name; + private Thread _thread; + } + + private readonly int _size; // Number of threads that are pre-created. + private readonly int _sizeMax; // Maximum number of threads. + private readonly int _sizeWarn; // If _inUse reaches _sizeWarn, a "low on threads" warning will be printed. + private readonly bool _serialize; // True if requests need to be serialized over the connection. +#if !SILVERLIGHT + private readonly ThreadPriority _priority; + private readonly bool _hasPriority = false; +#endif + private readonly int _serverIdleTime; + private readonly int _threadIdleTime; + private readonly int _stackSize; + + private List<WorkerThread> _threads; // All threads, running or not. + private int _threadIndex; // For assigning thread names. + private int _inUse; // Number of threads that are currently in use. + + private Queue<ThreadPoolWorkItem> _workItems; + } + +} diff --git a/csharp/src/Ice/TieBase.cs b/csharp/src/Ice/TieBase.cs new file mode 100644 index 00000000000..a88b301a913 --- /dev/null +++ b/csharp/src/Ice/TieBase.cs @@ -0,0 +1,30 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// Interface for servants using the tie mapping. + /// </summary> + public interface TieBase + { + /// <summary> + /// Returns the delegate for this tie. + /// </summary> + /// <returns>The delegate.</returns> + object ice_delegate(); + + /// <summary> + /// Returns the delegate for this tie. + /// </summary> + /// <param name="o">The delegate.</param> + void ice_delegate(object o); + } + +} diff --git a/csharp/src/Ice/Time.cs b/csharp/src/Ice/Time.cs new file mode 100644 index 00000000000..85f68b438f3 --- /dev/null +++ b/csharp/src/Ice/Time.cs @@ -0,0 +1,103 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ +#if !SILVERLIGHT + using System.Diagnostics; + + public sealed class Time + { + static Time() + { + _stopwatch.Start(); + } + + public static long currentMonotonicTimeMillis() + { + return _stopwatch.ElapsedMilliseconds; + } + + private static Stopwatch _stopwatch = new Stopwatch(); + } + +#else + + public class Stopwatch + { + public void Start() + { + if(!_running) + { + _startTick = System.DateTime.Now.Ticks; + _running = true; + } + } + + public void Stop() + { + if(_running) + { + _elapsedTicks += System.DateTime.Now.Ticks - _startTick; + _running = false; + } + + } + + public void Reset() + { + _startTick = 0; + _elapsedTicks = 0; + _running = false; + } + + public long ElapsedTicks + { + get + { + if(!_running) + { + return _elapsedTicks; + } + else + { + return _elapsedTicks + (System.DateTime.Now.Ticks - _startTick); + } + } + } + + public long Frequency + { + get + { + return System.TimeSpan.TicksPerMillisecond * 1000; + } + } + + private long _startTick = 0; + private long _elapsedTicks = 0; + private bool _running = false; + } + + public sealed class Time + { + static Time() + { + _begin = System.DateTime.Now.Ticks; + } + + public static long currentMonotonicTimeMillis() + { + return (System.DateTime.Now.Ticks - _begin) / 10000; + } + + private static long _begin; + } +#endif +} diff --git a/csharp/src/Ice/Timer.cs b/csharp/src/Ice/Timer.cs new file mode 100644 index 00000000000..9c13bb67b7f --- /dev/null +++ b/csharp/src/Ice/Timer.cs @@ -0,0 +1,413 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// NOTE: We don't use C# timers, the API is quite a bit different from +// the C++ & Java timers and it's not clear what is the cost of +// scheduling and cancelling timers. +// + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Threading; + using System.Collections; + using System.Collections.Generic; + + public interface TimerTask + { + void runTimerTask(); + } + + public sealed class Timer + { + public void destroy() + { + lock(this) + { + if(_instance == null) + { + return; + } + + _instance = null; + System.Threading.Monitor.Pulse(this); + + _tokens.Clear(); + _tasks.Clear(); + } + + _thread.Join(); + } + + public void schedule(TimerTask task, long delay) + { + lock(this) + { + if(_instance == null) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Token token = new Token(Time.currentMonotonicTimeMillis() + delay, ++_tokenId, 0, task); + + try + { + _tasks.Add(task, token); +#if SILVERLIGHT + int index = _tokens.BinarySearch(token); + Debug.Assert(index < 0); + if(index < 0) + { + _tokens.Insert(~index, token); + } +#else + _tokens.Add(token, null); +#endif + } + catch(System.ArgumentException) + { + Debug.Assert(false); + } + + if(token.scheduledTime < _wakeUpTime) + { + System.Threading.Monitor.Pulse(this); + } + } + } + + public void scheduleRepeated(TimerTask task, long period) + { + lock(this) + { + if(_instance == null) + { + throw new Ice.CommunicatorDestroyedException(); + } + + Token token = new Token(Time.currentMonotonicTimeMillis() + period, ++_tokenId, period, task); + + try + { + _tasks.Add(task, token); +#if SILVERLIGHT + int index = _tokens.BinarySearch(token); + Debug.Assert(index < 0); + if(index < 0) + { + _tokens.Insert(~index, token); + } +#else + _tokens.Add(token, null); +#endif + } + catch(System.ArgumentException) + { + Debug.Assert(false); + } + + if(token.scheduledTime < _wakeUpTime) + { + System.Threading.Monitor.Pulse(this); + } + } + } + + public bool cancel(TimerTask task) + { + lock(this) + { + if(_instance == null) + { + return false; + } + + Token token; + if(!_tasks.TryGetValue(task, out token)) + { + return false; + } + _tasks.Remove(task); + _tokens.Remove(token); + return true; + } + } + + // + // Only for use by Instance. + // +#if !SILVERLIGHT + internal Timer(IceInternal.Instance instance, ThreadPriority priority) + { + init(instance, priority, true); + } +#endif + + internal Timer(IceInternal.Instance instance) + { +#if !SILVERLIGHT + init(instance, ThreadPriority.Normal, false); +#else + init(instance); +#endif + } + +#if !SILVERLIGHT + internal void init(IceInternal.Instance instance, ThreadPriority priority, bool hasPriority) +#else + internal void init(IceInternal.Instance instance) +#endif + { + _instance = instance; + + string threadName = _instance.initializationData().properties.getProperty("Ice.ProgramName"); + if(threadName.Length > 0) + { + threadName += "-"; + } + + _thread = new Thread(new ThreadStart(Run)); + _thread.IsBackground = true; + _thread.Name = threadName + "Ice.Timer"; +#if !SILVERLIGHT + if(hasPriority) + { + _thread.Priority = priority; + } +#endif + _thread.Start(); + } + + internal void updateObserver(Ice.Instrumentation.CommunicatorObserver obsv) + { + lock(this) + { + Debug.Assert(obsv != null); + _observer = obsv.getThreadObserver("Communicator", + _thread.Name, + Ice.Instrumentation.ThreadState.ThreadStateIdle, + _observer); + if(_observer != null) + { + _observer.attach(); + } + } + } + + public void Run() + { + Token token = null; + while(true) + { + lock(this) + { + if(_instance != null) + { + // + // If the task we just ran is a repeated task, schedule it + // again for execution if it wasn't canceled. + // + if(token != null && token.delay > 0) + { + if(_tasks.ContainsKey(token.task)) + { + token.scheduledTime = Time.currentMonotonicTimeMillis() + token.delay; +#if SILVERLIGHT + int index = _tokens.BinarySearch(token); + Debug.Assert(index < 0); + if(index < 0) + { + _tokens.Insert(~index, token); + } +#else + _tokens.Add(token, null); +#endif + } + } + } + token = null; + + if(_instance == null) + { + break; + } + + if(_tokens.Count == 0) + { + _wakeUpTime = System.Int64.MaxValue; + System.Threading.Monitor.Wait(this); + } + + if(_instance == null) + { + break; + } + + while(_tokens.Count > 0 && _instance != null) + { + long now = Time.currentMonotonicTimeMillis(); + + Token first = null; +#if SILVERLIGHT + foreach(Token t in _tokens) +#else + foreach(Token t in _tokens.Keys) +#endif + { + first = t; + break; + } + Debug.Assert(first != null); + + if(first.scheduledTime <= now) + { + _tokens.Remove(first); + token = first; + if(token.delay == 0) + { + _tasks.Remove(token.task); + } + break; + } + + _wakeUpTime = first.scheduledTime; + System.Threading.Monitor.Wait(this, (int)(first.scheduledTime - now)); + } + + if(_instance == null) + { + break; + } + } + + if(token != null) + { + try + { + Ice.Instrumentation.ThreadObserver threadObserver = _observer; + if(threadObserver != null) + { + threadObserver.stateChanged(Ice.Instrumentation.ThreadState.ThreadStateIdle, + Ice.Instrumentation.ThreadState.ThreadStateInUseForOther); + try + { + token.task.runTimerTask(); + } + finally + { + threadObserver.stateChanged(Ice.Instrumentation.ThreadState.ThreadStateInUseForOther, + Ice.Instrumentation.ThreadState.ThreadStateIdle); + } + } + else + { + token.task.runTimerTask(); + } + } + catch(System.Exception ex) + { + lock(this) + { + if(_instance != null) + { + string s = "unexpected exception from task run method in timer thread:\n" + ex; + _instance.initializationData().logger.error(s); + } + } + } + } + } + } + + private class Token : System.IComparable + { + public + Token(long scheduledTime, int id, long delay, TimerTask task) + { + this.scheduledTime = scheduledTime; + this.id = id; + this.delay = delay; + this.task = task; + } + + public int CompareTo(object o) + { + // + // Token are sorted by scheduled time and token id. + // + Token r = (Token)o; + if(scheduledTime < r.scheduledTime) + { + return -1; + } + else if(scheduledTime > r.scheduledTime) + { + return 1; + } + + if(id < r.id) + { + return -1; + } + else if(id > r.id) + { + return 1; + } + + return 0; + } + + public override bool Equals(object o) + { + if(object.ReferenceEquals(this, o)) + { + return true; + } + Token t = o as Token; + return t == null ? false : CompareTo(t) == 0; + } + + public override int GetHashCode() + { + int h = 5381; + IceInternal.HashUtil.hashAdd(ref h, id); + IceInternal.HashUtil.hashAdd(ref h, scheduledTime); + return h; + } + + public long scheduledTime; + public int id; // Since we can't compare references, we need to use another id. + public long delay; + public TimerTask task; + } + +#if COMPACT + private IDictionary<Token, object> _tokens = new SortedList<Token, object>(); +#elif SILVERLIGHT + private List<Token> _tokens = new List<Token>(); +#else + private IDictionary<Token, object> _tokens = new SortedDictionary<Token, object>(); +#endif + private IDictionary<TimerTask, Token> _tasks = new Dictionary<TimerTask, Token>(); + private Instance _instance; + private long _wakeUpTime = System.Int64.MaxValue; + private int _tokenId = 0; + private Thread _thread; + + // + // We use a volatile to avoid synchronization when reading + // _observer. Reference assignement is atomic in Java so it + // also doesn't need to be synchronized. + // + private volatile Ice.Instrumentation.ThreadObserver _observer; +} + +} diff --git a/csharp/src/Ice/TraceLevels.cs b/csharp/src/Ice/TraceLevels.cs new file mode 100644 index 00000000000..dd6b606dc3b --- /dev/null +++ b/csharp/src/Ice/TraceLevels.cs @@ -0,0 +1,48 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + public sealed class TraceLevels + { + internal TraceLevels(Ice.Properties properties) + { + networkCat = "Network"; + protocolCat = "Protocol"; + retryCat = "Retry"; + locationCat = "Locator"; + slicingCat = "Slicing"; + threadPoolCat = "ThreadPool"; + + string keyBase = "Ice.Trace."; + + network = properties.getPropertyAsInt(keyBase + networkCat); + protocol = properties.getPropertyAsInt(keyBase + protocolCat); + retry = properties.getPropertyAsInt(keyBase + retryCat); + location = properties.getPropertyAsInt(keyBase + locationCat); + slicing = properties.getPropertyAsInt(keyBase + slicingCat); + threadPool = properties.getPropertyAsInt(keyBase + threadPoolCat); + } + + public readonly int network; + public readonly string networkCat; + public readonly int protocol; + public readonly string protocolCat; + public readonly int retry; + public readonly string retryCat; + public readonly int location; + public readonly string locationCat; + public readonly int slicing; + public readonly string slicingCat; + public readonly int threadPool; + public readonly string threadPoolCat; + } + +} diff --git a/csharp/src/Ice/TraceUtil.cs b/csharp/src/Ice/TraceUtil.cs new file mode 100644 index 00000000000..70ba169698e --- /dev/null +++ b/csharp/src/Ice/TraceUtil.cs @@ -0,0 +1,504 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + + sealed class TraceUtil + { + internal static void traceSend(BasicStream str, Ice.Logger logger, TraceLevels tl) + { + if(tl.protocol >= 1) + { + int p = str.pos(); + str.pos(0); + + using(System.IO.StringWriter s = new System.IO.StringWriter(CultureInfo.CurrentCulture)) + { + byte type = printMessage(s, str); + + logger.trace(tl.protocolCat, "sending " + getMessageTypeAsString(type) + " " + s.ToString()); + } + str.pos(p); + } + } + + internal static void traceRecv(BasicStream str, Ice.Logger logger, TraceLevels tl) + { + if(tl.protocol >= 1) + { + int p = str.pos(); + str.pos(0); + + using(System.IO.StringWriter s = new System.IO.StringWriter(CultureInfo.CurrentCulture)) + { + byte type = printMessage(s, str); + + logger.trace(tl.protocolCat, "received " + getMessageTypeAsString(type) + " " + s.ToString()); + } + str.pos(p); + } + } + + internal static void trace(string heading, BasicStream str, Ice.Logger logger, TraceLevels tl) + { + if(tl.protocol >= 1) + { + int p = str.pos(); + str.pos(0); + + using(System.IO.StringWriter s = new System.IO.StringWriter(CultureInfo.CurrentCulture)) + { + s.Write(heading); + printMessage(s, str); + + logger.trace(tl.protocolCat, s.ToString()); + } + str.pos(p); + } + } + + private static HashSet<string> slicingIds = new HashSet<string>(); + + internal static void traceSlicing(string kind, string typeId, string slicingCat, Ice.Logger logger) + { + lock(typeof(IceInternal.TraceUtil)) + { + if(slicingIds.Add(typeId)) + { + using(System.IO.StringWriter s = new System.IO.StringWriter(CultureInfo.CurrentCulture)) + { + s.Write("unknown " + kind + " type `" + typeId + "'"); + logger.trace(slicingCat, s.ToString()); + } + } + } + } + + public static void dumpStream(BasicStream stream) + { + int pos = stream.pos(); + stream.pos(0); + + byte[] data = new byte[stream.size()]; + stream.readBlob(data); + dumpOctets(data); + + stream.pos(pos); + } + + public static void dumpOctets(byte[] data) + { + const int inc = 8; + + for(int i = 0; i < data.Length; i += inc) + { + for(int j = i; j - i < inc; j++) + { + if(j < data.Length) + { + int n = (int)data[j]; + if(n < 0) + { + n += 256; + } + string s; + if(n < 10) + { + s = " " + n; + } + else if(n < 100) + { + s = " " + n; + } + else + { + s = "" + n; + } + System.Console.Out.Write(s + " "); + } + else + { + System.Console.Out.Write(" "); + } + } + + System.Console.Out.Write('"'); + + for(int j = i; j < data.Length && j - i < inc; j++) + { + // TODO: this needs fixing + if(data[j] >= (byte)32 && data[j] < (byte)127) + { + System.Console.Out.Write((char) data[j]); + } + else + { + System.Console.Out.Write('.'); + } + } + + System.Console.Out.WriteLine('"'); + } + } + + private static void printIdentityFacetOperation(System.IO.StringWriter s, BasicStream str) + { + try + { + Ice.Identity identity = new Ice.Identity(); + identity.read__(str); + s.Write("\nidentity = " + str.instance().identityToString(identity)); + + string[] facet = str.readStringSeq(); + s.Write("\nfacet = "); + if(facet.Length > 0) + { + s.Write(IceUtilInternal.StringUtil.escapeString(facet[0], "")); + } + + string operation = str.readString(); + s.Write("\noperation = " + operation); + } + catch(System.IO.IOException) + { + Debug.Assert(false); + } + } + + private static void printRequest(System.IO.StringWriter s, BasicStream str) + { + int requestId = str.readInt(); + s.Write("\nrequest id = " + requestId); + if(requestId == 0) + { + s.Write(" (oneway)"); + } + + printRequestHeader(s, str); + } + + private static void printBatchRequest(System.IO.StringWriter s, BasicStream str) + { + int batchRequestNum = str.readInt(); + s.Write("\nnumber of requests = " + batchRequestNum); + + for(int i = 0; i < batchRequestNum; ++i) + { + s.Write("\nrequest #" + i + ':'); + printRequestHeader(s, str); + } + } + + private static void printReply(System.IO.StringWriter s, BasicStream str) + { + int requestId = str.readInt(); + s.Write("\nrequest id = " + requestId); + + byte replyStatus = str.readByte(); + s.Write("\nreply status = " + (int)replyStatus + ' '); + + switch(replyStatus) + { + case ReplyStatus.replyOK: + { + s.Write("(ok)"); + break; + } + + case ReplyStatus.replyUserException: + { + s.Write("(user exception)"); + break; + } + + case ReplyStatus.replyObjectNotExist: + case ReplyStatus.replyFacetNotExist: + case ReplyStatus.replyOperationNotExist: + { + switch(replyStatus) + { + case ReplyStatus.replyObjectNotExist: + { + s.Write("(object not exist)"); + break; + } + + case ReplyStatus.replyFacetNotExist: + { + s.Write("(facet not exist)"); + break; + } + + case ReplyStatus.replyOperationNotExist: + { + s.Write("(operation not exist)"); + break; + } + + default: + { + Debug.Assert(false); + break; + } + } + + printIdentityFacetOperation(s, str); + break; + } + + case ReplyStatus.replyUnknownException: + case ReplyStatus.replyUnknownLocalException: + case ReplyStatus.replyUnknownUserException: + { + switch(replyStatus) + { + case ReplyStatus.replyUnknownException: + { + s.Write("(unknown exception)"); + break; + } + + case ReplyStatus.replyUnknownLocalException: + { + s.Write("(unknown local exception)"); + break; + } + + case ReplyStatus.replyUnknownUserException: + { + s.Write("(unknown user exception)"); + break; + } + + default: + { + Debug.Assert(false); + break; + } + } + + string unknown = str.readString(); + s.Write("\nunknown = " + unknown); + break; + } + + default: + { + s.Write("(unknown)"); + break; + } + } + } + + private static void printRequestHeader(System.IO.StringWriter s, BasicStream str) + { + printIdentityFacetOperation(s, str); + + try + { + byte mode = str.readByte(); + s.Write("\nmode = " + (int)mode + ' '); + switch((Ice.OperationMode)mode) + { + case Ice.OperationMode.Normal: + { + s.Write("(normal)"); + break; + } + + case Ice.OperationMode.Nonmutating: + { + s.Write("(nonmutating)"); + break; + } + + case Ice.OperationMode.Idempotent: + { + s.Write("(idempotent)"); + break; + } + + default: + { + s.Write("(unknown)"); + break; + } + } + + int sz = str.readSize(); + s.Write("\ncontext = "); + while(sz-- > 0) + { + string key = str.readString(); + string val = str.readString(); + s.Write(key + '/' + val); + if(sz > 0) + { + s.Write(", "); + } + } + + Ice.EncodingVersion v = str.skipEncaps(); + if(!v.Equals(Ice.Util.Encoding_1_0)) + { + s.Write("\nencoding = "); + s.Write(Ice.Util.encodingVersionToString(v)); + } + } + catch(System.IO.IOException) + { + Debug.Assert(false); + } + } + + private static byte printHeader(System.IO.StringWriter s, BasicStream str) + { + try + { + str.readByte(); // Don't bother printing the magic number + str.readByte(); + str.readByte(); + str.readByte(); + + /* byte pMajor = */ str.readByte(); + /* byte pMinor = */ str.readByte(); + //s.Write("\nprotocol version = " + (int)pMajor + "." + (int)pMinor); + + /* byte eMajor = */ str.readByte(); + /* byte eMinor = */ str.readByte(); + //s.Write("\nencoding version = " + (int)eMajor + "." + (int)eMinor); + + byte type = str.readByte(); + s.Write("\nmessage type = " + (int)type + " (" + getMessageTypeAsString(type) + ')'); + + byte compress = str.readByte(); + s.Write("\ncompression status = " + (int)compress + ' '); + switch(compress) + { + case (byte)0: + { + s.Write("(not compressed; do not compress response, if any)"); + break; + } + + case (byte)1: + { + s.Write("(not compressed; compress response, if any)"); + break; + } + + case (byte)2: + { + s.Write("(compressed; compress response, if any)"); + break; + } + + default: + { + s.Write("(unknown)"); + break; + } + } + + int size = str.readInt(); + s.Write("\nmessage size = " + size); + return type; + } + catch(System.IO.IOException) + { + Debug.Assert(false); + return 0; + } + } + + private static byte printMessage(System.IO.StringWriter s, BasicStream str) + { + byte type = printHeader(s, str); + + switch(type) + { + case Protocol.closeConnectionMsg: + case Protocol.validateConnectionMsg: + { + // We're done. + break; + } + + case Protocol.requestMsg: + { + printRequest(s, str); + break; + } + + case Protocol.requestBatchMsg: + { + printBatchRequest(s, str); + break; + } + + case Protocol.replyMsg: + { + printReply(s, str); + break; + } + + default: + { + s.Write("(unknown)"); + break; + } + } + + return type; + } + + internal static void traceHeader(string heading, BasicStream str, Ice.Logger logger, TraceLevels tl) + { + if(tl.protocol >= 1) + { + int p = str.pos(); + str.pos(0); + + using(System.IO.StringWriter s = new System.IO.StringWriter(CultureInfo.CurrentCulture)) + { + s.Write(heading); + printHeader(s, str); + + logger.trace(tl.protocolCat, s.ToString()); + } + str.pos(p); + } + } + + private static string getMessageTypeAsString(byte type) + { + switch(type) + { + case Protocol.requestMsg: + return "request"; + case Protocol.requestBatchMsg: + return "batch request"; + case Protocol.replyMsg: + return "reply"; + case Protocol.closeConnectionMsg: + return "close connection"; + case Protocol.validateConnectionMsg: + return "validate connection"; + default: + return "unknown"; + } + } + } + +} diff --git a/csharp/src/Ice/Transceiver.cs b/csharp/src/Ice/Transceiver.cs new file mode 100644 index 00000000000..4208c96dfc1 --- /dev/null +++ b/csharp/src/Ice/Transceiver.cs @@ -0,0 +1,59 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Net.Sockets; + + public interface Transceiver + { + Socket fd(); + int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData); + int closing(bool initiator, Ice.LocalException ex); + void close(); + void destroy(); + + EndpointI bind(); + int write(Buffer buf); + int read(Buffer buf, ref bool hasMoreData); + + // + // Read data asynchronously. + // + // The I/O request may complete synchronously, in which case finishRead + // will be invoked in the same thread as startRead. The caller must check + // the buffer after finishRead completes to determine whether all of the + // requested data has been read. + // + // The read request is canceled upon the termination of the thread that + // calls startRead, or when the socket is closed. In this case finishRead + // raises ReadAbortedException. + // + bool startRead(Buffer buf, AsyncCallback callback, object state); + void finishRead(Buffer buf); + + // + // Write data asynchronously. + // + // The I/O request may complete synchronously, in which case finishWrite + // will be invoked in the same thread as startWrite. The request + // will be canceled upon the termination of the thread that calls startWrite. + // + bool startWrite(Buffer buf, AsyncCallback callback, object state, out bool completed); + void finishWrite(Buffer buf); + + string protocol(); + string toDetailedString(); + Ice.ConnectionInfo getInfo(); + void checkSendSize(Buffer buf); + void setBufferSize(int rcvSize, int sndSize); + } + +} diff --git a/csharp/src/Ice/UdpConnector.cs b/csharp/src/Ice/UdpConnector.cs new file mode 100644 index 00000000000..7899a40d396 --- /dev/null +++ b/csharp/src/Ice/UdpConnector.cs @@ -0,0 +1,107 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Net; + using System.Net.Sockets; + + sealed class UdpConnector : Connector + { + public Transceiver connect() + { + return new UdpTransceiver(_instance, _addr, _sourceAddr, _mcastInterface, _mcastTtl); + } + + public short type() + { + return _instance.type(); + } + + // + // Only for use by UdpEndpointI + // + internal UdpConnector(ProtocolInstance instance, EndPoint addr, EndPoint sourceAddr, string mcastInterface, + int mcastTtl, string connectionId) + { + _instance = instance; + _addr = addr; + _sourceAddr = sourceAddr; + _mcastInterface = mcastInterface; + _mcastTtl = mcastTtl; + _connectionId = connectionId; + + _hashCode = 5381; + IceInternal.HashUtil.hashAdd(ref _hashCode, _addr); + if(sourceAddr != null) + { + IceInternal.HashUtil.hashAdd(ref _hashCode, _sourceAddr); + } + IceInternal.HashUtil.hashAdd(ref _hashCode, _mcastInterface); + IceInternal.HashUtil.hashAdd(ref _hashCode, _mcastTtl); + IceInternal.HashUtil.hashAdd(ref _hashCode, _connectionId); + } + + public override bool Equals(object obj) + { + if(!(obj is UdpConnector)) + { + return false; + } + + if(this == obj) + { + return true; + } + + UdpConnector p = (UdpConnector)obj; + if(!_connectionId.Equals(p._connectionId)) + { + return false; + } + + if(!_mcastInterface.Equals(p._mcastInterface)) + { + return false; + } + + if(_mcastTtl != p._mcastTtl) + { + return false; + } + + if(!Network.addressEquals(_sourceAddr, p._sourceAddr)) + { + return false; + } + + return _addr.Equals(p._addr); + } + + public override string ToString() + { + return Network.addrToString(_addr); + } + + public override int GetHashCode() + { + return _hashCode; + } + + private ProtocolInstance _instance; + private EndPoint _addr; + private EndPoint _sourceAddr; + private string _mcastInterface; + private int _mcastTtl; + private string _connectionId; + private int _hashCode; + } +} diff --git a/csharp/src/Ice/UdpEndpointI.cs b/csharp/src/Ice/UdpEndpointI.cs new file mode 100644 index 00000000000..9d28b27c21b --- /dev/null +++ b/csharp/src/Ice/UdpEndpointI.cs @@ -0,0 +1,449 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Diagnostics; + using System.Collections; + using System.Collections.Generic; + using System.Net; + using System; + using System.Globalization; + + sealed class UdpEndpointI : IPEndpointI + { + public UdpEndpointI(ProtocolInstance instance, string ho, int po, EndPoint sourceAddr, string mcastInterface, + int mttl, bool conn, string conId, bool co) : + base(instance, ho, po, sourceAddr, conId) + { + _mcastInterface = mcastInterface; + _mcastTtl = mttl; + _connect = conn; + _compress = co; + } + + public UdpEndpointI(ProtocolInstance instance) : + base(instance) + { + _connect = false; + _compress = false; + } + + public UdpEndpointI(ProtocolInstance instance, BasicStream s) : + base(instance, s) + { + if(s.getReadEncoding().Equals(Ice.Util.Encoding_1_0)) + { + s.readByte(); + s.readByte(); + s.readByte(); + s.readByte(); + } + // Not transmitted. + //_connect = s.readBool(); + _connect = false; + _compress = s.readBool(); + } + + private sealed class InfoI : Ice.UDPEndpointInfo + { + public InfoI(UdpEndpointI e) + { + _endpoint = e; + } + + override public short type() + { + return _endpoint.type(); + } + + override public bool datagram() + { + return _endpoint.datagram(); + } + + override public bool secure() + { + return _endpoint.secure(); + } + + private UdpEndpointI _endpoint; + } + + // + // Return the endpoint information. + // + public override Ice.EndpointInfo getInfo() + { + InfoI info = new InfoI(this); + fillEndpointInfo(info); + return info; + } + + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + public override int timeout() + { + return -1; + } + + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + public override EndpointI timeout(int timeout) + { + return this; + } + + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + public override bool compress() + { + return _compress; + } + + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + public override EndpointI compress(bool compress) + { + if(compress == _compress) + { + return this; + } + else + { + return new UdpEndpointI(instance_, host_, port_, sourceAddr_, _mcastInterface, _mcastTtl, _connect, + connectionId_, compress); + } + } + + // + // Return true if the endpoint is datagram-based. + // + public override bool datagram() + { + return true; + } + + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. + // + public override Transceiver transceiver() + { + return new UdpTransceiver(this, instance_, host_, port_, _mcastInterface, _connect); + } + + // + // Return an acceptor for this endpoint, or null if no acceptors + // is available. + // + public override Acceptor acceptor(string adapterName) + { + return null; + } + + public UdpEndpointI endpoint(UdpTransceiver transceiver) + { + return new UdpEndpointI(instance_, host_, transceiver.effectivePort(), sourceAddr_, _mcastInterface, + _mcastTtl, _connect, connectionId_, _compress); + } + + public override string options() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + string s = base.options(); + + if(_mcastInterface.Length != 0) + { + s += " --interface " + _mcastInterface; + } + + if(_mcastTtl != -1) + { + s += " --ttl " + _mcastTtl; + } + + if(_connect) + { + s += " -c"; + } + + if(_compress) + { + s += " -z"; + } + + return s; + } + + // + // Compare endpoints for sorting purposes + // + public override int CompareTo(EndpointI obj) + { + if(!(obj is UdpEndpointI)) + { + return type() < obj.type() ? -1 : 1; + } + + UdpEndpointI p = (UdpEndpointI)obj; + if(this == p) + { + return 0; + } + + if(!_connect && p._connect) + { + return -1; + } + else if(!p._connect && _connect) + { + return 1; + } + + if(!_compress && p._compress) + { + return -1; + } + else if(!p._compress && _compress) + { + return 1; + } + + int rc = string.Compare(_mcastInterface, p._mcastInterface, StringComparison.Ordinal); + if(rc != 0) + { + return rc; + } + + if(_mcastTtl < p._mcastTtl) + { + return -1; + } + else if(p._mcastTtl < _mcastTtl) + { + return 1; + } + + return base.CompareTo(p); + } + + // + // Marshal the endpoint + // + public override void streamWriteImpl(BasicStream s) + { + base.streamWriteImpl(s); + if(s.getWriteEncoding().Equals(Ice.Util.Encoding_1_0)) + { + Ice.Util.Protocol_1_0.write__(s); + Ice.Util.Encoding_1_0.write__(s); + } + // Not transmitted. + //s.writeBool(_connect); + s.writeBool(_compress); + } + + public override void hashInit(ref int h) + { + base.hashInit(ref h); + IceInternal.HashUtil.hashAdd(ref h, _mcastInterface); + IceInternal.HashUtil.hashAdd(ref h, _mcastTtl); + IceInternal.HashUtil.hashAdd(ref h, _connect); + IceInternal.HashUtil.hashAdd(ref h, _compress); + } + + public override void fillEndpointInfo(Ice.IPEndpointInfo info) + { + base.fillEndpointInfo(info); + if(info is Ice.UDPEndpointInfo) + { + Ice.UDPEndpointInfo udpInfo = (Ice.UDPEndpointInfo)info; + udpInfo.timeout = -1; + udpInfo.compress = _compress; + udpInfo.mcastInterface = _mcastInterface; + udpInfo.mcastTtl = _mcastTtl; + } + } + + protected override bool checkOption(string option, string argument, string endpoint) + { + if(base.checkOption(option, argument, endpoint)) + { + return true; + } + + if(option.Equals("-c")) + { + if(argument != null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "unexpected argument `" + argument + "' provided for -c option in " + endpoint; + throw e; + } + + _connect = true; + } + else if(option.Equals("-z")) + { + if(argument != null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "unexpected argument `" + argument + "' provided for -z option in " + endpoint; + throw e; + } + + _compress = true; + } + else if(option.Equals("-v") || option.Equals("-e")) + { + if(argument == null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "no argument provided for " + option + " option in endpoint " + endpoint; + throw e; + } + + try + { + Ice.EncodingVersion v = Ice.Util.stringToEncodingVersion(argument); + if(v.major != 1 || v.minor != 0) + { + instance_.logger().warning("deprecated udp endpoint option: " + option); + } + } + catch(Ice.VersionParseException ex) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "invalid version `" + argument + "' in endpoint " + endpoint + ":\n" + ex.str; + throw e; + } + } + else if(option.Equals("--ttl")) + { + if(argument == null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "no argument provided for --ttl option in endpoint " + endpoint; + throw e; + } + + try + { + _mcastTtl = System.Int32.Parse(argument, CultureInfo.InvariantCulture); + } + catch(System.FormatException ex) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(ex); + e.str = "invalid TTL value `" + argument + "' in endpoint " + endpoint; + throw e; + } + + if(_mcastTtl < 0) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "TTL value `" + argument + "' out of range in endpoint " + endpoint; + throw e; + } + } + else if(option.Equals("--interface")) + { + if(argument == null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "no argument provided for --interface option in endpoint " + endpoint; + throw e; + } + _mcastInterface = argument; + } + else + { + return false; + } + + return true; + } + + protected override Connector createConnector(EndPoint addr, NetworkProxy proxy) + { + return new UdpConnector(instance_, addr, sourceAddr_, _mcastInterface, _mcastTtl, connectionId_); + } + + protected override IPEndpointI createEndpoint(string host, int port, string connectionId) + { + return new UdpEndpointI(instance_, host, port, sourceAddr_, _mcastInterface, _mcastTtl, _connect, + connectionId, _compress); + } + + private string _mcastInterface = ""; + private int _mcastTtl = -1; + private bool _connect; + private bool _compress; + } + + sealed class UdpEndpointFactory : EndpointFactory + { + internal UdpEndpointFactory(ProtocolInstance instance) + { + _instance = instance; + } + + public short type() + { + return _instance.type(); + } + + public string protocol() + { + return _instance.protocol(); + } + + public EndpointI create(List<string> args, bool oaEndpoint) + { + IPEndpointI endpt = new UdpEndpointI(_instance); + endpt.initWithOptions(args, oaEndpoint); + return endpt; + } + + public EndpointI read(BasicStream s) + { + return new UdpEndpointI(_instance, s); + } + + public void destroy() + { + _instance = null; + } + + public EndpointFactory clone(ProtocolInstance instance) + { + return new UdpEndpointFactory(instance); + } + + private ProtocolInstance _instance; + } + +} diff --git a/csharp/src/Ice/UdpTransceiver.cs b/csharp/src/Ice/UdpTransceiver.cs new file mode 100644 index 00000000000..e9a7531340a --- /dev/null +++ b/csharp/src/Ice/UdpTransceiver.cs @@ -0,0 +1,1120 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// .NET and Silverlight use the new socket asynchronous APIs whereas +// the compact framework and mono still use the old Begin/End APIs. +// +#if !COMPACT && !__MonoCS__ && !UNITY +#define ICE_SOCKET_ASYNC_API +#endif + +namespace IceInternal +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Diagnostics; + using System.Net; + using System.Net.Sockets; + using System.Text; + + sealed class UdpTransceiver : Transceiver + { + public Socket fd() + { + return _fd; + } + + public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData) + { + if(_state == StateNeedConnect) + { + _state = StateConnectPending; +#if ICE_SOCKET_ASYNC_API && !SILVERLIGHT + try + { + if(_sourceAddr != null) + { + _fd.Bind(_sourceAddr); + } + _fd.Connect(_addr); + } + catch(SocketException ex) + { + if(Network.wouldBlock(ex)) + { + return SocketOperation.Connect; + } + throw new Ice.ConnectFailedException(ex); + } + catch(Exception ex) + { + throw new Ice.ConnectFailedException(ex); + } +#else + return SocketOperation.Connect; +#endif + } + + if(_state <= StateConnectPending) + { +#if !SILVERLIGHT + if(!AssemblyUtil.osx_) + { + // + // On Windows, we delay the join for the mcast group after the connection + // establishment succeeds. This is necessary for older Windows versions + // where joining the group fails if the socket isn't bound. See ICE-5113. + // + if(Network.isMulticast((IPEndPoint)_addr)) + { + Network.setMcastGroup(_fd, ((IPEndPoint)_addr).Address, _mcastInterface); + if(_mcastTtl != -1) + { + Network.setMcastTtl(_fd, _mcastTtl, _addr.AddressFamily); + } + } + } +#endif + _state = StateConnected; + } + + Debug.Assert(_state >= StateConnected); + return SocketOperation.None; + } + + public int closing(bool initiator, Ice.LocalException ex) + { + // + // Nothing to do. + // + return SocketOperation.None; + } + + public void close() + { + if(_fd != null) + { + try + { + _fd.Close(); + } + catch(System.IO.IOException) + { + } + _fd = null; + } + } + + public EndpointI bind() + { +#if !SILVERLIGHT + if(Network.isMulticast((IPEndPoint)_addr)) + { + Network.setReuseAddress(_fd, true); + _mcastAddr = (IPEndPoint)_addr; + if(AssemblyUtil.platform_ == AssemblyUtil.Platform.Windows) + { + // + // Windows does not allow binding to the mcast address itself + // so we bind to INADDR_ANY (0.0.0.0) instead. As a result, + // bi-directional connection won't work because the source + // address won't the multicast address and the client will + // therefore reject the datagram. + // + if(_addr.AddressFamily == AddressFamily.InterNetwork) + { + _addr = new IPEndPoint(IPAddress.Any, _port); + } + else + { + _addr = new IPEndPoint(IPAddress.IPv6Any, _port); + } + } + _addr = Network.doBind(_fd, _addr); + if(_port == 0) + { + _mcastAddr.Port = ((IPEndPoint)_addr).Port; + } + Network.setMcastGroup(_fd, _mcastAddr.Address, _mcastInterface); + } + else + { + if(AssemblyUtil.platform_ != AssemblyUtil.Platform.Windows) + { + // + // Enable SO_REUSEADDR on Unix platforms to allow re-using + // the socket even if it's in the TIME_WAIT state. On + // Windows, this doesn't appear to be necessary and + // enabling SO_REUSEADDR would actually not be a good + // thing since it allows a second process to bind to an + // address even it's already bound by another process. + // + // TODO: using SO_EXCLUSIVEADDRUSE on Windows would + // probably be better but it's only supported by recent + // Windows versions (XP SP2, Windows Server 2003). + // + Network.setReuseAddress(_fd, true); + } + _addr = Network.doBind(_fd, _addr); + } +#endif + _bound = true; + _endpoint = _endpoint.endpoint(this); + return _endpoint; + } + + public void destroy() + { +#if ICE_SOCKET_ASYNC_API + _readEventArgs.Dispose(); + _writeEventArgs.Dispose(); +#endif + } + + public int write(Buffer buf) + { + if(!buf.b.hasRemaining()) + { + return SocketOperation.None; + } +#if COMPACT || SILVERLIGHT +# if !ICE_SOCKET_ASYNC_API + if(_writeResult != null) + { + return SocketOperation.None; + } +# endif + // + // Silverlight and the Compact .NET Framework don't support the use of synchronous socket + // operations on a non-blocking socket. Returning SocketOperation.Write here forces the + // caller to schedule an asynchronous operation. + // + return SocketOperation.Write; +#else + Debug.Assert(buf.b.position() == 0); + Debug.Assert(_fd != null && _state >= StateConnected); + + // The caller is supposed to check the send size before by calling checkSendSize + Debug.Assert(System.Math.Min(_maxPacketSize, _sndSize - _udpOverhead) >= buf.size()); + + int ret = 0; + while(true) + { + try + { + if(_state == StateConnected) + { + ret = _fd.Send(buf.b.rawBytes(), 0, buf.size(), SocketFlags.None); + } + else + { + if(_peerAddr == null) + { + throw new Ice.SocketException(); + } + ret = _fd.SendTo(buf.b.rawBytes(), 0, buf.size(), SocketFlags.None, _peerAddr); + } + break; + } + catch(SocketException ex) + { + if(Network.interrupted(ex)) + { + continue; + } + + if(Network.wouldBlock(ex)) + { + return SocketOperation.Write; + } + + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + else + { + throw new Ice.SocketException(ex); + } + } + catch(System.Exception e) + { + throw new Ice.SyscallException(e); + } + } + + Debug.Assert(ret > 0); + Debug.Assert(ret == buf.b.limit()); + buf.b.position(buf.b.limit()); + return SocketOperation.None; +#endif + } + + public int read(Buffer buf, ref bool hasMoreData) + { + if(!buf.b.hasRemaining()) + { + return SocketOperation.None; + } +#if COMPACT || SILVERLIGHT + // + // Silverlight and the Compact .NET Framework don't support the use of synchronous socket + // operations on a non-blocking socket. Returning SocketOperation.Read here forces the + // caller to schedule an asynchronous operation. + // + return SocketOperation.Read; +#else + Debug.Assert(buf.b.position() == 0); + Debug.Assert(_fd != null); + + int packetSize = System.Math.Min(_maxPacketSize, _rcvSize - _udpOverhead); + buf.resize(packetSize, true); + buf.b.position(0); + + int ret = 0; + while(true) + { + try + { + EndPoint peerAddr = _peerAddr; + if(peerAddr == null) + { + if(_addr.AddressFamily == AddressFamily.InterNetwork) + { + peerAddr = new IPEndPoint(IPAddress.Any, 0); + } + else + { + Debug.Assert(_addr.AddressFamily == AddressFamily.InterNetworkV6); + peerAddr = new IPEndPoint(IPAddress.IPv6Any, 0); + } + } + + if(_state == StateConnected) + { + ret = _fd.Receive(buf.b.rawBytes(), 0, buf.b.limit(), SocketFlags.None); + } + else + { + ret = _fd.ReceiveFrom(buf.b.rawBytes(), 0, buf.b.limit(), SocketFlags.None, ref peerAddr); + _peerAddr = (IPEndPoint)peerAddr; + } + break; + } + catch(SocketException e) + { + if(Network.recvTruncated(e)) + { + // The message was truncated and the whole buffer is filled. We ignore + // this error here, it will be detected at the connection level when + // the Ice message size is checked against the buffer size. + ret = buf.size(); + break; + } + + if(Network.interrupted(e)) + { + continue; + } + + if(Network.wouldBlock(e)) + { + return SocketOperation.Read; + } + + if(Network.connectionLost(e)) + { + throw new Ice.ConnectionLostException(); + } + else + { + throw new Ice.SocketException(e); + } + } + catch(System.Exception e) + { + throw new Ice.SyscallException(e); + } + } + + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + + if(_state == StateNeedConnect) + { + Debug.Assert(_incoming); + + // + // If we must connect, then we connect to the first peer that sends us a packet. + // + bool connected = Network.doConnect(_fd, _peerAddr, null); + Debug.Assert(connected); + _state = StateConnected; // We're connected now + + if(_instance.traceLevel() >= 1) + { + string s = "connected " + protocol() + " socket\n" + ToString(); + _instance.logger().trace(_instance.traceCategory(), s); + } + } + + buf.resize(ret, true); + buf.b.position(ret); + + return SocketOperation.None; +#endif + } + + public bool startRead(Buffer buf, AsyncCallback callback, object state) + { + Debug.Assert(buf.b.position() == 0); + + int packetSize = System.Math.Min(_maxPacketSize, _rcvSize - _udpOverhead); + buf.resize(packetSize, true); + buf.b.position(0); + + try + { + if(_state == StateConnected) + { + _readCallback = callback; +#if ICE_SOCKET_ASYNC_API + _readEventArgs.UserToken = state; + _readEventArgs.SetBuffer(buf.b.rawBytes(), buf.b.position(), packetSize); + return !_fd.ReceiveAsync(_readEventArgs); +#else + _readResult = _fd.BeginReceive(buf.b.rawBytes(), 0, buf.b.limit(), SocketFlags.None, + readCompleted, state); + return _readResult.CompletedSynchronously; +#endif + } + else + { + Debug.Assert(_incoming); + _readCallback = callback; +#if ICE_SOCKET_ASYNC_API + _readEventArgs.UserToken = state; + _readEventArgs.SetBuffer(buf.b.rawBytes(), 0, buf.b.limit()); + return !_fd.ReceiveFromAsync(_readEventArgs); +#else + EndPoint peerAddr = _peerAddr; + if(peerAddr == null) + { + if(_addr.AddressFamily == AddressFamily.InterNetwork) + { + peerAddr = new IPEndPoint(IPAddress.Any, 0); + } + else + { + Debug.Assert(_addr.AddressFamily == AddressFamily.InterNetworkV6); + peerAddr = new IPEndPoint(IPAddress.IPv6Any, 0); + } + } + _readResult = _fd.BeginReceiveFrom(buf.b.rawBytes(), 0, buf.b.limit(), SocketFlags.None, + ref peerAddr, readCompleted, state); + return _readResult.CompletedSynchronously; +#endif + } + } + catch(SocketException ex) + { + if(Network.recvTruncated(ex)) + { + // Nothing todo + return true; + } + else + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + else + { + throw new Ice.SocketException(ex); + } + } + } + } + + public void finishRead(Buffer buf) + { + if(_fd == null) + { + return; + } + + int ret; + try + { +#if ICE_SOCKET_ASYNC_API + if(_readEventArgs.SocketError != SocketError.Success) + { + throw new SocketException((int)_readEventArgs.SocketError); + } + ret = _readEventArgs.BytesTransferred; + if(_state != StateConnected) + { + _peerAddr = _readEventArgs.RemoteEndPoint; + } +#else + Debug.Assert(_readResult != null); + if(_state == StateConnected) + { + ret = _fd.EndReceive(_readResult); + } + else + { + EndPoint peerAddr = _peerAddr; + if(_addr.AddressFamily == AddressFamily.InterNetwork) + { + peerAddr = new IPEndPoint(IPAddress.Any, 0); + } + else + { + Debug.Assert(_addr.AddressFamily == AddressFamily.InterNetworkV6); + peerAddr = new IPEndPoint(IPAddress.IPv6Any, 0); + } + ret = _fd.EndReceiveFrom(_readResult, ref peerAddr); + _peerAddr = (IPEndPoint)peerAddr; + } + _readResult = null; +#endif + } + catch(SocketException ex) + { + if(Network.recvTruncated(ex)) + { + // The message was truncated and the whole buffer is filled. We ignore + // this error here, it will be detected at the connection level when + // the Ice message size is checked against the buffer size. + ret = buf.size(); + } + else + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + + if(Network.connectionRefused(ex)) + { + throw new Ice.ConnectionRefusedException(ex); + } + else + { + throw new Ice.SocketException(ex); + } + } + } + + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + + Debug.Assert(ret > 0); + + if(_state == StateNeedConnect) + { + Debug.Assert(_incoming); + + // + // If we must connect, then we connect to the first peer that + // sends us a packet. + // +#if ICE_SOCKET_ASYNC_API + bool connected = !_fd.ConnectAsync(_readEventArgs); +#else + bool connected = Network.doConnect(_fd, _peerAddr, null); +#endif + Debug.Assert(connected); + _state = StateConnected; // We're connected now + + if(_instance.traceLevel() >= 1) + { + string s = "connected " + protocol() + " socket\n" + ToString(); + _instance.logger().trace(_instance.traceCategory(), s); + } + } + + buf.resize(ret, true); + buf.b.position(ret); + } + + public bool startWrite(Buffer buf, AsyncCallback callback, object state, out bool completed) + { + if(!_incoming && _state < StateConnected) + { + Debug.Assert(_addr != null); + completed = false; +#if ICE_SOCKET_ASYNC_API +# if !SILVERLIGHT + if(_sourceAddr != null) + { + _fd.Bind(_sourceAddr); + } +# endif + _writeEventArgs.UserToken = state; + return !_fd.ConnectAsync(_writeEventArgs); +#else + _writeResult = Network.doConnectAsync(_fd, _addr, _sourceAddr, callback, state); + return _writeResult.CompletedSynchronously; +#endif + } + + Debug.Assert(_fd != null); + + // The caller is supposed to check the send size before by calling checkSendSize + Debug.Assert(System.Math.Min(_maxPacketSize, _sndSize - _udpOverhead) >= buf.size()); + + Debug.Assert(buf.b.position() == 0); + + bool completedSynchronously; + try + { + _writeCallback = callback; + + if(_state == StateConnected) + { +#if ICE_SOCKET_ASYNC_API + _writeEventArgs.UserToken = state; + _writeEventArgs.SetBuffer(buf.b.rawBytes(), 0, buf.b.limit()); + completedSynchronously = !_fd.SendAsync(_writeEventArgs); +#else + _writeResult = _fd.BeginSend(buf.b.rawBytes(), 0, buf.b.limit(), SocketFlags.None, + writeCompleted, state); + completedSynchronously = _writeResult.CompletedSynchronously; +#endif + } + else + { + if(_peerAddr == null) + { + throw new Ice.SocketException(); + } +#if ICE_SOCKET_ASYNC_API + _writeEventArgs.RemoteEndPoint = _peerAddr; + _writeEventArgs.UserToken = state; + _writeEventArgs.SetBuffer(buf.b.rawBytes(), 0, buf.b.limit()); + completedSynchronously = !_fd.SendToAsync(_writeEventArgs); +#else + _writeResult = _fd.BeginSendTo(buf.b.rawBytes(), 0, buf.b.limit(), SocketFlags.None, _peerAddr, + writeCompleted, state); + completedSynchronously = _writeResult.CompletedSynchronously; +#endif + } + } + catch(SocketException ex) + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + else + { + throw new Ice.SocketException(ex); + } + } + + completed = true; + return completedSynchronously; + } + + public void finishWrite(Buffer buf) + { + if(_fd == null) + { + buf.b.position(buf.size()); // Assume all the data was sent for at-most-once semantics. +#if ICE_SOCKET_ASYNC_API + _writeEventArgs = null; +#else + _writeResult = null; +#endif + return; + } + + if(!_incoming && _state < StateConnected) + { +#if ICE_SOCKET_ASYNC_API + if(_writeEventArgs.SocketError != SocketError.Success) + { + SocketException ex = new SocketException((int)_writeEventArgs.SocketError); + if(Network.connectionRefused(ex)) + { + throw new Ice.ConnectionRefusedException(ex); + } + else + { + throw new Ice.ConnectFailedException(ex); + } + } +#else + Debug.Assert(_writeResult != null); + Network.doFinishConnectAsync(_fd, _writeResult); + _writeResult = null; +#endif + return; + } + + int ret; + try + { +#if ICE_SOCKET_ASYNC_API + if(_writeEventArgs.SocketError != SocketError.Success) + { + throw new SocketException((int)_writeEventArgs.SocketError); + } + ret = _writeEventArgs.BytesTransferred; +#else + if (_state == StateConnected) + { + ret = _fd.EndSend(_writeResult); + } + else + { + ret = _fd.EndSendTo(_writeResult); + } + _writeResult = null; +#endif + } + catch(SocketException ex) + { + if(Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + else + { + throw new Ice.SocketException(ex); + } + } + + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + + Debug.Assert(ret > 0); + Debug.Assert(ret == buf.b.limit()); + buf.b.position(buf.b.position() + ret); + } + + public string protocol() + { + return _instance.protocol(); + } + + public Ice.ConnectionInfo getInfo() + { + Ice.UDPConnectionInfo info = new Ice.UDPConnectionInfo(); + if(_fd != null) + { + EndPoint localEndpoint = Network.getLocalAddress(_fd); + info.localAddress = Network.endpointAddressToString(localEndpoint); + info.localPort = Network.endpointPort(localEndpoint); + if(_state == StateNotConnected) + { + if(_peerAddr != null) + { + info.remoteAddress = Network.endpointAddressToString(_peerAddr); + info.remotePort = Network.endpointPort(_peerAddr); + } + } + else + { + EndPoint remoteEndpoint = Network.getRemoteAddress(_fd); + if(remoteEndpoint != null) + { + info.remoteAddress = Network.endpointAddressToString(remoteEndpoint); + info.remotePort = Network.endpointPort(remoteEndpoint); + } + } + } + + info.rcvSize = Network.getRecvBufferSize(_fd); + info.sndSize = Network.getSendBufferSize(_fd); + +#if !SILVERLIGHT + if(_mcastAddr != null) + { + info.mcastAddress = Network.endpointAddressToString(_mcastAddr); + info.mcastPort = Network.endpointPort(_mcastAddr); + } +#endif + return info; + } + + public void checkSendSize(Buffer buf) + { + // + // The maximum packetSize is either the maximum allowable UDP packet size, or + // the UDP send buffer size (which ever is smaller). + // + int packetSize = System.Math.Min(_maxPacketSize, _sndSize - _udpOverhead); + if(packetSize < buf.size()) + { + throw new Ice.DatagramLimitException(); + } + } + + public void setBufferSize(int rcvSize, int sndSize) + { + setBufSize(rcvSize, sndSize); + } + + public override string ToString() + { + if(_fd == null) + { + return "<closed>"; + } + + string s; + if(_incoming && !_bound) + { + s = "local address = " + Network.addrToString(_addr); + } + else if(_state == StateNotConnected) + { + s = "local address = " + Network.localAddrToString(Network.getLocalAddress(_fd)); + if(_peerAddr != null) + { + s += "\nremote address = " + Network.addrToString(_peerAddr); + } + } + else + { + s = Network.fdToString(_fd); + } + +#if !SILVERLIGHT + if(_mcastAddr != null) + { + s += "\nmulticast address = " + Network.addrToString(_mcastAddr); + } +#endif + return s; + } + + public string toDetailedString() + { + StringBuilder s = new StringBuilder(ToString()); + List<string> intfs = Network.getHostsForEndpointExpand( + Network.endpointAddressToString(_addr), _instance.protocolSupport(), true); + if(intfs.Count != 0) + { + s.Append("\nlocal interfaces = "); + s.Append(String.Join(", ", intfs.ToArray())); + } + return s.ToString(); + } + + public int effectivePort() + { + return Network.endpointPort(_addr); + } + + // + // Only for use by UdpConnector. + // + internal UdpTransceiver(ProtocolInstance instance, EndPoint addr, EndPoint sourceAddr, string mcastInterface, + int mcastTtl) + { + _instance = instance; + _addr = addr; + _sourceAddr = sourceAddr; + +#if ICE_SOCKET_ASYNC_API + _readEventArgs = new SocketAsyncEventArgs(); + _readEventArgs.RemoteEndPoint = _addr; + _readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ioCompleted); + + _writeEventArgs = new SocketAsyncEventArgs(); + _writeEventArgs.RemoteEndPoint = _addr; + _writeEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ioCompleted); +#if SILVERLIGHT + String policy = instance.properties().getProperty("Ice.ClientAccessPolicyProtocol"); + if(policy.Equals("Http")) + { + _readEventArgs.SocketClientAccessPolicyProtocol = SocketClientAccessPolicyProtocol.Http; + _writeEventArgs.SocketClientAccessPolicyProtocol = SocketClientAccessPolicyProtocol.Http; + } + else if(!String.IsNullOrEmpty(policy)) + { + _instance.logger().warning("Ignoring invalid Ice.ClientAccessPolicyProtocol value `" + policy + "'"); + } +#endif +#endif + + _mcastInterface = mcastInterface; + _mcastTtl = mcastTtl; + _state = StateNeedConnect; + _incoming = false; + + try + { + _fd = Network.createSocket(true, _addr.AddressFamily); + setBufSize(-1, -1); +#if !SILVERLIGHT + Network.setBlock(_fd, false); + if(AssemblyUtil.osx_) + { + // + // On Windows, we delay the join for the mcast group after the connection + // establishment succeeds. This is necessary for older Windows versions + // where joining the group fails if the socket isn't bound. See ICE-5113. + // + if(Network.isMulticast((IPEndPoint)_addr)) + { + Network.setMcastGroup(_fd, ((IPEndPoint)_addr).Address, _mcastInterface); + if(_mcastTtl != -1) + { + Network.setMcastTtl(_fd, _mcastTtl, _addr.AddressFamily); + } + } + } +#endif + } + catch(Ice.LocalException) + { + _fd = null; + throw; + } + } + + // + // Only for use by UdpEndpoint. + // + internal UdpTransceiver(UdpEndpointI endpoint, ProtocolInstance instance, string host, int port, + string mcastInterface, bool connect) + { + _endpoint = endpoint; + _instance = instance; + _state = connect ? StateNeedConnect : StateNotConnected; + _mcastInterface = mcastInterface; + _incoming = true; + _port = port; + + try + { + _addr = Network.getAddressForServer(host, port, instance.protocolSupport(), instance.preferIPv6()); + +#if ICE_SOCKET_ASYNC_API + _readEventArgs = new SocketAsyncEventArgs(); + _readEventArgs.RemoteEndPoint = _addr; + _readEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ioCompleted); + + _writeEventArgs = new SocketAsyncEventArgs(); + _writeEventArgs.RemoteEndPoint = _addr; + _writeEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(ioCompleted); +#endif + + _fd = Network.createServerSocket(true, _addr.AddressFamily, instance.protocolSupport()); + setBufSize(-1, -1); +#if !SILVERLIGHT + Network.setBlock(_fd, false); +#endif + } + catch(Ice.LocalException) + { +#if ICE_SOCKET_ASYNC_API + if(_readEventArgs != null) + { + _readEventArgs.Dispose(); + } + if(_writeEventArgs != null) + { + _writeEventArgs.Dispose(); + } +#endif + _fd = null; + throw; + } + } + + private void setBufSize(int rcvSize, int sndSize) + { + Debug.Assert(_fd != null); + + for (int i = 0; i < 2; ++i) + { + bool isSnd; + string direction; + string prop; + int dfltSize; + int sizeRequested; + if(i == 0) + { + isSnd = false; + direction = "receive"; + prop = "Ice.UDP.RcvSize"; + dfltSize = Network.getRecvBufferSize(_fd); + sizeRequested = rcvSize; + _rcvSize = dfltSize; + } + else + { + isSnd = true; + direction = "send"; + prop = "Ice.UDP.SndSize"; + dfltSize = Network.getSendBufferSize(_fd); + sizeRequested = sndSize; + _sndSize = dfltSize; + } + + // + // Get property for buffer size if size not passed in. + // + if(sizeRequested == -1) + { + sizeRequested = _instance.properties().getPropertyAsIntWithDefault(prop, dfltSize); + } + // + // Check for sanity. + // + if(sizeRequested < (_udpOverhead + IceInternal.Protocol.headerSize)) + { + _instance.logger().warning("Invalid " + prop + " value of " + sizeRequested + " adjusted to " + + dfltSize); + sizeRequested = dfltSize; + } + + if(sizeRequested != dfltSize) + { + // + // Try to set the buffer size. The kernel will silently adjust + // the size to an acceptable value. Then read the size back to + // get the size that was actually set. + // + int sizeSet; + if(i == 0) + { + Network.setRecvBufferSize(_fd, sizeRequested); + _rcvSize = Network.getRecvBufferSize(_fd); + sizeSet = _rcvSize; + } + else + { + Network.setSendBufferSize(_fd, sizeRequested); + _sndSize = Network.getSendBufferSize(_fd); + sizeSet = _sndSize; + } + + // + // Warn if the size that was set is less than the requested size + // and we have not already warned + // + if(sizeSet < sizeRequested) + { + BufSizeWarnInfo winfo = _instance.getBufSizeWarn(Ice.UDPEndpointType.value); + if((isSnd && (!winfo.sndWarn || winfo.sndSize != sizeRequested)) || + (!isSnd && (!winfo.rcvWarn || winfo.rcvSize != sizeRequested))) + { + _instance.logger().warning("UDP " + direction + " buffer size: requested size of " + + sizeRequested + " adjusted to " + sizeSet); + + if(isSnd) + { + _instance.setSndBufSizeWarn(Ice.UDPEndpointType.value, sizeRequested); + } + else + { + _instance.setRcvBufSizeWarn(Ice.UDPEndpointType.value, sizeRequested); + } + } + } + } + } + } + +#if ICE_SOCKET_ASYNC_API + internal void ioCompleted(object sender, SocketAsyncEventArgs e) + { + switch (e.LastOperation) + { + case SocketAsyncOperation.Receive: +#if !SILVERLIGHT + case SocketAsyncOperation.ReceiveFrom: +#endif + _readCallback(e.UserToken); + break; + case SocketAsyncOperation.Send: + case SocketAsyncOperation.Connect: + _writeCallback(e.UserToken); + break; + default: + throw new ArgumentException("The last operation completed on the socket was not a receive or send"); + } + } +#else + internal void readCompleted(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + _readCallback(result.AsyncState); + } + } + + internal void writeCompleted(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + _writeCallback(result.AsyncState); + } + } +#endif + + private UdpEndpointI _endpoint; + private ProtocolInstance _instance; + private int _state; + private bool _incoming; + private int _rcvSize; + private int _sndSize; + private Socket _fd; + private EndPoint _addr; + private EndPoint _sourceAddr; +#if !SILVERLIGHT + private IPEndPoint _mcastAddr = null; +#endif + private EndPoint _peerAddr = null; + private string _mcastInterface = null; + private int _mcastTtl = -1; + + private int _port = 0; + private bool _bound = false; + +#if ICE_SOCKET_ASYNC_API + private SocketAsyncEventArgs _writeEventArgs; + private SocketAsyncEventArgs _readEventArgs; +#else + private IAsyncResult _writeResult; + private IAsyncResult _readResult; +#endif + + AsyncCallback _writeCallback; + AsyncCallback _readCallback; + + private const int StateNeedConnect = 0; + private const int StateConnectPending = 1; + private const int StateConnected = 2; + private const int StateNotConnected = 3; + + // + // The maximum IP datagram size is 65535. Subtract 20 bytes for the IP header and 8 bytes for the UDP header + // to get the maximum payload. + // + private const int _udpOverhead = 20 + 8; + private const int _maxPacketSize = 65535 - _udpOverhead; + } +} diff --git a/csharp/src/Ice/UnknownSlicedObject.cs b/csharp/src/Ice/UnknownSlicedObject.cs new file mode 100644 index 00000000000..dcdc098280c --- /dev/null +++ b/csharp/src/Ice/UnknownSlicedObject.cs @@ -0,0 +1,50 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace Ice +{ + /// <summary> + /// Unknown sliced object holds an instance of unknown type. + /// </summary> + public sealed class UnknownSlicedObject : ObjectImpl + { + /// <summary> + /// Instantiates the class for an Ice object having the given Slice type. + /// </summary> + /// <param name="unknownTypeId">The Slice type ID of the unknown object.</param> + public UnknownSlicedObject(string unknownTypeId) + { + _unknownTypeId = unknownTypeId; + } + + /// <summary> + /// Determine the Slice type ID associated with this object. + /// </summary> + /// <returns>The type ID.</returns> + public string getUnknownTypeId() + { + return _unknownTypeId; + } + + public override void write__(IceInternal.BasicStream os__) + { + os__.startWriteObject(_slicedData); + os__.endWriteObject(); + } + + public override void read__(IceInternal.BasicStream is__) + { + is__.startReadObject(); + _slicedData = is__.endReadObject(true); + } + + private string _unknownTypeId; + private SlicedData _slicedData; + } +} diff --git a/csharp/src/Ice/UserExceptionFactory.cs b/csharp/src/Ice/UserExceptionFactory.cs new file mode 100644 index 00000000000..e8426a904a2 --- /dev/null +++ b/csharp/src/Ice/UserExceptionFactory.cs @@ -0,0 +1,19 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + public interface UserExceptionFactory + { + void createAndThrow(string typeId); + void destroy(); + } + +} diff --git a/csharp/src/Ice/Util.cs b/csharp/src/Ice/Util.cs new file mode 100644 index 00000000000..3f97251a2ee --- /dev/null +++ b/csharp/src/Ice/Util.cs @@ -0,0 +1,816 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Threading; +using System.Collections; +using System.Text; +using System.Globalization; + +namespace Ice +{ + /// <summary> + /// Interface for thread notification hooks. Applications can derive + /// a class tat implements the start and stop + /// methods to intercept creation and destruction of threads created + /// by the Ice run time. + /// </summary> + public interface ThreadNotification + { + /// <summary> + /// The Ice run time calls start for each new + /// thread it creates. The call is made by the newly-started thread. + /// </summary> + void start(); + + /// <summary> + /// The Ice run time calls stop before it destroys + /// a thread. The call is made by thread that is about to be + /// destroyed. + /// </summary> + void stop(); + } + +#if COMPACT + /// <summary> + /// A delegate for an action taking no parameters. + /// </summary> + public delegate void VoidAction(); +#endif + + /// <summary> + /// A delegate for the dispatcher. The dispatcher is called by the Ice + /// runtime to dispatch servant calls and AMI callbacks. + /// </summary> +#if COMPACT + public delegate void Dispatcher(VoidAction call, Connection con); +#else + public delegate void Dispatcher(System.Action call, Connection con); +#endif + + /// <summary> + /// Applications that make use of compact type IDs to conserve space + /// when marshaling class instances, and also use the streaming API to + /// extract such classes, can intercept the translation between compact + /// type IDs and their corresponding string type IDs by installing an + /// instance of CompactIdResolver in InitializationData. + /// </summary> + public delegate string CompactIdResolver(int id); + + /// <summary> + /// A class that encpasulates data to initialize a communicator. + /// </summary> + public class InitializationData : ICloneable + { + /// <summary> + /// Creates and returns a copy of this object. + /// </summary> + public System.Object Clone() + { + // + // A member-wise copy is safe because the members are immutable. + // + return MemberwiseClone(); + } + + /// <summary> + /// The properties for the communicator. + /// </summary> + public Properties properties; + + /// <summary> + /// The logger for the communicator. + /// </summary> + public Logger logger; + + /// <summary> + /// The communicator observer used by the Ice run-time. + /// </summary> + public Ice.Instrumentation.CommunicatorObserver observer; + + /// <summary> + /// The thread hook for the communicator. + /// </summary> + public ThreadNotification threadHook; + + /// <summary> + /// The dispatcher for the communicator. + /// </summary> + public Dispatcher dispatcher; + + /// <summary> + /// The compact type ID resolver. + /// </summary> + public CompactIdResolver compactIdResolver; + + /// <summary> + /// The batch request interceptor. + /// </summary> + public BatchRequestInterceptor batchRequestInterceptor; + } + + /// <summary> + /// Utility methods for the Ice run time. + /// </summary> + public sealed class Util + { + /// <summary> + /// Creates a new empty property set. + /// </summary> + /// <returns>A new empty property set.</returns> + public static Properties createProperties() + { + return new PropertiesI(); + } + + /// <summary> + /// Creates a property set initialized from an argument vector. + /// </summary> + /// <param name="args">A command-line argument vector, possibly containing + /// options to set properties. If the command-line options include + /// a --Ice.Config option, the corresponding configuration + /// files are parsed. If the same property is set in a configuration + /// file and in the argument vector, the argument vector takes precedence. + /// This method modifies the argument vector by removing any Ice-related options.</param> + /// <returns>A property set initialized with the property settings + /// that were removed from args.</returns> + public static Properties createProperties(ref string[] args) + { + return new PropertiesI(ref args, null); + } + + /// <summary> + /// Creates a property set initialized from an argument vector. + /// </summary> + /// <param name="args">A command-line argument vector, possibly containing + /// options to set properties. If the command-line options include + /// a --Ice.Config option, the corresponding configuration + /// files are parsed. If the same property is set in a configuration + /// file and in the argument vector, the argument vector takes precedence. + /// This method modifies the argument vector by removing any Ice-related options.</param> + /// <param name="defaults">Default values for the property set. Settings in configuration + /// files and args override these defaults.</param> + /// <returns>A property set initialized with the property settings + /// that were removed from args.</returns> + public static Properties createProperties(ref string[] args, Properties defaults) + { + return new PropertiesI(ref args, defaults); + } + + /// <summary> + /// Creates a communicator. + /// </summary> + /// <param name="args">A command-line argument vector. Any Ice-related options + /// in this vector are used to intialize the communicator. + /// This method modifies the argument vector by removing any Ice-related options.</param> + /// <returns>The initialized communicator.</returns> + public static Communicator initialize(ref string[] args) + { + return initialize(ref args, null); + } + + /// <summary> + /// Creates a communicator. + /// </summary> + /// <param name="args">A command-line argument vector. Any Ice-related options + /// in this vector are used to intialize the communicator. + /// This method modifies the argument vector by removing any Ice-related options.</param> + /// <param name="initData">Additional intialization data. Property settings in args + /// override property settings in initData.</param> + /// <returns>The initialized communicator.</returns> + public static Communicator initialize(ref string[] args, InitializationData initData) + { + if(initData == null) + { + initData = new InitializationData(); + } + else + { + initData = (InitializationData)initData.Clone(); + } + + initData.properties = createProperties(ref args, initData.properties); + + CommunicatorI result = new CommunicatorI(initData); + result.finishSetup(ref args); + return result; + } + + /// <summary> + /// Creates a communicator. + /// </summary> + /// <param name="initData">Additional intialization data.</param> + /// <returns>The initialized communicator.</returns> + public static Communicator initialize(InitializationData initData) + { + if(initData == null) + { + initData = new InitializationData(); + } + else + { + initData = (InitializationData)initData.Clone(); + } + + CommunicatorI result = new CommunicatorI(initData); + string[] args = new string[0]; + result.finishSetup(ref args); + return result; + } + + /// <summary> + /// Creates a communicator using a default configuration. + /// </summary> + public static Communicator initialize() + { + return initialize(null); + } + + /// <summary> + /// Converts a string to an object identity. + /// </summary> + /// <param name="s">The string to convert.</param> + /// <returns>The converted object identity.</returns> + public static Identity stringToIdentity(string s) + { + Identity ident = new Identity(); + + // + // Find unescaped separator; note that the string may contain an escaped + // backslash before the separator. + // + int slash = -1, pos = 0; + while((pos = s.IndexOf((System.Char) '/', pos)) != -1) + { + int escapes = 0; + while(pos - escapes > 0 && s[pos - escapes - 1] == '\\') + { + escapes++; + } + + // + // We ignore escaped escapes + // + if(escapes % 2 == 0) + { + if(slash == -1) + { + slash = pos; + } + else + { + // + // Extra unescaped slash found. + // + IdentityParseException ex = new IdentityParseException(); + ex.str = "unescaped backslash in identity `" + s + "'"; + throw ex; + } + } + pos++; + } + + if(slash == -1) + { + ident.category = ""; + try + { + ident.name = IceUtilInternal.StringUtil.unescapeString(s, 0, s.Length); + } + catch(System.ArgumentException e) + { + IdentityParseException ex = new IdentityParseException(); + ex.str = "invalid identity name `" + s + "': " + e.Message; + throw ex; + } + } + else + { + try + { + ident.category = IceUtilInternal.StringUtil.unescapeString(s, 0, slash); + } + catch(System.ArgumentException e) + { + IdentityParseException ex = new IdentityParseException(); + ex.str = "invalid category in identity `" + s + "': " + e.Message; + throw ex; + } + if(slash + 1 < s.Length) + { + try + { + ident.name = IceUtilInternal.StringUtil.unescapeString(s, slash + 1, s.Length); + } + catch(System.ArgumentException e) + { + IdentityParseException ex = new IdentityParseException(); + ex.str = "invalid name in identity `" + s + "': " + e.Message; + throw ex; + } + } + else + { + ident.name = ""; + } + } + + return ident; + } + + /// <summary> + /// Converts an object identity to a string. + /// </summary> + /// <param name="ident">The object identity to convert.</param> + /// <returns>The string representation of the object identity.</returns> + public static string identityToString(Identity ident) + { + if(ident.category == null || ident.category.Length == 0) + { + return IceUtilInternal.StringUtil.escapeString(ident.name, "/"); + } + else + { + return IceUtilInternal.StringUtil.escapeString(ident.category, "/") + '/' + + IceUtilInternal.StringUtil.escapeString(ident.name, "/"); + } + } + + /// <summary> + /// This method is deprecated. Use System.Guid instead. + /// </summary> + [Obsolete("This method is deprecated. Use System.Guid instead.")] + public static string generateUUID() + { + return Guid.NewGuid().ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture); + } + + /// <summary> + /// Compares the object identities of two proxies. + /// </summary> + /// <param name="lhs">A proxy.</param> + /// <param name="rhs">A proxy.</param> + /// <returns>-1 if the identity in lhs compares + /// less than the identity in rhs; 0 if the identities + /// compare equal; 1, otherwise.</returns> + public static int proxyIdentityCompare(ObjectPrx lhs, ObjectPrx rhs) + { + if(lhs == null && rhs == null) + { + return 0; + } + else if(lhs == null && rhs != null) + { + return -1; + } + else if(lhs != null && rhs == null) + { + return 1; + } + else + { + Identity lhsIdentity = lhs.ice_getIdentity(); + Identity rhsIdentity = rhs.ice_getIdentity(); + int n; + n = string.CompareOrdinal(lhsIdentity.name, rhsIdentity.name); + if(n != 0) + { + return n; + } + return string.CompareOrdinal(lhsIdentity.category, rhsIdentity.category); + } + } + + /// <summary> + /// Compares the object identities and facets of two proxies. + /// </summary> + /// <param name="lhs">A proxy.</param> + /// <param name="rhs">A proxy.</param> + /// <returns>-1 if the identity and facet in lhs compare + /// less than the identity and facet in rhs; 0 if the identities + /// and facets compare equal; 1, otherwise.</returns> + public static int proxyIdentityAndFacetCompare(ObjectPrx lhs, ObjectPrx rhs) + { + if(lhs == null && rhs == null) + { + return 0; + } + else if(lhs == null && rhs != null) + { + return -1; + } + else if(lhs != null && rhs == null) + { + return 1; + } + else + { + Identity lhsIdentity = lhs.ice_getIdentity(); + Identity rhsIdentity = rhs.ice_getIdentity(); + int n; + n = string.CompareOrdinal(lhsIdentity.name, rhsIdentity.name); + if(n != 0) + { + return n; + } + n = string.CompareOrdinal(lhsIdentity.category, rhsIdentity.category); + if(n != 0) + { + return n; + } + + string lhsFacet = lhs.ice_getFacet(); + string rhsFacet = rhs.ice_getFacet(); + if(lhsFacet == null && rhsFacet == null) + { + return 0; + } + else if(lhsFacet == null) + { + return -1; + } + else if(rhsFacet == null) + { + return 1; + } + else + { + return string.CompareOrdinal(lhsFacet, rhsFacet); + } + } + } + + /// <summary> + /// Creates an input stream for dynamic invocation and dispatch. The stream uses + /// the communicator's default encoding version. The given data is copied. + /// </summary> + /// <param name="communicator">The communicator for the stream.</param> + /// <param name="bytes">An encoded request or reply.</param> + /// <returns>The input stream.</returns> + public static InputStream createInputStream(Communicator communicator, byte[] bytes) + { + return new InputStreamI(communicator, bytes, true); + } + + /// <summary> + /// Creates an input stream for dynamic invocation and dispatch. The stream uses + /// the given encoding version. + /// </summary> + /// <param name="communicator">The communicator for the stream.</param> + /// <param name="bytes">An encoded request or reply.</param> + /// <param name="v">The desired encoding version.</param> + /// <returns>The input stream.</returns> + public static InputStream createInputStream(Communicator communicator, byte[] bytes, EncodingVersion v) + { + return new InputStreamI(communicator, bytes, v, true); + } + + /// <summary> + /// Wraps encoded data with an input stream for dynamic invocation and dispatch. + /// The stream uses the communicator's default encoding version. + /// </summary> + /// <param name="communicator">The communicator for the stream.</param> + /// <param name="bytes">An encoded request or reply.</param> + /// <returns>The input stream.</returns> + public static InputStream wrapInputStream(Communicator communicator, byte[] bytes) + { + return new InputStreamI(communicator, bytes, false); + } + + /// <summary> + /// Wraps encoded data with an input stream for dynamic invocation and dispatch. + /// The stream uses the given encoding version. + /// </summary> + /// <param name="communicator">The communicator for the stream.</param> + /// <param name="bytes">An encoded request or reply.</param> + /// <param name="v">The desired encoding version.</param> + /// <returns>The input stream.</returns> + public static InputStream wrapInputStream(Communicator communicator, byte[] bytes, EncodingVersion v) + { + return new InputStreamI(communicator, bytes, v, false); + } + + /// <summary> + /// Creates an output stream for dynamic invocation and dispatch. The stream uses + /// the communicator's default encoding version. + /// </summary> + /// <param name="communicator">The communicator for the stream.</param> + /// <returns>The output stream.</returns> + public static OutputStream createOutputStream(Communicator communicator) + { + return new OutputStreamI(communicator); + } + + /// <summary> + /// Creates an output stream for dynamic invocation and dispatch. The stream uses + /// the given encoding version. + /// </summary> + /// <param name="communicator">The communicator for the stream.</param> + /// <param name="v">The desired encoding version.</param> + /// <returns>The output stream.</returns> + public static OutputStream createOutputStream(Communicator communicator, EncodingVersion v) + { + return new OutputStreamI(communicator, v); + } + + /// <summary> + /// Returns the process-wide logger. + /// </summary> + /// <returns>The process-wide logger.</returns> + public static Logger getProcessLogger() + { + lock(_processLoggerMutex) + { + if(_processLogger == null) + { + _processLogger = new ConsoleLoggerI(System.AppDomain.CurrentDomain.FriendlyName); + } + return _processLogger; + } + } + + /// <summary> + /// Changes the process-wide logger. + /// </summary> + /// <param name="logger">The new process-wide logger.</param> + public static void setProcessLogger(Logger logger) + { + lock(_processLoggerMutex) + { + _processLogger = logger; + } + } + + /// <summary> + /// Returns the Ice version in the form A.B.C, where A indicates the + /// major version, B indicates the minor version, and C indicates the + /// patch level. + /// </summary> + /// <returns>The Ice version.</returns> + public static string stringVersion() + { + return "3.6.0"; // "A.B.C", with A=major, B=minor, C=patch + } + + /// <summary> + /// Returns the Ice version as an integer in the form A.BB.CC, where A + /// indicates the major version, BB indicates the minor version, and CC + /// indicates the patch level. For example, for Ice 3.3.1, the returned value is 30301. + /// </summary> + /// <returns>The Ice version.</returns> + public static int intVersion() + { + return 30600; // AABBCC, with AA=major, BB=minor, CC=patch + } + + /// <summary> + /// Converts a string to a protocol version. + /// </summary> + /// <param name="version">The string to convert.</param> + /// <returns>The converted protocol version.</returns> + public static Ice.ProtocolVersion stringToProtocolVersion(string version) + { + byte major, minor; + stringToMajorMinor(version, out major, out minor); + return new Ice.ProtocolVersion(major, minor); + } + + /// <summary> + /// Converts a string to an encoding version. + /// </summary> + /// <param name="version">The string to convert.</param> + /// <returns>The converted object identity.</returns> + public static Ice.EncodingVersion stringToEncodingVersion(string version) + { + byte major, minor; + stringToMajorMinor(version, out major, out minor); + return new Ice.EncodingVersion(major, minor); + } + + /// <summary> + /// Converts a protocol version to a string. + /// </summary> + /// <param name="v">The protocol version to convert.</param> + /// <returns>The converted string.</returns> + public static string protocolVersionToString(Ice.ProtocolVersion v) + { + return majorMinorToString(v.major, v.minor); + } + + /// <summary> + /// Converts an encoding version to a string. + /// </summary> + /// <param name="v">The encoding version to convert.</param> + /// <returns>The converted string.</returns> + public static string encodingVersionToString(Ice.EncodingVersion v) + { + return majorMinorToString(v.major, v.minor); + } + + static private void stringToMajorMinor(string str, out byte major, out byte minor) + { + int pos = str.IndexOf((System.Char) '.'); + if(pos == -1) + { + throw new Ice.VersionParseException("malformed version value `" + str + "'"); + } + + string majStr = str.Substring(0, (pos) - (0)); + string minStr = str.Substring(pos + 1, (str.Length) - (pos + 1)); + int majVersion; + int minVersion; + try + { + majVersion = System.Int32.Parse(majStr, CultureInfo.InvariantCulture); + minVersion = System.Int32.Parse(minStr, CultureInfo.InvariantCulture); + } + catch(System.FormatException) + { + throw new Ice.VersionParseException("invalid version value `" + str + "'"); + } + + if(majVersion < 1 || majVersion > 255 || minVersion < 0 || minVersion > 255) + { + throw new Ice.VersionParseException("range error in version `" + str + "'"); + } + + major = (byte)majVersion; + minor = (byte)minVersion; + } + + static private String majorMinorToString(byte major, byte minor) + { + StringBuilder str = new StringBuilder(); + str.Append(major < 0 ? (int)major + 255 : (int)major); + str.Append("."); + str.Append(minor < 0 ? (int)minor + 255 : (int)minor); + return str.ToString(); + } + + public static readonly ProtocolVersion currentProtocol = + new ProtocolVersion(IceInternal.Protocol.protocolMajor, IceInternal.Protocol.protocolMinor); + + public static readonly EncodingVersion currentProtocolEncoding = + new EncodingVersion(IceInternal.Protocol.protocolEncodingMajor, + IceInternal.Protocol.protocolEncodingMinor); + + public static readonly EncodingVersion currentEncoding = + new EncodingVersion(IceInternal.Protocol.encodingMajor, IceInternal.Protocol.encodingMinor); + + public static readonly ProtocolVersion Protocol_1_0 = new ProtocolVersion(1, 0); + + public static readonly EncodingVersion Encoding_1_0 = new EncodingVersion(1, 0); + public static readonly EncodingVersion Encoding_1_1 = new EncodingVersion(1, 1); + + public static readonly NoneType None = new NoneType(); + + private static object _processLoggerMutex = new object(); + private static Logger _processLogger = null; + } +} + +namespace IceInternal +{ + public sealed class HashUtil + { + public static void hashAdd(ref int hashCode, bool value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ value.GetHashCode()); + } + + public static void hashAdd(ref int hashCode, short value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ (int)(2654435761 * value)); + } + + public static void hashAdd(ref int hashCode, byte value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ (int)(2654435761 * value)); + } + + public static void hashAdd(ref int hashCode, int value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ (int)(2654435761 * value)); + } + + public static void hashAdd(ref int hashCode, long value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ value.GetHashCode()); + } + + public static void hashAdd(ref int hashCode, float value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ value.GetHashCode()); + } + + public static void hashAdd(ref int hashCode, double value) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ value.GetHashCode()); + } + + public static void hashAdd(ref int hashCode, object value) + { + if(value != null) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ value.GetHashCode()); + } + } + + public static void hashAdd(ref int hashCode, object[] arr) + { + if(arr != null) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ IceUtilInternal.Arrays.GetHashCode(arr)); + } + } + + public static void hashAdd(ref int hashCode, Array arr) + { + if(arr != null) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ IceUtilInternal.Arrays.GetHashCode(arr)); + } + } + + public static void hashAdd(ref int hashCode, IEnumerable s) + { + if(s != null) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ IceUtilInternal.Collections.SequenceGetHashCode(s)); + } + } + + public static void hashAdd(ref int hashCode, IDictionary d) + { + if(d != null) + { + hashCode = unchecked(((hashCode << 5) + hashCode) ^ IceUtilInternal.Collections.DictionaryGetHashCode(d)); + } + } + } + + public sealed class Util + { + public static Instance getInstance(Ice.Communicator communicator) + { + Ice.CommunicatorI p = (Ice.CommunicatorI) communicator; + return p.getInstance(); + } + + public static ProtocolPluginFacade getProtocolPluginFacade(Ice.Communicator communicator) + { + return new ProtocolPluginFacadeI(communicator); + } + +#if !SILVERLIGHT + public static System.Threading.ThreadPriority stringToThreadPriority(string s) + { + if(String.IsNullOrEmpty(s)) + { + return ThreadPriority.Normal; + } + if(s.StartsWith("ThreadPriority.", StringComparison.Ordinal)) + { + s = s.Substring("ThreadPriority.".Length, s.Length); + } + if(s.Equals("Lowest")) + { + return ThreadPriority.Lowest; + } + else if(s.Equals("BelowNormal")) + { + return ThreadPriority.BelowNormal; + } + else if(s.Equals("Normal")) + { + return ThreadPriority.Normal; + } + else if(s.Equals("AboveNormal")) + { + return ThreadPriority.AboveNormal; + } + else if(s.Equals("Highest")) + { + return ThreadPriority.Highest; + } + return ThreadPriority.Normal; + } +#endif + } +} + +#if SILVERLIGHT +namespace System +{ + public interface ICloneable + { + Object Clone(); + } +} +#endif diff --git a/csharp/src/Ice/ValueWriter.cs b/csharp/src/Ice/ValueWriter.cs new file mode 100644 index 00000000000..5e8ecbaa1eb --- /dev/null +++ b/csharp/src/Ice/ValueWriter.cs @@ -0,0 +1,154 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using IceUtilInternal; + + public sealed class ValueWriter + { + public static void write(object obj, OutputBase output) + { + writeValue(null, obj, null, output); + } + + private static void writeValue(string name, object val, Dictionary<Ice.Object, object> objectTable, OutputBase output) + { + if(val == null) + { + writeName(name, output); + output.print("(null)"); + } + else + { + System.Type c = val.GetType(); + if(c.Equals(typeof(byte)) || c.Equals(typeof(short)) || c.Equals(typeof(int)) || + c.Equals(typeof(long)) || c.Equals(typeof(double)) || c.Equals(typeof(float)) || + c.Equals(typeof(bool))) + { + writeName(name, output); + output.print(val.ToString()); + } + else if(c.Equals(typeof(string))) + { + writeName(name, output); + output.print("\""); + output.print(val.ToString()); + output.print("\""); + } + else if(val is IList) + { + int n = 0; + IEnumerator i = ((IList)val).GetEnumerator(); + while(i.MoveNext()) + { + string elem = (name != null ? name : ""); + elem += "[" + n++ + "]"; + writeValue(elem, i.Current, objectTable, output); + } + } + else if(val is IDictionary) + { + foreach(DictionaryEntry entry in (IDictionary)val) + { + string elem = name != null ? name + "." : ""; + writeValue(elem + "key", entry.Key, objectTable, output); + writeValue(elem + "value", entry.Value, objectTable, output); + } + } + else if(val is Ice.ObjectPrxHelperBase) + { + writeName(name, output); + Ice.ObjectPrxHelperBase proxy = (Ice.ObjectPrxHelperBase)val; + output.print(proxy.reference__().ToString()); + } + else if(val is Ice.Object) + { + // + // Check for recursion. + // + if(objectTable != null && objectTable.ContainsKey((Ice.Object)val)) + { + writeName(name, output); + output.print("(recursive)"); + } + else + { + if(objectTable == null) + { + objectTable = new Dictionary<Ice.Object, object>(); + } + objectTable[(Ice.Object)val] = null; + writeFields(name, val, c, objectTable, output); + } + } + else if(c.IsEnum) + { + writeName(name, output); + output.print(val.ToString()); + } + else + { + // + // Must be struct. + // + writeFields(name, val, c, objectTable, output); + } + } + } + + private static void writeFields(string name, object obj, System.Type c, Dictionary<Ice.Object, object> objectTable, + OutputBase output) + { + if(!c.Equals(typeof(object))) + { + // + // Write the superclass first. + // + writeFields(name, obj, c.BaseType, objectTable, output); + + // + // Write the declared fields of the given class. + // + FieldInfo[] fields = + c.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public); + + for(int i = 0; i < fields.Length; i++) + { + string fieldName = (name != null ? name + '.' + fields[i].Name : fields[i].Name); + + try + { + object val = fields[i].GetValue(obj); + writeValue(fieldName, val, objectTable, output); + } + catch(System.UnauthorizedAccessException) + { + Debug.Assert(false); + } + } + } + } + + private static void writeName(string name, OutputBase output) + { + if(name != null) + { + output.nl(); + output.print(name + " = "); + } + } + } + +} diff --git a/csharp/src/Ice/WSAcceptor.cs b/csharp/src/Ice/WSAcceptor.cs new file mode 100644 index 00000000000..68d2d526a9c --- /dev/null +++ b/csharp/src/Ice/WSAcceptor.cs @@ -0,0 +1,73 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System.Diagnostics; + + class WSAcceptor : Acceptor + { + public void close() + { + _delegate.close(); + } + + public EndpointI listen() + { + _endpoint = _endpoint.endpoint(_delegate.listen()); + return _endpoint; + } + + public bool startAccept(AsyncCallback callback, object state) + { + return _delegate.startAccept(callback, state); + } + + public void finishAccept() + { + _delegate.finishAccept(); + } + + public Transceiver accept() + { + return new WSTransceiver(_instance, _delegate.accept()); + } + + public string protocol() + { + return _delegate.protocol(); + } + + public override string ToString() + { + return _delegate.ToString(); + } + + public string toDetailedString() + { + return _delegate.toDetailedString(); + } + + public Acceptor getDelegate() + { + return _delegate; + } + + internal WSAcceptor(WSEndpoint endpoint, ProtocolInstance instance, Acceptor del) + { + _endpoint = endpoint; + _instance = instance; + _delegate = del; + } + + private WSEndpoint _endpoint; + private ProtocolInstance _instance; + private Acceptor _delegate; + } +} diff --git a/csharp/src/Ice/WSConnector.cs b/csharp/src/Ice/WSConnector.cs new file mode 100644 index 00000000000..0661ab6fbf0 --- /dev/null +++ b/csharp/src/Ice/WSConnector.cs @@ -0,0 +1,75 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + sealed class WSConnector : IceInternal.Connector + { + public IceInternal.Transceiver connect() + { + return new WSTransceiver(_instance, _delegate.connect(), _host, _port, _resource); + } + + public short type() + { + return _delegate.type(); + } + + internal WSConnector(ProtocolInstance instance, IceInternal.Connector del, string host, int port, string resource) + { + _instance = instance; + _delegate = del; + _host = host; + _port = port; + _resource = resource; + } + + public override bool Equals(object obj) + { + if(!(obj is WSConnector)) + { + return false; + } + + if(this == obj) + { + return true; + } + + WSConnector p = (WSConnector)obj; + if(!_delegate.Equals(p._delegate)) + { + return false; + } + + if(!_resource.Equals(p._resource)) + { + return false; + } + + return true; + } + + public override string ToString() + { + return _delegate.ToString(); + } + + public override int GetHashCode() + { + return _delegate.GetHashCode(); + } + + private ProtocolInstance _instance; + private IceInternal.Connector _delegate; + private string _host; + private int _port; + private string _resource; + } +} diff --git a/csharp/src/Ice/WSEndpoint.cs b/csharp/src/Ice/WSEndpoint.cs new file mode 100644 index 00000000000..27f7816d5e9 --- /dev/null +++ b/csharp/src/Ice/WSEndpoint.cs @@ -0,0 +1,372 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Collections.Generic; + using System.Globalization; + + sealed class WSEndpoint : EndpointI + { + internal WSEndpoint(ProtocolInstance instance, EndpointI del, string res) + { + _instance = instance; + _delegate = (IPEndpointI)del; + _resource = res; + } + + internal WSEndpoint(ProtocolInstance instance, EndpointI del, List<string> args) + { + _instance = instance; + _delegate = (IPEndpointI)del; + + initWithOptions(args); + + if(_resource == null) + { + _resource = "/"; + } + } + + internal WSEndpoint(ProtocolInstance instance, EndpointI del, BasicStream s) + { + _instance = instance; + _delegate = (IPEndpointI)del; + + _resource = s.readString(); + } + + private sealed class InfoI : Ice.WSEndpointInfo + { + public InfoI(EndpointI e) + { + _endpoint = e; + } + + override public short type() + { + return _endpoint.type(); + } + + override public bool datagram() + { + return _endpoint.datagram(); + } + + override public bool secure() + { + return _endpoint.secure(); + } + + private EndpointI _endpoint; + } + + public override Ice.EndpointInfo getInfo() + { + InfoI info = new InfoI(this); + _delegate.fillEndpointInfo(info); + info.resource = _resource; + return info; + } + + public override short type() + { + return _delegate.type(); + } + + public override string protocol() + { + return _delegate.protocol(); + } + + public override void streamWrite(BasicStream s) + { + s.startWriteEncaps(); + _delegate.streamWriteImpl(s); + s.writeString(_resource); + s.endWriteEncaps(); + } + + public override int timeout() + { + return _delegate.timeout(); + } + + public override EndpointI timeout(int timeout) + { + if(timeout == _delegate.timeout()) + { + return this; + } + else + { + return new WSEndpoint(_instance, _delegate.timeout(timeout), _resource); + } + } + + public override string connectionId() + { + return _delegate.connectionId(); + } + + public override EndpointI connectionId(string connectionId) + { + if(connectionId.Equals(_delegate.connectionId())) + { + return this; + } + else + { + return new WSEndpoint(_instance, _delegate.connectionId(connectionId), _resource); + } + } + + public override bool compress() + { + return _delegate.compress(); + } + + public override EndpointI compress(bool compress) + { + if(compress == _delegate.compress()) + { + return this; + } + else + { + return new WSEndpoint(_instance, _delegate.compress(compress), _resource); + } + } + + public override bool datagram() + { + return _delegate.datagram(); + } + + public override bool secure() + { + return _delegate.secure(); + } + + public override Transceiver transceiver() + { + return null; + } + + private sealed class EndpointI_connectorsI : EndpointI_connectors + { + public EndpointI_connectorsI(ProtocolInstance instance, string host, int port, string resource, + EndpointI_connectors cb) + { + _instance = instance; + _host = host; + _port = port; + _resource = resource; + _callback = cb; + } + + public void connectors(List<Connector> connectors) + { + List<Connector> l = new List<Connector>(); + foreach(Connector c in connectors) + { + l.Add(new WSConnector(_instance, c, _host, _port, _resource)); + } + _callback.connectors(l); + } + + public void exception(Ice.LocalException ex) + { + _callback.exception(ex); + } + + private ProtocolInstance _instance; + private string _host; + private int _port; + private string _resource; + private EndpointI_connectors _callback; + } + + public override void connectors_async(Ice.EndpointSelectionType selType, + EndpointI_connectors callback) + { + EndpointI_connectorsI cb = + new EndpointI_connectorsI(_instance, _delegate.host(), _delegate.port(), _resource, callback); + _delegate.connectors_async(selType, cb); + } + + public override Acceptor acceptor(string adapterName) + { + Acceptor delAcc = _delegate.acceptor(adapterName); + return new WSAcceptor(this, _instance, delAcc); + } + + public WSEndpoint endpoint(EndpointI delEndp) + { + return new WSEndpoint(_instance, delEndp, _resource); + } + + public override List<EndpointI> expand() + { + List<EndpointI> endps = _delegate.expand(); + List<EndpointI> l = new List<EndpointI>(); + foreach(EndpointI e in endps) + { + l.Add(e == _delegate ? this : new WSEndpoint(_instance, e, _resource)); + } + return l; + } + + public override bool equivalent(EndpointI endpoint) + { + if(!(endpoint is WSEndpoint)) + { + return false; + } + WSEndpoint wsEndpointI = (WSEndpoint)endpoint; + return _delegate.equivalent(wsEndpointI._delegate); + } + + public override string options() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + string s = _delegate.options(); + + if(_resource != null && _resource.Length > 0) + { + s += " -r "; + bool addQuote = _resource.IndexOf(':') != -1; + if(addQuote) + { + s += "\""; + } + s += _resource; + if(addQuote) + { + s += "\""; + } + } + + return s; + } + + public override int GetHashCode() + { + int h = _delegate.GetHashCode(); + HashUtil.hashAdd(ref h, _resource); + return h; + } + + public override int CompareTo(EndpointI obj) + { + if(!(obj is EndpointI)) + { + return type() < obj.type() ? -1 : 1; + } + + WSEndpoint p = (WSEndpoint)obj; + if(this == p) + { + return 0; + } + + int v = string.Compare(_resource, p._resource, StringComparison.Ordinal); + if(v != 0) + { + return v; + } + + return _delegate.CompareTo(p._delegate); + } + + public EndpointI getDelegate() + { + return _delegate; + } + + protected override bool checkOption(string option, string argument, string endpoint) + { + switch(option[1]) + { + case 'r': + { + if(argument == null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "no argument provided for -r option in endpoint " + endpoint + _delegate.options(); + throw e; + } + _resource = argument; + return true; + } + + default: + { + return false; + } + } + } + + private ProtocolInstance _instance; + private IPEndpointI _delegate; + private string _resource; + } + + public class WSEndpointFactory : EndpointFactory + { + public WSEndpointFactory(ProtocolInstance instance, EndpointFactory del) + { + _instance = instance; + _delegate = del; + } + + public short type() + { + return _instance.type(); + } + + public string protocol() + { + return _instance.protocol(); + } + + public EndpointI create(List<string> args, bool oaEndpoint) + { + return new WSEndpoint(_instance, _delegate.create(args, oaEndpoint), args); + } + + public EndpointI read(BasicStream s) + { + return new WSEndpoint(_instance, _delegate.read(s), s); + } + + public void destroy() + { + _delegate.destroy(); + _instance = null; + } + + public EndpointFactory clone(ProtocolInstance instance) + { + Debug.Assert(false); // We don't support cloning this transport. + return null; + } + + private ProtocolInstance _instance; + private EndpointFactory _delegate; + } +} diff --git a/csharp/src/Ice/WSTransceiver.cs b/csharp/src/Ice/WSTransceiver.cs new file mode 100644 index 00000000000..7218027bdc2 --- /dev/null +++ b/csharp/src/Ice/WSTransceiver.cs @@ -0,0 +1,1693 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceInternal +{ + using System; + using System.Diagnostics; + using System.Collections.Generic; + using System.Net.Sockets; + using System.Security.Cryptography; + using System.Text; + + sealed class WSTransceiver : Transceiver + { + public Socket fd() + { + return _delegate.fd(); + } + + public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData) + { + // + // Delegate logs exceptions that occur during initialize(), so there's no need to trap them here. + // + if(_state == StateInitializeDelegate) + { + int op = _delegate.initialize(readBuffer, writeBuffer, ref hasMoreData); + if(op != 0) + { + return op; + } + _state = StateConnected; + } + + try + { + if(_state == StateConnected) + { + // + // We don't know how much we'll need to read. + // + _readBuffer.resize(1024, true); + _readBuffer.b.position(0); + _readBufferPos = 0; + + // + // The server waits for the client's upgrade request, the + // client sends the upgrade request. + // + _state = StateUpgradeRequestPending; + if(!_incoming) + { + // + // Compose the upgrade request. + // + StringBuilder @out = new StringBuilder(); + @out.Append("GET " + _resource + " HTTP/1.1\r\n"); + @out.Append("Host: " + _host + ":"); + @out.Append(_port); + @out.Append("\r\n"); + @out.Append("Upgrade: websocket\r\n"); + @out.Append("Connection: Upgrade\r\n"); + @out.Append("Sec-WebSocket-Protocol: " + _iceProtocol + "\r\n"); + @out.Append("Sec-WebSocket-Version: 13\r\n"); + @out.Append("Sec-WebSocket-Key: "); + + // + // The value for Sec-WebSocket-Key is a 16-byte random number, + // encoded with Base64. + // + byte[] key = new byte[16]; + _rand.NextBytes(key); + _key = IceUtilInternal.Base64.encode(key); + @out.Append(_key + "\r\n\r\n"); // EOM + + byte[] bytes = _utf8.GetBytes(@out.ToString()); + _writeBuffer.resize(bytes.Length, false); + _writeBuffer.b.position(0); + _writeBuffer.b.put(bytes); + _writeBuffer.b.flip(); + } + } + + // + // Try to write the client's upgrade request. + // + if(_state == StateUpgradeRequestPending && !_incoming) + { + if(_writeBuffer.b.hasRemaining()) + { + int s = _delegate.write(_writeBuffer); + if(s != 0) + { + return s; + } + } + Debug.Assert(!_writeBuffer.b.hasRemaining()); + _state = StateUpgradeResponsePending; + } + + while(true) + { + if(_readBuffer.b.hasRemaining()) + { + int s = _delegate.read(_readBuffer, ref hasMoreData); + if(s == SocketOperation.Write || _readBuffer.b.position() == 0) + { + return s; + } + } + + // + // Try to read the client's upgrade request or the server's response. + // + if((_state == StateUpgradeRequestPending && _incoming) || + (_state == StateUpgradeResponsePending && !_incoming)) + { + // + // Check if we have enough data for a complete message. + // + int p = _parser.isCompleteMessage(_readBuffer.b, 0, _readBuffer.b.position()); + if(p == -1) + { + if(_readBuffer.b.hasRemaining()) + { + return SocketOperation.Read; + } + + // + // Enlarge the buffer and try to read more. + // + int oldSize = _readBuffer.b.position(); + if(oldSize + 1024 > _instance.messageSizeMax()) + { + throw new Ice.MemoryLimitException(); + } + _readBuffer.resize(oldSize + 1024, true); + _readBuffer.b.position(oldSize); + continue; // Try again to read the response/request + } + + // + // Set _readBufferPos at the end of the response/request message. + // + _readBufferPos = p; + } + + // + // We're done, the client's upgrade request or server's response is read. + // + break; + } + + try + { + // + // Parse the client's upgrade request. + // + if(_state == StateUpgradeRequestPending && _incoming) + { + if(_parser.parse(_readBuffer.b, 0, _readBufferPos)) + { + handleRequest(_writeBuffer); + _state = StateUpgradeResponsePending; + } + else + { + throw new Ice.ProtocolException("incomplete request message"); + } + } + + if(_state == StateUpgradeResponsePending) + { + if(_incoming) + { + if(_writeBuffer.b.hasRemaining()) + { + int s = _delegate.write(_writeBuffer); + if(s != 0) + { + return s; + } + } + } + else + { + // + // Parse the server's response + // + if(_parser.parse(_readBuffer.b, 0, _readBufferPos)) + { + handleResponse(); + } + else + { + throw new Ice.ProtocolException("incomplete response message"); + } + } + } + } + catch(WebSocketException ex) + { + throw new Ice.ProtocolException(ex.Message); + } + + _state = StateOpened; + _nextState = StateOpened; + + hasMoreData = _readBufferPos < _readBuffer.b.position(); + } + catch(Ice.LocalException ex) + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + protocol() + " connection HTTP upgrade request failed\n" + ToString() + "\n" + ex); + } + throw; + } + + if(_instance.traceLevel() >= 1) + { + if(_incoming) + { + _instance.logger().trace(_instance.traceCategory(), + "accepted " + protocol() + " connection HTTP upgrade request\n" + ToString()); + } + else + { + _instance.logger().trace(_instance.traceCategory(), + protocol() + " connection HTTP upgrade request accepted\n" + ToString()); + } + } + + return SocketOperation.None; + } + + public int closing(bool initiator, Ice.LocalException reason) + { + if(_instance.traceLevel() >= 1) + { + _instance.logger().trace(_instance.traceCategory(), + "gracefully closing " + protocol() + " connection\n" + ToString()); + } + + int s = _nextState == StateOpened ? _state : _nextState; + + if(s == StateClosingRequestPending && _closingInitiator) + { + // + // If we initiated a close connection but also received a + // close connection, we assume we didn't initiated the + // connection and we send the close frame now. This is to + // ensure that if both peers close the connection at the same + // time we don't hang having both peer waiting for the close + // frame of the other. + // + Debug.Assert(!initiator); + _closingInitiator = false; + return SocketOperation.Write; + } + else if(s >= StateClosingRequestPending) + { + return SocketOperation.None; + } + + _closingInitiator = initiator; + if(reason is Ice.CloseConnectionException) + { + _closingReason = CLOSURE_NORMAL; + } + else if(reason is Ice.ObjectAdapterDeactivatedException || + reason is Ice.CommunicatorDestroyedException) + { + _closingReason = CLOSURE_SHUTDOWN; + } + else if(reason is Ice.ProtocolException) + { + _closingReason = CLOSURE_PROTOCOL_ERROR; + } + else if(reason is Ice.MemoryLimitException) + { + _closingReason = CLOSURE_TOO_BIG; + } + + if(_state == StateOpened) + { + _state = StateClosingRequestPending; + return initiator ? SocketOperation.Read : SocketOperation.Write; + } + else + { + _nextState = StateClosingRequestPending; + return SocketOperation.None; + } + } + + public void close() + { + _delegate.close(); + _state = StateClosed; + } + + public EndpointI bind() + { + Debug.Assert(false); + return null; + } + + public void destroy() + { + _delegate.destroy(); + } + + public int write(Buffer buf) + { + if(_writePending) + { + return SocketOperation.Write; + } + + if(_state < StateOpened) + { + if(_state < StateConnected) + { + return _delegate.write(buf); + } + else + { + return _delegate.write(_writeBuffer); + } + } + + int s = SocketOperation.None; + do + { + if(preWrite(buf)) + { + if(_writeState == WriteStateFlush) + { + // + // Invoke write() even though there's nothing to write. + // + Debug.Assert(!buf.b.hasRemaining()); + s = _delegate.write(buf); + } + + if(s == SocketOperation.None && _writeBuffer.b.hasRemaining()) + { + s = _delegate.write(_writeBuffer); + } + else if(s == SocketOperation.None && _incoming && !buf.empty() && _writeState == WriteStatePayload) + { + s = _delegate.write(buf); + } + } + } + while(postWrite(buf, s)); + + if(s != SocketOperation.None) + { + return s; + } + if(_state == StateClosingResponsePending && !_closingInitiator) + { + return SocketOperation.Read; + } + return SocketOperation.None; + } + + public int read(Buffer buf, ref bool hasMoreData) + { + if(_readPending) + { + return SocketOperation.Read; + } + + if(_state < StateOpened) + { + if(_state < StateConnected) + { + return _delegate.read(buf, ref hasMoreData); + } + else + { + if(_delegate.read(_readBuffer, ref hasMoreData) == SocketOperation.Write) + { + return SocketOperation.Write; + } + else + { + return SocketOperation.None; + } + } + } + + if(!buf.b.hasRemaining()) + { + hasMoreData |= _readBufferPos < _readBuffer.b.position(); + return SocketOperation.None; + } + + int s = SocketOperation.None; + do + { + if(preRead(buf)) + { + if(_readState == ReadStatePayload) + { + // + // If the payload length is smaller than what remains to be read, we read + // no more than the payload length. The remaining of the buffer will be + // sent over in another frame. + // + int readSz = _readPayloadLength - (buf.b.position() - _readStart); + if(buf.b.remaining() > readSz) + { + int size = buf.size(); + buf.resize(buf.b.position() + readSz, true); + s = _delegate.read(buf, ref hasMoreData); + buf.resize(size, true); + } + else + { + s = _delegate.read(buf, ref hasMoreData); + } + } + else + { + s = _delegate.read(_readBuffer, ref hasMoreData); + } + + if(s == SocketOperation.Write) + { + postRead(buf); + return s; + } + } + } + while(postRead(buf)); + + if(!buf.b.hasRemaining()) + { + hasMoreData |= _readBufferPos < _readBuffer.b.position(); + s = SocketOperation.None; + } + else + { + hasMoreData = false; + s = SocketOperation.Read; + } + + if(((_state == StateClosingRequestPending && !_closingInitiator) || + (_state == StateClosingResponsePending && _closingInitiator) || + _state == StatePingPending || + _state == StatePongPending) && + _writeState == WriteStateHeader) + { + // We have things to write, ask to be notified when writes are ready. + s |= SocketOperation.Write; + } + + return s; + } + + public bool startRead(Buffer buf, AsyncCallback callback, object state) + { + _readPending = true; + if(_state < StateOpened) + { + _finishRead = true; + if(_state < StateConnected) + { + return _delegate.startRead(buf, callback, state); + } + else + { + return _delegate.startRead(_readBuffer, callback, state); + } + } + + if(preRead(buf)) + { + _finishRead = true; + if(_readState == ReadStatePayload) + { + // + // If the payload length is smaller than what remains to be read, we read + // no more than the payload length. The remaining of the buffer will be + // sent over in another frame. + // + int readSz = _readPayloadLength - (buf.b.position() - _readStart); + if(buf.b.remaining() > readSz) + { + int size = buf.size(); + buf.resize(buf.b.position() + readSz, true); + bool completedSynchronously = _delegate.startRead(buf, callback, state); + buf.resize(size, true); + return completedSynchronously; + } + else + { + return _delegate.startRead(buf, callback, state); + } + } + else + { + return _delegate.startRead(_readBuffer, callback, state); + } + } + else + { + return true; + } + } + + public void finishRead(Buffer buf) + { + Debug.Assert(_readPending); + _readPending = false; + if(_state < StateOpened) + { + Debug.Assert(_finishRead); + _finishRead = false; + if(_state < StateConnected) + { + _delegate.finishRead(buf); + } + else + { + _delegate.finishRead(_readBuffer); + } + return; + } + + if(!_finishRead) + { + // Nothing to do. + } + else if(_readState == ReadStatePayload) + { + Debug.Assert(_finishRead); + _finishRead = false; + _delegate.finishRead(buf); + } + else + { + Debug.Assert(_finishRead); + _finishRead = false; + _delegate.finishRead(_readBuffer); + } + postRead(buf); + } + + public bool startWrite(Buffer buf, AsyncCallback callback, object state, + out bool completed) + { + _writePending = true; + if(_state < StateOpened) + { + if(_state < StateConnected) + { + return _delegate.startWrite(buf, callback, state, out completed); + } + else + { + return _delegate.startWrite(_writeBuffer, callback, state, out completed); + } + } + + if(preWrite(buf)) + { + if(_writeBuffer.b.hasRemaining()) + { + return _delegate.startWrite(_writeBuffer, callback, state, out completed); + } + else + { + Debug.Assert(_incoming); + return _delegate.startWrite(buf, callback, state, out completed); + } + } + else + { + completed = true; + return false; + } + } + + public void finishWrite(Buffer buf) + { + _writePending = false; + if(_state < StateOpened) + { + if(_state < StateConnected) + { + _delegate.finishWrite(buf); + } + else + { + _delegate.finishWrite(_writeBuffer); + } + return; + } + + if(_writeBuffer.b.hasRemaining()) + { + _delegate.finishWrite(_writeBuffer); + } + else if(!buf.empty() && buf.b.hasRemaining()) + { + Debug.Assert(_incoming); + _delegate.finishWrite(buf); + } + + postWrite(buf, SocketOperation.None); + } + + public string protocol() + { + return _instance.protocol(); + } + + public Ice.ConnectionInfo getInfo() + { + Ice.IPConnectionInfo di = (Ice.IPConnectionInfo)_delegate.getInfo(); + Ice.WSConnectionInfo info = new Ice.WSConnectionInfo(); + info.localAddress = di.localAddress; + info.localPort = di.localPort; + info.remoteAddress = di.remoteAddress; + info.remotePort = di.remotePort; + info.rcvSize = di.rcvSize; + info.sndSize = di.sndSize; + info.headers = _parser.getHeaders(); + return info; + } + + public void checkSendSize(Buffer buf) + { + _delegate.checkSendSize(buf); + } + + public void setBufferSize(int rcvSize, int sndSize) + { + _delegate.setBufferSize(rcvSize, sndSize); + } + + public override string ToString() + { + return _delegate.ToString(); + } + + public string toDetailedString() + { + return _delegate.toDetailedString(); + } + + internal + WSTransceiver(ProtocolInstance instance, Transceiver del, string host, int port, string resource) + { + init(instance, del); + _host = host; + _port = port; + _resource = resource; + _incoming = false; + + // + // Use a 16KB write buffer size. We use 16KB for the write + // buffer size because all the data needs to be copied to the + // write buffer for the purpose of masking. A 16KB buffer + // appears to be a good compromise to reduce the number of + // socket write calls and not consume too much memory. + // + _writeBufferSize = 16 * 1024; + + // + // Write and read buffer size must be large enough to hold the frame header! + // + Debug.Assert(_writeBufferSize > 256); + Debug.Assert(_readBufferSize > 256); + } + + internal WSTransceiver(ProtocolInstance instance, Transceiver del) + { + init(instance, del); + _host = ""; + _port = -1; + _resource = ""; + _incoming = true; + + // + // Write and read buffer size must be large enough to hold the frame header! + // + Debug.Assert(_writeBufferSize > 256); + Debug.Assert(_readBufferSize > 256); + } + + private void init(ProtocolInstance instance, Transceiver del) + { + _instance = instance; + _delegate = del; + _state = StateInitializeDelegate; + _parser = new HttpParser(); + _readState = ReadStateOpcode; + _readBuffer = new Buffer(ByteBuffer.ByteOrder.BIG_ENDIAN); // Network byte order + _readBufferSize = 1024; + _readLastFrame = true; + _readOpCode = 0; + _readHeaderLength = 0; + _readPayloadLength = 0; + _writeState = WriteStateHeader; + _writeBuffer = new Buffer(ByteBuffer.ByteOrder.BIG_ENDIAN); // Network byte order + _writeBufferSize = 1024; + _readPending = false; + _finishRead = false; + _writePending = false; + _readMask = new byte[4]; + _writeMask = new byte[4]; + _key = ""; + _pingPayload = new byte[0]; + _rand = new Random(); + } + + private void handleRequest(Buffer responseBuffer) + { + // + // HTTP/1.1 + // + if(_parser.versionMajor() != 1 || _parser.versionMinor() != 1) + { + throw new WebSocketException("unsupported HTTP version"); + } + + // + // "An |Upgrade| header field containing the value 'websocket', + // treated as an ASCII case-insensitive value." + // + string val = _parser.getHeader("Upgrade", true); + if(val == null) + { + throw new WebSocketException("missing value for Upgrade field"); + } + else if(!val.Equals("websocket")) + { + throw new WebSocketException("invalid value `" + val + "' for Upgrade field"); + } + + // + // "A |Connection| header field that includes the token 'Upgrade', + // treated as an ASCII case-insensitive value. + // + val = _parser.getHeader("Connection", true); + if(val == null) + { + throw new WebSocketException("missing value for Connection field"); + } + else if(val.IndexOf("upgrade") == -1) + { + throw new WebSocketException("invalid value `" + val + "' for Connection field"); + } + + // + // "A |Sec-WebSocket-Version| header field, with a value of 13." + // + val = _parser.getHeader("Sec-WebSocket-Version", false); + if(val == null) + { + throw new WebSocketException("missing value for WebSocket version"); + } + else if(!val.Equals("13")) + { + throw new WebSocketException("unsupported WebSocket version `" + val + "'"); + } + + // + // "Optionally, a |Sec-WebSocket-Protocol| header field, with a list + // of values indicating which protocols the client would like to + // speak, ordered by preference." + // + bool addProtocol = false; + val = _parser.getHeader("Sec-WebSocket-Protocol", true); + if(val != null) + { + string[] protocols = IceUtilInternal.StringUtil.splitString(val, ","); + if(protocols == null) + { + throw new WebSocketException("invalid value `" + val + "' for WebSocket protocol"); + } + foreach(string p in protocols) + { + if(!p.Trim().Equals(_iceProtocol)) + { + throw new WebSocketException("unknown value `" + p + "' for WebSocket protocol"); + } + addProtocol = true; + } + } + + // + // "A |Sec-WebSocket-Key| header field with a base64-encoded + // value that, when decoded, is 16 bytes in length." + // + string key = _parser.getHeader("Sec-WebSocket-Key", false); + if(key == null) + { + throw new WebSocketException("missing value for WebSocket key"); + } + + byte[] decodedKey = IceUtilInternal.Base64.decode(key); + if(decodedKey.Length != 16) + { + throw new WebSocketException("invalid value `" + key + "' for WebSocket key"); + } + + // + // Retain the target resource. + // + _resource = _parser.uri(); + + // + // Compose the response. + // + StringBuilder @out = new StringBuilder(); + @out.Append("HTTP/1.1 101 Switching Protocols\r\n"); + @out.Append("Upgrade: websocket\r\n"); + @out.Append("Connection: Upgrade\r\n"); + if(addProtocol) + { + @out.Append("Sec-WebSocket-Protocol: " + _iceProtocol + "\r\n"); + } + + // + // The response includes: + // + // "A |Sec-WebSocket-Accept| header field. The value of this + // header field is constructed by concatenating /key/, defined + // above in step 4 in Section 4.2.2, with the string "258EAFA5- + // E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this + // concatenated value to obtain a 20-byte value and base64- + // encoding (see Section 4 of [RFC4648]) this 20-byte hash. + // + @out.Append("Sec-WebSocket-Accept: "); + string input = key + _wsUUID; +#if SILVERLIGHT + SHA1Managed sha1 = new SHA1Managed(); + byte[] hash = sha1.ComputeHash(_utf8.GetBytes(input)); +#else + byte[] hash = SHA1.Create().ComputeHash(_utf8.GetBytes(input)); +#endif + @out.Append(IceUtilInternal.Base64.encode(hash) + "\r\n" + "\r\n"); // EOM + + byte[] bytes = _utf8.GetBytes(@out.ToString()); + Debug.Assert(bytes.Length == @out.Length); + responseBuffer.resize(bytes.Length, false); + responseBuffer.b.position(0); + responseBuffer.b.put(bytes); + responseBuffer.b.flip(); + } + + private void handleResponse() + { + string val; + + // + // HTTP/1.1 + // + if(_parser.versionMajor() != 1 || _parser.versionMinor() != 1) + { + throw new WebSocketException("unsupported HTTP version"); + } + + // + // "If the status code received from the server is not 101, the + // client handles the response per HTTP [RFC2616] procedures. In + // particular, the client might perform authentication if it + // receives a 401 status code; the server might redirect the client + // using a 3xx status code (but clients are not required to follow + // them), etc." + // + if(_parser.status() != 101) + { + StringBuilder @out = new StringBuilder("unexpected status value " + _parser.status()); + if(_parser.reason().Length > 0) + { + @out.Append(":\n" + _parser.reason()); + } + throw new WebSocketException(@out.ToString()); + } + + // + // "If the response lacks an |Upgrade| header field or the |Upgrade| + // header field contains a value that is not an ASCII case- + // insensitive match for the value "websocket", the client MUST + // _Fail the WebSocket Connection_." + // + val = _parser.getHeader("Upgrade", true); + if(val == null) + { + throw new WebSocketException("missing value for Upgrade field"); + } + else if(!val.Equals("websocket")) + { + throw new WebSocketException("invalid value `" + val + "' for Upgrade field"); + } + + // + // "If the response lacks a |Connection| header field or the + // |Connection| header field doesn't contain a token that is an + // ASCII case-insensitive match for the value "Upgrade", the client + // MUST _Fail the WebSocket Connection_." + // + val = _parser.getHeader("Connection", true); + if(val == null) + { + throw new WebSocketException("missing value for Connection field"); + } + else if(val.IndexOf("upgrade") == -1) + { + throw new WebSocketException("invalid value `" + val + "' for Connection field"); + } + + // + // "If the response includes a |Sec-WebSocket-Protocol| header field + // and this header field indicates the use of a subprotocol that was + // not present in the client's handshake (the server has indicated a + // subprotocol not requested by the client), the client MUST _Fail + // the WebSocket Connection_." + // + val = _parser.getHeader("Sec-WebSocket-Protocol", true); + if(val != null && !val.Equals(_iceProtocol)) + { + throw new WebSocketException("invalid value `" + val + "' for WebSocket protocol"); + } + + // + // "If the response lacks a |Sec-WebSocket-Accept| header field or + // the |Sec-WebSocket-Accept| contains a value other than the + // base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket- + // Key| (as a string, not base64-decoded) with the string "258EAFA5- + // E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and + // trailing whitespace, the client MUST _Fail the WebSocket + // Connection_." + // + val = _parser.getHeader("Sec-WebSocket-Accept", false); + if(val == null) + { + throw new WebSocketException("missing value for Sec-WebSocket-Accept"); + } + + string input = _key + _wsUUID; +#if SILVERLIGHT + SHA1Managed sha1 = new SHA1Managed(); + byte[] hash = sha1.ComputeHash(_utf8.GetBytes(input)); +#else + byte[] hash = SHA1.Create().ComputeHash(_utf8.GetBytes(input)); +#endif + if(!val.Equals(IceUtilInternal.Base64.encode(hash))) + { + throw new WebSocketException("invalid value `" + val + "' for Sec-WebSocket-Accept"); + } + } + + private bool preRead(Buffer buf) + { + while(true) + { + if(_readState == ReadStateOpcode) + { + // + // Is there enough data available to read the opcode? + // + if(!readBuffered(2)) + { + return true; + } + + // + // Most-significant bit indicates whether this is the + // last frame. Least-significant four bits hold the + // opcode. + // + int ch = _readBuffer.b.get(_readBufferPos++); + _readOpCode = ch & 0xf; + + // + // Remember if last frame if we're going to read a data or + // continuation frame, this is only for protocol + // correctness checking purpose. + // + if(_readOpCode == OP_DATA) + { + if(!_readLastFrame) + { + throw new Ice.ProtocolException("invalid data frame, no FIN on previous frame"); + } + _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL; + } + else if(_readOpCode == OP_CONT) + { + if(_readLastFrame) + { + throw new Ice.ProtocolException("invalid continuation frame, previous frame FIN set"); + } + _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL; + } + + ch = _readBuffer.b.get(_readBufferPos++); + + // + // Check the MASK bit. Messages sent by a client must be masked; + // messages sent by a server must not be masked. + // + bool masked = (ch & FLAG_MASKED) == FLAG_MASKED; + if(masked != _incoming) + { + throw new Ice.ProtocolException("invalid masking"); + } + + // + // Extract the payload length, which can have the following values: + // + // 0-125: The payload length + // 126: The subsequent two bytes contain the payload length + // 127: The subsequent eight bytes contain the payload length + // + _readPayloadLength = (ch & 0x7f); + if(_readPayloadLength < 126) + { + _readHeaderLength = 0; + } + else if(_readPayloadLength == 126) + { + _readHeaderLength = 2; // Need to read a 16-bit payload length. + } + else + { + _readHeaderLength = 8; // Need to read a 64-bit payload length. + } + if(masked) + { + _readHeaderLength += 4; // Need to read a 32-bit mask. + } + + _readState = ReadStateHeader; + } + + if(_readState == ReadStateHeader) + { + // + // Is there enough data available to read the header? + // + if(_readHeaderLength > 0 && !readBuffered(_readHeaderLength)) + { + return true; + } + + if(_readPayloadLength == 126) + { + _readPayloadLength = _readBuffer.b.getShort(_readBufferPos); // Uses network byte order. + if(_readPayloadLength < 0) + { + _readPayloadLength += 65536; + } + _readBufferPos += 2; + } + else if(_readPayloadLength == 127) + { + long l = _readBuffer.b.getLong(_readBufferPos); // Uses network byte order. + _readBufferPos += 8; + if(l < 0 || l > Int32.MaxValue) + { + throw new Ice.ProtocolException("invalid WebSocket payload length: " + l); + } + _readPayloadLength = (int)l; + } + + // + // Read the mask if this is an incoming connection. + // + if(_incoming) + { + // + // We must have needed to read the mask. + // + Debug.Assert(_readBuffer.b.position() - _readBufferPos >= 4); + for(int i = 0; i < 4; ++i) + { + _readMask[i] = _readBuffer.b.get(_readBufferPos++); // Copy the mask. + } + } + + switch(_readOpCode) + { + case OP_TEXT: // Text frame + { + throw new Ice.ProtocolException("text frames not supported"); + } + case OP_DATA: // Data frame + case OP_CONT: // Continuation frame + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), "received " + protocol() + + (_readOpCode == OP_DATA ? " data" : " continuation") + + " frame with payload length of " + _readPayloadLength + + " bytes\n" + ToString()); + } + + if(_readPayloadLength <= 0) + { + throw new Ice.ProtocolException("payload length is 0"); + } + _readState = ReadStatePayload; + Debug.Assert(buf.b.hasRemaining()); + _readFrameStart = buf.b.position(); + break; + } + case OP_CLOSE: // Connection close + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + "received " + protocol() + " connection close frame\n" + ToString()); + } + + _readState = ReadStateControlFrame; + int s = _nextState == StateOpened ? _state : _nextState; + if(s == StateClosingRequestPending) + { + // + // If we receive a close frame while we were actually + // waiting to send one, change the role and send a + // close frame response. + // + if(!_closingInitiator) + { + _closingInitiator = true; + } + if(_state == StateClosingRequestPending) + { + _state = StateClosingResponsePending; + } + else + { + _nextState = StateClosingResponsePending; + } + return false; // No longer interested in reading + } + else + { + throw new Ice.ConnectionLostException(); + } + } + case OP_PING: + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + "received " + protocol() + " connection ping frame\n" + ToString()); + } + _readState = ReadStateControlFrame; + break; + } + case OP_PONG: // Pong + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + "received " + protocol() + " connection pong frame\n" + ToString()); + } + _readState = ReadStateControlFrame; + break; + } + default: + { + throw new Ice.ProtocolException("unsupported opcode: " + _readOpCode); + } + } + } + + if(_readState == ReadStateControlFrame) + { + if(_readPayloadLength > 0 && !readBuffered(_readPayloadLength)) + { + return true; + } + + if(_readPayloadLength > 0 && _readOpCode == OP_PING) + { + _pingPayload = new byte[_readPayloadLength]; + System.Buffer.BlockCopy(_readBuffer.b.rawBytes(), _readBufferPos, _pingPayload, 0, + _readPayloadLength); + } + + _readBufferPos += _readPayloadLength; + _readPayloadLength = 0; + + if(_readOpCode == OP_PING) + { + if(_state == StateOpened) + { + _state = StatePongPending; // Send pong frame now + } + else if(_nextState < StatePongPending) + { + _nextState = StatePongPending; // Send pong frame next + } + } + + // + // We've read the payload of the PING/PONG frame, we're ready + // to read a new frame. + // + _readState = ReadStateOpcode; + } + + if(_readState == ReadStatePayload) + { + // + // This must be assigned before the check for the buffer. If the buffer is empty + // or already read, postRead will return false. + // + _readStart = buf.b.position(); + + if(buf.empty() || !buf.b.hasRemaining()) + { + return false; + } + + if(_readBufferPos < _readBuffer.b.position()) + { + int n = Math.Min(_readBuffer.b.position() - _readBufferPos, buf.b.remaining()); + System.Buffer.BlockCopy(_readBuffer.b.rawBytes(), _readBufferPos, buf.b.rawBytes(), + buf.b.position(), n); + buf.b.position(buf.b.position() + n); + _readBufferPos += n; + } + + // + // Continue reading if we didn't read the full message, otherwise give back + // the control to the connection + // + return buf.b.hasRemaining(); + } + } + } + + private bool postRead(Buffer buf) + { + if(_readState != ReadStatePayload) + { + return _readStart < _readBuffer.b.position(); // Returns true if data was read. + } + + if(_readStart == buf.b.position()) + { + return false; // Nothing was read or nothing to read. + } + Debug.Assert(_readStart < buf.b.position()); + + if(_incoming) + { + // + // Unmask the data we just read. + // + int pos = buf.b.position(); + byte[] arr = buf.b.rawBytes(); + for(int n = _readStart; n < pos; ++n) + { + arr[n] = (byte)(arr[n] ^ _readMask[(n - _readFrameStart) % 4]); + } + } + + _readPayloadLength -= buf.b.position() - _readStart; + _readStart = buf.b.position(); + if(_readPayloadLength == 0) + { + // + // We've read the complete payload, we're ready to read a new frame. + // + _readState = ReadStateOpcode; + } + return buf.b.hasRemaining(); + } + + private bool preWrite(Buffer buf) + { + if(_writeState == WriteStateHeader) + { + if(_state == StateOpened) + { + if(buf.empty() || !buf.b.hasRemaining()) + { + return false; + } + + Debug.Assert(buf.b.position() == 0); + prepareWriteHeader((byte)OP_DATA, buf.size()); + + _writeState = WriteStatePayload; + } + else if(_state == StatePingPending) + { + prepareWriteHeader((byte)OP_PING, 0); // Don't send any payload + + _writeState = WriteStateControlFrame; + _writeBuffer.b.flip(); + } + else if(_state == StatePongPending) + { + prepareWriteHeader((byte)OP_PONG, _pingPayload.Length); + if(_pingPayload.Length > _writeBuffer.b.remaining()) + { + int pos = _writeBuffer.b.position(); + _writeBuffer.resize(pos + _pingPayload.Length, false); + _writeBuffer.b.position(pos); + } + _writeBuffer.b.put(_pingPayload); + _pingPayload = new byte[0]; + + _writeState = WriteStateControlFrame; + _writeBuffer.b.flip(); + } + else if((_state == StateClosingRequestPending && !_closingInitiator) || + (_state == StateClosingResponsePending && _closingInitiator)) + { + prepareWriteHeader((byte)OP_CLOSE, 2); + + // Write closing reason + _writeBuffer.b.putShort((short)_closingReason); + + if(!_incoming) + { + byte b; + int pos = _writeBuffer.b.position() - 2; + b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[0]); + _writeBuffer.b.put(pos, b); + pos++; + b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[1]); + _writeBuffer.b.put(pos, b); + } + + _writeState = WriteStateControlFrame; + _writeBuffer.b.flip(); + } + else + { + Debug.Assert(_state != StateClosed); + return false; // Nothing to write in this state + } + + _writePayloadLength = 0; + } + + if(_writeState == WriteStatePayload) + { + // + // For an outgoing connection, each message must be masked with a random + // 32-bit value, so we copy the entire message into the internal buffer + // for writing. For incoming connections, we just copy the start of the + // message in the internal buffer after the hedaer. If the message is + // larger, the reminder is sent directly from the message buffer to avoid + // copying. + // + if(!_incoming && (_writePayloadLength == 0 || !_writeBuffer.b.hasRemaining())) + { + if(!_writeBuffer.b.hasRemaining()) + { + _writeBuffer.b.position(0); + } + + int n = buf.b.position(); + int sz = buf.size(); + int pos = _writeBuffer.b.position(); + int count = Math.Min(sz - n, _writeBuffer.b.remaining()); + byte[] src = buf.b.rawBytes(); + byte[] dest = _writeBuffer.b.rawBytes(); + for(int i = 0; i < count; ++i, ++n, ++pos) + { + dest[pos] = (byte)(src[n] ^ _writeMask[n % 4]); + } + _writeBuffer.b.position(pos); + _writePayloadLength = n; + + _writeBuffer.b.flip(); + } + else if(_writePayloadLength == 0) + { + Debug.Assert(_incoming); + if(_writeBuffer.b.hasRemaining()) + { + Debug.Assert(buf.b.position() == 0); + int n = Math.Min(_writeBuffer.b.remaining(), buf.b.remaining()); + int pos = _writeBuffer.b.position(); + System.Buffer.BlockCopy(buf.b.rawBytes(), 0, _writeBuffer.b.rawBytes(), pos, n); + _writeBuffer.b.position(pos + n); + _writePayloadLength = n; + } + _writeBuffer.b.flip(); + } + return true; + } + else if(_writeState == WriteStateControlFrame) + { + return _writeBuffer.b.hasRemaining(); + } + else + { + Debug.Assert(_writeState == WriteStateFlush); + return true; + } + } + + private bool postWrite(Buffer buf, int status) + { + if(_state > StateOpened && _writeState == WriteStateControlFrame) + { + if(!_writeBuffer.b.hasRemaining()) + { + if(_state == StatePingPending) + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + "sent " + protocol() + " connection ping frame\n" + ToString()); + } + } + else if(_state == StatePongPending) + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + "sent " + protocol() + " connection pong frame\n" + ToString()); + } + } + else if((_state == StateClosingRequestPending && !_closingInitiator) || + (_state == StateClosingResponsePending && _closingInitiator)) + { + if(_instance.traceLevel() >= 2) + { + _instance.logger().trace(_instance.traceCategory(), + "sent " + protocol() + " connection close frame\n" + ToString()); + } + + if(_state == StateClosingRequestPending && !_closingInitiator) + { + _writeState = WriteStateHeader; + _state = StateClosingResponsePending; + return false; + } + else + { + throw new Ice.ConnectionLostException(); + } + } + else if(_state == StateClosed) + { + return false; + } + + _state = _nextState; + _nextState = StateOpened; + _writeState = WriteStateHeader; + } + else + { + return status == SocketOperation.None; + } + } + + if((!_incoming || buf.b.position() == 0) && _writePayloadLength > 0) + { + if(!_writeBuffer.b.hasRemaining()) + { + buf.b.position(_writePayloadLength); + } + } + + if(status == SocketOperation.Write && !buf.b.hasRemaining() && !_writeBuffer.b.hasRemaining()) + { + // + // Our buffers are empty but the delegate needs another call to write(). + // + _writeState = WriteStateFlush; + return false; + } + else if(!buf.b.hasRemaining()) + { + _writeState = WriteStateHeader; + if(_state == StatePingPending || + _state == StatePongPending || + (_state == StateClosingRequestPending && !_closingInitiator) || + (_state == StateClosingResponsePending && _closingInitiator)) + { + return true; + } + } + else if(_state == StateOpened) + { + return status == SocketOperation.None; + } + + return false; + } + + private bool readBuffered(int sz) + { + if(_readBufferPos == _readBuffer.b.position()) + { + _readBuffer.resize(_readBufferSize, true); + _readBufferPos = 0; + _readBuffer.b.position(0); + } + else + { + int available = _readBuffer.b.position() - _readBufferPos; + if(available < sz) + { + if(_readBufferPos > 0) + { + _readBuffer.b.limit(_readBuffer.b.position()); + _readBuffer.b.position(_readBufferPos); + _readBuffer.b.compact(); + Debug.Assert(_readBuffer.b.position() == available); + } + _readBuffer.resize(Math.Max(_readBufferSize, sz), true); + _readBufferPos = 0; + _readBuffer.b.position(available); + } + } + + _readStart = _readBuffer.b.position(); + if(_readBufferPos + sz > _readBuffer.b.position()) + { + return false; // Not enough read. + } + Debug.Assert(_readBuffer.b.position() > _readBufferPos); + return true; + } + + private void prepareWriteHeader(byte opCode, int payloadLength) + { + // + // We need to prepare the frame header. + // + _writeBuffer.resize(_writeBufferSize, false); + _writeBuffer.b.limit(_writeBufferSize); + _writeBuffer.b.position(0); + + // + // Set the opcode - this is the one and only data frame. + // + _writeBuffer.b.put((byte)(opCode | FLAG_FINAL)); + + // + // Set the payload length. + // + if(payloadLength <= 125) + { + _writeBuffer.b.put((byte)payloadLength); + } + else if(payloadLength > 125 && payloadLength <= 65535) + { + // + // Use an extra 16 bits to encode the payload length. + // + _writeBuffer.b.put((byte)126); + _writeBuffer.b.putShort((short)payloadLength); + } + else if(payloadLength > 65535) + { + // + // Use an extra 64 bits to encode the payload length. + // + _writeBuffer.b.put((byte)127); + _writeBuffer.b.putLong(payloadLength); + } + + if(!_incoming) + { + // + // Add a random 32-bit mask to every outgoing frame, copy the payload data, + // and apply the mask. + // + _writeBuffer.b.put(1, (byte)(_writeBuffer.b.get(1) | FLAG_MASKED)); + _rand.NextBytes(_writeMask); + _writeBuffer.b.put(_writeMask); + } + } + + private ProtocolInstance _instance; + private Transceiver _delegate; + private string _host; + private int _port; + private string _resource; + private bool _incoming; + + private const int StateInitializeDelegate = 0; + private const int StateConnected = 1; + private const int StateUpgradeRequestPending = 2; + private const int StateUpgradeResponsePending = 3; + private const int StateOpened = 4; + private const int StatePingPending = 5; + private const int StatePongPending = 6; + private const int StateClosingRequestPending = 7; + private const int StateClosingResponsePending = 8; + private const int StateClosed = 9; + + private int _state; + private int _nextState; + + private HttpParser _parser; + private string _key; + + private const int ReadStateOpcode = 0; + private const int ReadStateHeader = 1; + private const int ReadStateControlFrame = 2; + private const int ReadStatePayload = 3; + + private int _readState; + private Buffer _readBuffer; + private int _readBufferPos; + private int _readBufferSize; + + private bool _readLastFrame; + private int _readOpCode; + private int _readHeaderLength; + private int _readPayloadLength; + private int _readStart; + private int _readFrameStart; + private byte[] _readMask; + + private const int WriteStateHeader = 0; + private const int WriteStatePayload = 1; + private const int WriteStateControlFrame = 2; + private const int WriteStateFlush = 3; + + private int _writeState; + private Buffer _writeBuffer; + private int _writeBufferSize; + private byte[] _writeMask; + private int _writePayloadLength; + + private bool _closingInitiator; + private int _closingReason; + + private bool _readPending; + private bool _finishRead; + private bool _writePending; + + private byte[] _pingPayload; + + private Random _rand; + + // + // WebSocket opcodes + // + private const int OP_CONT = 0x0; // Continuation frame + private const int OP_TEXT = 0x1; // Text frame + private const int OP_DATA = 0x2; // Data frame + private const int OP_RES_0x3 = 0x3; // Reserved + private const int OP_RES_0x4 = 0x4; // Reserved + private const int OP_RES_0x5 = 0x5; // Reserved + private const int OP_RES_0x6 = 0x6; // Reserved + private const int OP_RES_0x7 = 0x7; // Reserved + private const int OP_CLOSE = 0x8; // Connection close + private const int OP_PING = 0x9; // Ping + private const int OP_PONG = 0xA; // Pong + private const int OP_RES_0xB = 0xB; // Reserved + private const int OP_RES_0xC = 0xC; // Reserved + private const int OP_RES_0xD = 0xD; // Reserved + private const int OP_RES_0xE = 0xE; // Reserved + private const int OP_RES_0xF = 0xF; // Reserved + private const int FLAG_FINAL = 0x80; // Last frame + private const int FLAG_MASKED = 0x80; // Payload is masked + + private const int CLOSURE_NORMAL = 1000; + private const int CLOSURE_SHUTDOWN = 1001; + private const int CLOSURE_PROTOCOL_ERROR = 1002; + private const int CLOSURE_TOO_BIG = 1009; + + private const string _iceProtocol = "ice.zeroc.com"; + private const string _wsUUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private static System.Text.UTF8Encoding _utf8 = new System.Text.UTF8Encoding(false, true); + } +} diff --git a/csharp/src/Ice/generated/.gitignore b/csharp/src/Ice/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/Ice/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IceBox/.depend.mak b/csharp/src/IceBox/.depend.mak new file mode 100644 index 00000000000..bf680c6be59 --- /dev/null +++ b/csharp/src/IceBox/.depend.mak @@ -0,0 +1,7 @@ + +IceBox.cs: \ + "$(slicedir)\IceBox\IceBox.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/CommunicatorF.ice" \ + "$(slicedir)/Ice/PropertiesF.ice" \ + "$(slicedir)/Ice/SliceChecksumDict.ice" diff --git a/csharp/src/IceBox/AssemblyInfo.cs b/csharp/src/IceBox/AssemblyInfo.cs new file mode 100644 index 00000000000..8c3d6231872 --- /dev/null +++ b/csharp/src/IceBox/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceBox")] +[assembly: AssemblyDescription("IceBox run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceBox for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceBox/AssemblyInfoExe.cs b/csharp/src/IceBox/AssemblyInfoExe.cs new file mode 100644 index 00000000000..2cf942e95f3 --- /dev/null +++ b/csharp/src/IceBox/AssemblyInfoExe.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceBox")] +[assembly: AssemblyDescription("IceBox")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceBox for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceBox/Makefile b/csharp/src/IceBox/Makefile new file mode 100644 index 00000000000..23413be5626 --- /dev/null +++ b/csharp/src/IceBox/Makefile @@ -0,0 +1,58 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IceBox +LIBNAME = $(PKG).dll +ICEBOXNET = $(bindir)/iceboxnet.exe +TARGETS = $(ICEBOXNET) $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +L_SRCS = AssemblyInfo.cs +I_SRCS = AssemblyInfoExe.cs Server.cs ServiceManagerI.cs + +SLICE_SRCS = $(SDIR)/IceBox.ice + +SDIR = $(slicedir)/IceBox +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +EXE_MCSFLAGS := $(MCSFLAGS) -target:exe + +LIB_MCSFLAGS := $(MCSFLAGS) -target:library -out:$(assembliesdir)/$(LIBNAME) +LIB_MCSFLAGS := $(LIB_MCSFLAGS) -keyfile:$(KEYFILE) +LIB_MCSFLAGS := $(LIB_MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) --checksum --ice -I. -I$(slicedir) + +$(ICEBOXNET): $(I_SRCS) $(assembliesdir)/$(LIBNAME) + $(MCS) $(EXE_MCSFLAGS) -out:$@ $(call ref,$(PKG)) $(call ref,Ice) $(I_SRCS) + +$(assembliesdir)/$(LIBNAME): $(L_SRCS) $(GEN_SRCS) + $(MCS) $(LIB_MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +install:: all + $(call installprogram,$(ICEBOXNET),$(DESTDIR)$(install_bindir)) + $(call installdata,$(top_srcdir)/../man/man1/iceboxnet.1,$(DESTDIR)$(install_mandir)) + $(call installmdb,$(ICEBOXNET).mdb) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IceBox/Makefile.mak b/csharp/src/IceBox/Makefile.mak new file mode 100644 index 00000000000..6633c7bdbf0 --- /dev/null +++ b/csharp/src/IceBox/Makefile.mak @@ -0,0 +1,117 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IceBox +LIBNAME = $(assembliesdir)\$(PKG).dll +ICEBOXNET = $(bindir)\iceboxnet.exe +TARGETS = $(LIBNAME) $(ICEBOXNET) +POLICY_TARGET = $(POLICY).dll + +L_SRCS = AssemblyInfo.cs +I_SRCS = AssemblyInfoExe.cs Server.cs ServiceManagerI.cs + +GEN_SRCS = $(GDIR)\IceBox.cs + +SDIR = $(slicedir)\IceBox +GDIR = generated + + +!include $(top_srcdir)/config/Make.rules.mak.cs + +all:: $(ICEBOXNET).config + +EXE_MCSFLAGS = $(MCSFLAGS) -target:exe + +LIB_MCSFLAGS = $(MCSFLAGS) -target:library -out:$(LIBNAME) +LIB_MCSFLAGS = $(LIB_MCSFLAGS) -keyfile:"$(KEYFILE)" +LIB_MCSFLAGS = $(LIB_MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) --checksum --ice -I. -I$(slicedir) + +$(ICEBOXNET): $(I_SRCS) $(LIBNAME) + $(MCS) $(EXE_MCSFLAGS) -out:$@ -r:$(LIBNAME) -r:$(refdir)\Ice.dll $(I_SRCS) + +$(LIBNAME): $(L_SRCS) $(GEN_SRCS) + $(MCS) /baseaddress:0x25000000 $(LIB_MCSFLAGS) -r:$(refdir)\Ice.dll $(L_SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb + del /q $(bindir)\iceboxnet.pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +!if "$(PUBLIC_KEY_TOKEN)" == "" +$(ICEBOXNET).config: + @sn -q -T $(assembliesdir)\Ice.dll > tmp.publicKeyToken && \ + set /P TMP_TOKEN= < tmp.publicKeyToken && \ + cmd /c "set PUBLIC_KEY_TOKEN=%TMP_TOKEN:~-16% && \ + del tmp.publicKeyToken && \ + nmake /nologo /f Makefile.mak iceboxnetconfig" +!endif + +publicKeyToken = $(PUBLIC_KEY_TOKEN: =) + +!if "$(COMPACT)" == "yes" +assembliesRelativeDir = ..\..\Assemblies\cf +!else +assembliesRelativeDir = ..\Assemblies +!endif + +iceboxnetconfig: + echo <<$(ICEBOXNET).config +<?xml version="1.0"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="Ice" culture="neutral" publicKeyToken="$(publicKeyToken)"/> + <codeBase version="3.6.0.0" href="$(assembliesRelativeDir)\Ice.dll"/> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="IceBox" culture="neutral" publicKeyToken="$(publicKeyToken)"/> + <codeBase version="3.6.0.0" href="$(assembliesRelativeDir)\IceBox.dll"/> + </dependentAssembly> + <!-- + This allows iceboxnet to load the IceSSL plug-in using a strong name. We omit the + optional attributes culture and publicKeyToken so they can be also omitted in the + IceSSL entry point. + --> + <dependentAssembly> + <assemblyIdentity name="IceSSL"/> + <codeBase version="3.6.0.0" href="$(assembliesRelativeDir)\IceSSL.dll"/> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration> +<<KEEP + +install:: all + copy $(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif + +install:: all + copy $(ICEBOXNET) "$(install_bindir)" + copy $(ICEBOXNET).config "$(install_bindir)" +!if "$(DEBUG)" == "yes" + copy $(bindir)\iceboxnet.pdb "$(install_bindir)" +!endif + +clean:: + del /q $(ICEBOXNET).config diff --git a/csharp/src/IceBox/Server.cs b/csharp/src/IceBox/Server.cs new file mode 100644 index 00000000000..bdd9d048bf5 --- /dev/null +++ b/csharp/src/IceBox/Server.cs @@ -0,0 +1,77 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections.Generic; + +namespace IceBox +{ + +public class Server +{ + public class App : Ice.Application + { + private static void usage() + { + Console.Error.WriteLine("Usage: iceboxnet [options] --Ice.Config=<file>\n"); + Console.Error.WriteLine( + "Options:\n" + + "-h, --help Show this message.\n" + ); + } + + public override int run(string[] args) + { + List<String> argSeq = new List<String>(args); + const String prefix = "IceBox.Service."; + Ice.Properties properties = communicator().getProperties(); + Dictionary<string, string> services = properties.getPropertiesForPrefix(prefix); + foreach(KeyValuePair<string, string> pair in services) + { + String name = pair.Key.Substring(prefix.Length); + for(int i = 0; i < argSeq.Count; ++i) + { + if(argSeq[i].StartsWith("--" + name, StringComparison.CurrentCulture)) + { + argSeq.RemoveAt(i); + i--; + } + } + } + + foreach(String s in argSeq) + { + if(s.Equals("-h") || s.Equals("--help")) + { + usage(); + return 0; + } + else + { + Console.Error.WriteLine("Server: unknown option `" + s + "'"); + usage(); + return 1; + } + } + + ServiceManagerI serviceManagerImpl = new ServiceManagerI(communicator(), args); + return serviceManagerImpl.run(); + } + } + + public static int Main(string[] args) + { + Ice.InitializationData initData = new Ice.InitializationData(); + initData.properties = Ice.Util.createProperties(); + initData.properties.setProperty("Ice.Admin.DelayCreation", "1"); + App server = new App(); + return server.main(args, initData); + } +} +} diff --git a/csharp/src/IceBox/ServiceManagerI.cs b/csharp/src/IceBox/ServiceManagerI.cs new file mode 100644 index 00000000000..658a0284b07 --- /dev/null +++ b/csharp/src/IceBox/ServiceManagerI.cs @@ -0,0 +1,1072 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Diagnostics; + +namespace IceBox +{ + +// +// NOTE: the class isn't final on purpose to allow users to eventually +// extend it. +// +class ServiceManagerI : ServiceManagerDisp_ +{ + public ServiceManagerI(Ice.Communicator communicator, string[] args) + { + _communicator = communicator; + _logger = _communicator.getLogger(); + + Ice.Properties props = _communicator.getProperties(); + + if(props.getProperty("Ice.Admin.Enabled").Length == 0) + { + _adminEnabled = props.getProperty("Ice.Admin.Endpoints").Length > 0; + } + else + { + _adminEnabled = props.getPropertyAsInt("Ice.Admin.Enabled") > 0; + } + + if(_adminEnabled) + { + string[] facetFilter = props.getPropertyAsList("Ice.Admin.Facets"); + if(facetFilter.Length > 0) + { + _adminFacetFilter = new HashSet<string>(facetFilter); + } + else + { + _adminFacetFilter = new HashSet<string>(); + } + } + + _argv = args; + _traceServiceObserver = _communicator.getProperties().getPropertyAsInt("IceBox.Trace.ServiceObserver"); + } + + public override Dictionary<string, string> getSliceChecksums(Ice.Current current) + { + return Ice.SliceChecksums.checksums; + } + + public override void startService(string name, Ice.Current current) + { + ServiceInfo info = new ServiceInfo(); + lock(this) + { + // + // Search would be more efficient if services were contained in + // a map, but order is required for shutdown. + // + int i; + for(i = 0; i < _services.Count; ++i) + { + info = _services[i]; + if(info.name.Equals(name)) + { + if(_services[i].status != ServiceStatus.Stopped) + { + throw new AlreadyStartedException(); + } + info.status = ServiceStatus.Starting; + _services[i] = info; + break; + } + } + if(i == _services.Count) + { + throw new NoSuchServiceException(); + } + _pendingStatusChanges = true; + } + + bool started = false; + try + { + info.service.start(info.name, info.communicator == null ? _sharedCommunicator : info.communicator, + info.args); + started = true; + } + catch(Exception e) + { + _logger.warning("ServiceManager: exception while starting service " + info.name + ":\n" + e.ToString()); + } + + lock(this) + { + int i; + for(i = 0; i < _services.Count; ++i) + { + info = _services[i]; + if(info.name.Equals(name)) + { + if(started) + { + info.status = ServiceStatus.Started; + + List<string> services = new List<string>(); + services.Add(name); + servicesStarted(services, _observers.Keys); + } + else + { + info.status = ServiceStatus.Stopped; + } + _services[i] = info; + break; + } + } + _pendingStatusChanges = false; + System.Threading.Monitor.PulseAll(this); + } + } + + public override void stopService(string name, Ice.Current current) + { + ServiceInfo info = new ServiceInfo(); + lock(this) + { + // + // Search would be more efficient if services were contained in + // a map, but order is required for shutdown. + // + int i; + for(i = 0; i < _services.Count; ++i) + { + info = _services[i]; + if(info.name.Equals(name)) + { + if(info.status != ServiceStatus.Started) + { + throw new AlreadyStoppedException(); + } + info.status = ServiceStatus.Stopping; + _services[i] = info; + break; + } + } + if(i == _services.Count) + { + throw new NoSuchServiceException(); + } + _pendingStatusChanges = true; + } + + bool stopped = false; + try + { + info.service.stop(); + stopped = true; + } + catch(Exception e) + { + _logger.warning("ServiceManager: exception while stopping service " + info.name + "\n" + e.ToString()); + } + + lock(this) + { + int i; + for(i = 0; i < _services.Count; ++i) + { + info = _services[i]; + if(info.name.Equals(name)) + { + if(stopped) + { + info.status = ServiceStatus.Stopped; + + List<string> services = new List<string>(); + services.Add(name); + servicesStopped(services, _observers.Keys); + } + else + { + info.status = ServiceStatus.Started; + } + _services[i] = info; + break; + } + } + _pendingStatusChanges = false; + System.Threading.Monitor.PulseAll(this); + } + } + + public override void addObserver(ServiceObserverPrx observer, Ice.Current current) + { + List<string> activeServices = new List<string>(); + + // + // Null observers and duplicate registrations are ignored + // + + lock(this) + { + if(observer != null) + { + try + { + _observers.Add(observer, true); + } + catch(ArgumentException) + { + return; + } + + if(_traceServiceObserver >= 1) + { + _logger.trace("IceBox.ServiceObserver", + "Added service observer " + _communicator.proxyToString(observer)); + } + + foreach(ServiceInfo info in _services) + { + if(info.status == ServiceStatus.Started) + { + activeServices.Add(info.name); + } + } + } + } + + if(activeServices.Count > 0) + { + observer.begin_servicesStarted(activeServices.ToArray(), this.observerCompleted, null); + } + } + + public override void shutdown(Ice.Current current) + { + _communicator.shutdown(); + } + + public int run() + { + try + { + Ice.Properties properties = _communicator.getProperties(); + + // + // Create an object adapter. Services probably should NOT share + // this object adapter, as the endpoint(s) for this object adapter + // will most likely need to be firewalled for security reasons. + // + Ice.ObjectAdapter adapter = null; + if(properties.getProperty("IceBox.ServiceManager.Endpoints").Length != 0) + { + adapter = _communicator.createObjectAdapter("IceBox.ServiceManager"); + + Ice.Identity identity = new Ice.Identity(); + identity.category = properties.getPropertyWithDefault("IceBox.InstanceName", "IceBox"); + identity.name = "ServiceManager"; + adapter.add(this, identity); + } + + // + // Parse the property set with the prefix "IceBox.Service.". These + // properties should have the following format: + // + // IceBox.Service.Foo=<assembly>:Package.Foo [args] + // + // We parse the service properties specified in IceBox.LoadOrder + // first, then the ones from remaining services. + // + string prefix = "IceBox.Service."; + Dictionary<string, string> services = properties.getPropertiesForPrefix(prefix); + string[] loadOrder = properties.getPropertyAsList("IceBox.LoadOrder"); + List<StartServiceInfo> servicesInfo = new List<StartServiceInfo>(); + for(int i = 0; i < loadOrder.Length; ++i) + { + if(loadOrder[i].Length > 0) + { + string key = prefix + loadOrder[i]; + string value = services[key]; + if(value == null) + { + FailureException ex = new FailureException(); + ex.reason = "ServiceManager: no service definition for `" + loadOrder[i] + "'"; + throw ex; + } + servicesInfo.Add(new StartServiceInfo(loadOrder[i], value, _argv)); + services.Remove(key); + } + } + foreach(KeyValuePair<string, string> entry in services) + { + string name = entry.Key.Substring(prefix.Length); + string value = entry.Value; + servicesInfo.Add(new StartServiceInfo(name, value, _argv)); + } + + // + // Check if some services are using the shared communicator in which + // case we create the shared communicator now with a property set that + // is the union of all the service properties (from services that use + // the shared communicator). + // + if(properties.getPropertiesForPrefix("IceBox.UseSharedCommunicator.").Count > 0) + { + Ice.InitializationData initData = new Ice.InitializationData(); + initData.properties = createServiceProperties("SharedCommunicator"); + foreach(StartServiceInfo service in servicesInfo) + { + if(properties.getPropertyAsInt("IceBox.UseSharedCommunicator." + service.name) <= 0) + { + continue; + } + + // + // Load the service properties using the shared communicator properties as + // the default properties. + // + Ice.Properties svcProperties = Ice.Util.createProperties(ref service.args, initData.properties); + + // + // Remove properties from the shared property set that a service explicitly clears. + // + Dictionary<string, string> allProps = initData.properties.getPropertiesForPrefix(""); + foreach(string key in allProps.Keys) + { + if(svcProperties.getProperty(key).Length == 0) + { + initData.properties.setProperty(key, ""); + } + } + + // + // Add the service properties to the shared communicator properties. + // + foreach(KeyValuePair<string, string> entry in svcProperties.getPropertiesForPrefix("")) + { + initData.properties.setProperty(entry.Key, entry.Value); + } + + // + // Parse <service>.* command line options (the Ice command line options + // were parsed by the call to createProperties above). + // + service.args = initData.properties.parseCommandLineOptions(service.name, service.args); + } + + string facetNamePrefix = "IceBox.SharedCommunicator."; + bool addFacets = configureAdmin(initData.properties, facetNamePrefix); + + _sharedCommunicator = Ice.Util.initialize(initData); + + if(addFacets) + { + // Add all facets created on shared communicator to the IceBox communicator + // but renamed <prefix>.<facet-name>, except for the Process facet which is + // never added. + foreach(KeyValuePair<string, Ice.Object> p in _sharedCommunicator.findAllAdminFacets()) + { + if(!p.Key.Equals("Process")) + { + _communicator.addAdminFacet(p.Value, facetNamePrefix + p.Key); + } + } + } + } + + foreach(StartServiceInfo s in servicesInfo) + { + startService(s.name, s.entryPoint, s.args); + } + + // + // We may want to notify external scripts that the services + // have started. This is done by defining the property: + // + // PrintServicesReady=bundleName + // + // Where bundleName is whatever you choose to call this set of + // services. It will be echoed back as "bundleName ready". + // + // This must be done after start() has been invoked on the + // services. + // + string bundleName = properties.getProperty("IceBox.PrintServicesReady"); + if(bundleName.Length > 0) + { + Console.Out.WriteLine(bundleName + " ready"); + } + + // + // Don't move after the adapter activation. This allows + // applications to wait for the service manager to be + // reachable before sending a signal to shutdown the + // + // + Ice.Application.shutdownOnInterrupt(); + + // + // Register "this" as a facet to the Admin object and create Admin object + // + try + { + _communicator.addAdminFacet(this, "IceBox.ServiceManager"); + _communicator.getAdmin(); + } + catch(Ice.ObjectAdapterDeactivatedException) + { + // + // Expected if the communicator has been shutdown. + // + } + + // + // Start request dispatching after we've started the services. + // + if(adapter != null) + { + try + { + adapter.activate(); + } + catch(Ice.ObjectAdapterDeactivatedException) + { + // + // Expected if the communicator has been shutdown. + // + } + } + + _communicator.waitForShutdown(); + } + catch(FailureException ex) + { + _logger.error(ex.ToString()); + return 1; + } + catch(Exception ex) + { + _logger.error("ServiceManager: caught exception:\n" + ex.ToString()); + return 1; + } + finally + { + // + // Invoke stop() on the services. + // + stopAll(); + } + + return 0; + } + + private void startService(string service, string entryPoint, string[] args) + { + lock(this) + { + // + // Extract the assembly name and the class name. + // + string err = "ServiceManager: unable to load service '" + entryPoint + "': "; + int sepPos = entryPoint.IndexOf(':'); + if(sepPos != -1 && IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows) + { + const string driveLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if(entryPoint.Length > 3 && + sepPos == 1 && + driveLetters.IndexOf(entryPoint[0]) != -1 && + (entryPoint[2] == '\\' || entryPoint[2] == '/')) + { + sepPos = entryPoint.IndexOf(':', 3); + } + } + if(sepPos == -1) + { + FailureException e = new FailureException(); + e.reason = err + "invalid entry point format"; + throw e; + } + + System.Reflection.Assembly serviceAssembly = null; + string assemblyName = entryPoint.Substring(0, sepPos); + string className = entryPoint.Substring(sepPos + 1); + + try + { + // + // First try to load the assembly using Assembly.Load, which will succeed + // if a fully-qualified name is provided or if a partial name has been qualified + // in configuration. If that fails, try Assembly.LoadFrom(), which will succeed + // if a file name is configured or a partial name is configured and DEVPATH is used. + // + try + { + serviceAssembly = System.Reflection.Assembly.Load(assemblyName); + } + catch(System.IO.IOException ex) + { + try + { + serviceAssembly = System.Reflection.Assembly.LoadFrom(assemblyName); + } + catch(System.IO.IOException) + { + throw ex; + } + } + } + catch(System.Exception ex) + { + FailureException e = new FailureException(ex); + e.reason = err + "unable to load assembly: " + assemblyName; + throw e; + } + + // + // Instantiate the class. + // + System.Type c = null; + try + { + c = serviceAssembly.GetType(className, true); + } + catch(System.Exception ex) + { + FailureException e = new FailureException(ex); + e.reason = err + "GetType failed for '" + className + "'"; + throw e; + } + + ServiceInfo info = new ServiceInfo(); + info.name = service; + info.status = ServiceStatus.Stopped; + info.args = args; + + // + // If IceBox.UseSharedCommunicator.<name> is defined, create a + // communicator for the service. The communicator inherits + // from the shared communicator properties. If it's not + // defined, add the service properties to the shared + // commnunicator property set. + // + Ice.Communicator communicator; + if(_communicator.getProperties().getPropertyAsInt("IceBox.UseSharedCommunicator." + service) > 0) + { + Debug.Assert(_sharedCommunicator != null); + communicator = _sharedCommunicator; + } + else + { + // + // Create the service properties. We use the communicator properties as the default + // properties if IceBox.InheritProperties is set. + // + Ice.InitializationData initData = new Ice.InitializationData(); + initData.properties = createServiceProperties(service); + if(info.args.Length > 0) + { + // + // Create the service properties with the given service arguments. This should + // read the service config file if it's specified with --Ice.Config. + // + initData.properties = Ice.Util.createProperties(ref info.args, initData.properties); + + // + // Next, parse the service "<service>.*" command line options (the Ice command + // line options were parsed by the createProperties above) + // + info.args = initData.properties.parseCommandLineOptions(service, info.args); + } + + // + // Clone the logger to assign a new prefix. If one of the built-in loggers is configured + // don't set any logger. + // + if(initData.properties.getProperty("Ice.LogFile").Length == 0 && + (initData.properties.getPropertyAsInt("Ice.UseSyslog") <= 0 || + IceInternal.AssemblyUtil.platform_ == IceInternal.AssemblyUtil.Platform.Windows)) + { + initData.logger = _logger.cloneWithPrefix(initData.properties.getProperty("Ice.ProgramName")); + } + + // + // If Admin is enabled on the IceBox communicator, for each service that does not set + // Ice.Admin.Enabled, we set Ice.Admin.Enabled=1 to have this service create facets; then + // we add these facets to the IceBox Admin object as IceBox.Service.<service>.<facet>. + // + string serviceFacetNamePrefix = "IceBox.Service." + service + "."; + bool addFacets = configureAdmin(initData.properties, serviceFacetNamePrefix); + + // + // Remaining command line options are passed to the communicator. This is + // necessary for Ice plug-in properties (e.g.: IceSSL). + // + info.communicator = Ice.Util.initialize(ref info.args, initData); + communicator = info.communicator; + + if(addFacets) + { + // Add all facets created on the service communicator to the IceBox communicator + // but renamed IceBox.Service.<service>.<facet-name>, except for the Process facet + // which is never added + foreach(KeyValuePair<string, Ice.Object> p in communicator.findAllAdminFacets()) + { + if(!p.Key.Equals("Process")) + { + _communicator.addAdminFacet(p.Value, serviceFacetNamePrefix + p.Key); + } + } + } + } + + try + { + // + // Instantiate the service. + // + try + { + // + // If the service class provides a constructor that accepts an Ice.Communicator argument, + // use that in preference to the default constructor. + // + Type[] parameterTypes = new Type[1]; + parameterTypes[0] = typeof(Ice.Communicator); + System.Reflection.ConstructorInfo ci = c.GetConstructor(parameterTypes); + if(ci != null) + { + try + { + Object[] parameters = new Object[1]; + parameters[0] = _communicator; + info.service = (Service)ci.Invoke(parameters); + } + catch(System.MethodAccessException ex) + { + FailureException e = new FailureException(ex); + e.reason = err + "unable to access service constructor " + className + "(Ice.Communicator)"; + throw e; + } + } + else + { + // + // Fall back to the default constructor. + // + try + { + info.service = (Service)IceInternal.AssemblyUtil.createInstance(c); + if(info.service == null) + { + FailureException e = new FailureException(); + e.reason = err + "no default constructor for '" + className + "'"; + throw e; + } + } + catch(System.UnauthorizedAccessException ex) + { + FailureException e = new FailureException(ex); + e.reason = err + "unauthorized access to default service constructor for " + className; + throw e; + } + } + } + catch(FailureException) + { + throw; + } + catch(System.InvalidCastException ex) + { + FailureException e = new FailureException(ex); + e.reason = err + "service does not implement IceBox.Service"; + throw e; + } + catch(System.Reflection.TargetInvocationException ex) + { + if(ex.InnerException is IceBox.FailureException) + { + throw ex.InnerException; + } + else + { + FailureException e = new FailureException(ex.InnerException); + e.reason = err + "exception in service constructor for " + className; + throw e; + } + } + catch(System.Exception ex) + { + FailureException e = new FailureException(ex); + e.reason = err + "exception in service constructor " + className; + throw e; + } + + + try + { + info.service.start(service, communicator, info.args); + } + catch(FailureException) + { + throw; + } + catch(System.Exception ex) + { + FailureException e = new FailureException(ex); + e.reason = "exception while starting service " + service; + throw e; + } + + info.status = ServiceStatus.Started; + _services.Add(info); + } + catch(System.Exception ex) + { + if(info.communicator != null) + { + destroyServiceCommunicator(service, info.communicator); + } + + throw ex; + } + + } + } + + private void stopAll() + { + lock(this) + { + // + // First wait for any active startService/stopService calls to complete. + // + while(_pendingStatusChanges) + { + System.Threading.Monitor.Wait(this); + } + + // + // For each service, we call stop on the service and flush its database environment to + // the disk. Services are stopped in the reverse order of the order they were started. + // + _services.Reverse(); + List<string> stoppedServices = new List<string>(); + foreach(ServiceInfo info in _services) + { + if(info.status == ServiceStatus.Started) + { + try + { + info.service.stop(); + stoppedServices.Add(info.name); + } + catch(System.Exception e) + { + _logger.warning("IceBox.ServiceManager: exception while stopping service " + info.name + ":\n" + + e.ToString()); + } + } + + if(info.communicator != null) + { + destroyServiceCommunicator(info.name, info.communicator); + } + } + + if(_sharedCommunicator != null) + { + removeAdminFacets("IceBox.SharedCommunicator."); + + try + { + _sharedCommunicator.destroy(); + } + catch(System.Exception e) + { + _logger.warning("ServiceManager: exception while destroying shared communicator:\n" + e.ToString()); + } + _sharedCommunicator = null; + } + + _services.Clear(); + servicesStopped(stoppedServices, _observers.Keys); + } + } + + private void servicesStarted(List<String> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers) + { + // + // Must be called with 'this' unlocked + // + + if(services.Count > 0) + { + string[] servicesArray = services.ToArray(); + + foreach(ServiceObserverPrx observer in observers) + { + observer.begin_servicesStarted(servicesArray, this.observerCompleted, null); + } + } + } + + private void servicesStopped(List<string> services, Dictionary<ServiceObserverPrx, bool>.KeyCollection observers) + { + // + // Must be called with 'this' unlocked + // + + if(services.Count > 0) + { + string[] servicesArray = services.ToArray(); + + foreach(ServiceObserverPrx observer in observers) + { + observer.begin_servicesStopped(servicesArray, this.observerCompleted, null); + } + } + } + + private void + observerCompleted(Ice.AsyncResult result) + { + try + { + result.throwLocalException(); + } + catch(Ice.LocalException ex) + { + lock(this) + { + ServiceObserverPrx observer = ServiceObserverPrxHelper.uncheckedCast(result.getProxy()); + if(_observers.Remove(observer)) + { + observerRemoved(observer, ex); + } + } + } + } + + private void observerRemoved(ServiceObserverPrx observer, System.Exception ex) + { + if(_traceServiceObserver >= 1) + { + // + // CommunicatorDestroyedException may occur during shutdown. The observer notification has + // been sent, but the communicator was destroyed before the reply was received. We do not + // log a message for this exception. + // + if(!(ex is Ice.CommunicatorDestroyedException)) + { + _logger.trace("IceBox.ServiceObserver", + "Removed service observer " + _communicator.proxyToString(observer) + + "\nafter catching " + ex.ToString()); + } + } + } + + private enum ServiceStatus + { + Stopping, + Stopped, + Starting, + Started + } + + struct ServiceInfo + { + public string name; + public Service service; + public Ice.Communicator communicator; + public ServiceStatus status; + public string[] args; + } + + class StartServiceInfo + { + public StartServiceInfo(string service, string value, string[] serverArgs) + { + // + // Separate the entry point from the arguments. + // + name = service; + + try + { + args = IceUtilInternal.Options.split(value); + } + catch(IceUtilInternal.Options.BadQuote ex) + { + FailureException e = new FailureException(); + e.reason = "ServiceManager: invalid arguments for service `" + name + "':\n" + ex.Message; + throw e; + } + + Debug.Assert(args.Length > 0); + + entryPoint = args[0]; + + // + // Shift the arguments. + // + string[] tmp = new string[args.Length - 1]; + Array.Copy(args, 1, tmp, 0, args.Length - 1); + args = tmp; + + if(serverArgs.Length > 0) + { + ArrayList l = new ArrayList(); + for(int j = 0; j < args.Length; j++) + { + l.Add(args[j]); + } + for(int j = 0; j < serverArgs.Length; j++) + { + if(serverArgs[j].StartsWith("--" + service + ".", StringComparison.Ordinal)) + { + l.Add(serverArgs[j]); + } + } + args = (string[])l.ToArray(typeof(string)); + } + } + + public string name; + public string entryPoint; + public string[] args; + } + + private Ice.Properties createServiceProperties(String service) + { + Ice.Properties properties; + Ice.Properties communicatorProperties = _communicator.getProperties(); + if(communicatorProperties.getPropertyAsInt("IceBox.InheritProperties") > 0) + { + properties = communicatorProperties.ice_clone_(); + // Inherit all except Ice.Admin.xxx properties + foreach(string p in properties.getPropertiesForPrefix("Ice.Admin.").Keys) + { + properties.setProperty(p, ""); + } + } + else + { + properties = Ice.Util.createProperties(); + } + + String programName = communicatorProperties.getProperty("Ice.ProgramName"); + if(programName.Length == 0) + { + properties.setProperty("Ice.ProgramName", service); + } + else + { + properties.setProperty("Ice.ProgramName", programName + "-" + service); + } + return properties; + } + + private void destroyServiceCommunicator(string service, Ice.Communicator communicator) + { + if(communicator != null) + { + try + { + communicator.shutdown(); + communicator.waitForShutdown(); + } + catch(Ice.CommunicatorDestroyedException) + { + // + // Ignore, the service might have already destroyed + // the communicator for its own reasons. + // + } + catch(System.Exception e) + { + _logger.warning("ServiceManager: exception while shutting down communicator for service " + + service + "\n" + e.ToString()); + } + + removeAdminFacets("IceBox.Service." + service + "."); + + try + { + communicator.destroy(); + } + catch(System.Exception e) + { + _logger.warning("ServiceManager: exception while destroying communicator for service " + + service + "\n" + e.ToString()); + } + } + } + + private bool configureAdmin(Ice.Properties properties, string prefix) + { + if(_adminEnabled && properties.getProperty("Ice.Admin.Enabled").Length == 0) + { + List<string> facetNames = new List<string>(); + foreach(string p in _adminFacetFilter) + { + if(p.StartsWith(prefix)) + { + facetNames.Add(p.Substring(prefix.Length)); + } + } + + if(_adminFacetFilter.Count == 0 || facetNames.Count > 0) + { + properties.setProperty("Ice.Admin.Enabled", "1"); + + if(facetNames.Count > 0) + { + // TODO: need String.Join with escape! + properties.setProperty("Ice.Admin.Facets", String.Join(" ", facetNames.ToArray())); + } + return true; + } + } + return false; + } + + private void removeAdminFacets(string prefix) + { + try + { + foreach(string p in _communicator.findAllAdminFacets().Keys) + { + if(p.StartsWith(prefix)) + { + _communicator.removeAdminFacet(p); + } + } + } + catch(Ice.CommunicatorDestroyedException) + { + // Ignored + } + catch(Ice.ObjectAdapterDeactivatedException) + { + // Ignored + } + } + + private Ice.Communicator _communicator; + private bool _adminEnabled = false; + private HashSet<string> _adminFacetFilter = null; + private Ice.Communicator _sharedCommunicator = null; + private Ice.Logger _logger; + private string[] _argv; // Filtered server argument vector + private List<ServiceInfo> _services = new List<ServiceInfo>(); + private bool _pendingStatusChanges = false; + private Dictionary<ServiceObserverPrx, bool> _observers = new Dictionary<ServiceObserverPrx, bool>(); + private int _traceServiceObserver = 0; +} + +} diff --git a/csharp/src/IceBox/generated/.gitignore b/csharp/src/IceBox/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IceBox/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IceDiscovery/.depend.mak b/csharp/src/IceDiscovery/.depend.mak new file mode 100644 index 00000000000..0f34b397f12 --- /dev/null +++ b/csharp/src/IceDiscovery/.depend.mak @@ -0,0 +1,4 @@ + +IceDiscovery.cs: \ + "$(slicedir)\IceDiscovery\IceDiscovery.ice" \ + "$(slicedir)/Ice/Identity.ice" diff --git a/csharp/src/IceDiscovery/AssemblyInfo.cs b/csharp/src/IceDiscovery/AssemblyInfo.cs new file mode 100644 index 00000000000..9fc80df085a --- /dev/null +++ b/csharp/src/IceDiscovery/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceDiscovery")] +[assembly: AssemblyDescription("IceDiscovery core run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceDiscovery for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceDiscovery/LocatorI.cs b/csharp/src/IceDiscovery/LocatorI.cs new file mode 100644 index 00000000000..b0adf04ce90 --- /dev/null +++ b/csharp/src/IceDiscovery/LocatorI.cs @@ -0,0 +1,210 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceDiscovery +{ + using System; + using System.Collections.Generic; + + class LocatorRegistryI : Ice.LocatorRegistryDisp_ + { + public + LocatorRegistryI(Ice.Communicator com) + { + _wellKnownProxy = com.stringToProxy("p").ice_locator(null).ice_router(null).ice_collocationOptimized(true); + } + + public override void + setAdapterDirectProxy_async(Ice.AMD_LocatorRegistry_setAdapterDirectProxy cb, + string adapterId, + Ice.ObjectPrx proxy, + Ice.Current current) + { + lock(this) + { + if(proxy != null) + { + _adapters.Add(adapterId, proxy); + } + else + { + _adapters.Remove(adapterId); + } + cb.ice_response(); + } + } + + public override void + setReplicatedAdapterDirectProxy_async(Ice.AMD_LocatorRegistry_setReplicatedAdapterDirectProxy cb, + string adapterId, + string replicaGroupId, + Ice.ObjectPrx proxy, + Ice.Current current) + { + lock(this) + { + if(proxy != null) + { + _adapters.Add(adapterId, proxy); + HashSet<string> adapterIds; + if(!_replicaGroups.TryGetValue(replicaGroupId, out adapterIds)) + { + adapterIds = new HashSet<string>(); + _replicaGroups.Add(replicaGroupId, adapterIds); + } + adapterIds.Add(adapterId); + } + else + { + _adapters.Remove(adapterId); + HashSet<string> adapterIds; + if(_replicaGroups.TryGetValue(replicaGroupId, out adapterIds)) + { + adapterIds.Remove(adapterId); + if(adapterIds.Count == 0) + { + _replicaGroups.Remove(replicaGroupId); + } + } + } + } + cb.ice_response(); + } + + public override void + setServerProcessProxy_async(Ice.AMD_LocatorRegistry_setServerProcessProxy cb, + string id, + Ice.ProcessPrx process, + Ice.Current current) + { + cb.ice_response(); + } + + internal Ice.ObjectPrx findObject(Ice.Identity id) + { + lock(this) + { + if(id.name.Length == 0) + { + return null; + } + + Ice.ObjectPrx prx = _wellKnownProxy.ice_identity(id); + + List<string> adapterIds = new List<string>(); + foreach(KeyValuePair<string, HashSet<string>> entry in _replicaGroups) + { + try + { + prx.ice_adapterId(entry.Key).ice_ping(); + adapterIds.Add(entry.Key); + } + catch(Ice.Exception) + { + } + } + if(adapterIds.Count == 0) + { + foreach(KeyValuePair<string, Ice.ObjectPrx> entry in _adapters) + { + try + { + prx.ice_adapterId(entry.Key).ice_ping(); + adapterIds.Add(entry.Key); + } + catch(Ice.Exception) + { + } + } + } + + if(adapterIds.Count == 0) + { + return null; + } + //adapterIds.Suffle(); + return prx.ice_adapterId(adapterIds[0]); + } + } + + internal Ice.ObjectPrx findAdapter(string adapterId, out bool isReplicaGroup) + { + lock(this) + { + Ice.ObjectPrx result = null; + if(_adapters.TryGetValue(adapterId, out result)) + { + isReplicaGroup = false; + return result; + } + + HashSet<string> adapterIds; + if(_replicaGroups.TryGetValue(adapterId, out adapterIds)) + { + List<Ice.Endpoint> endpoints = new List<Ice.Endpoint>(); + foreach(string a in adapterIds) + { + Ice.ObjectPrx proxy; + if(!_adapters.TryGetValue(a, out proxy)) + { + continue; // TODO: Inconsistency + } + + if(result == null) + { + result = proxy; + } + + endpoints.AddRange(proxy.ice_getEndpoints()); + } + + if(result != null) + { + isReplicaGroup = true; + return result.ice_endpoints(endpoints.ToArray()); + } + } + + isReplicaGroup = false; + return null; + } + } + + private readonly Ice.ObjectPrx _wellKnownProxy; + private Dictionary<string, Ice.ObjectPrx> _adapters = new Dictionary<string, Ice.ObjectPrx>(); + private Dictionary<string, HashSet<string>> _replicaGroups = new Dictionary<string, HashSet<string>>(); + }; + + class LocatorI : Ice.LocatorDisp_ + { + public LocatorI(LookupI lookup, Ice.LocatorRegistryPrx registry) + { + _lookup = lookup; + _registry = registry; + } + + public override void findObjectById_async(Ice.AMD_Locator_findObjectById cb, Ice.Identity id, Ice.Current c) + { + _lookup.findObject(cb, id); + } + + public override void findAdapterById_async(Ice.AMD_Locator_findAdapterById cb, string adapterId, Ice.Current c) + { + _lookup.findAdapter(cb, adapterId); + } + + public override Ice.LocatorRegistryPrx getRegistry(Ice.Current current) + { + return _registry; + } + + private LookupI _lookup; + private Ice.LocatorRegistryPrx _registry; + }; +}
\ No newline at end of file diff --git a/csharp/src/IceDiscovery/LookupI.cs b/csharp/src/IceDiscovery/LookupI.cs new file mode 100644 index 00000000000..27a143f3d9d --- /dev/null +++ b/csharp/src/IceDiscovery/LookupI.cs @@ -0,0 +1,385 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceDiscovery +{ + using System; + using System.Collections.Generic; + + class Request<T, AmdCB> + { + protected Request(LookupI lookup, T id, int retryCount) + { + lookup_ = lookup; + nRetry_ = retryCount; + _id = id; + } + + public T getId() + { + return _id; + } + + public bool addCallback(AmdCB cb) + { + callbacks_.Add(cb); + return callbacks_.Count == 1; + } + + public virtual bool retry() + { + return --nRetry_ >= 0; + } + + protected LookupI lookup_; + protected int nRetry_; + protected List<AmdCB> callbacks_ = new List<AmdCB>(); + + private T _id; + }; + + class AdapterRequest : Request<string, Ice.AMD_Locator_findAdapterById>, IceInternal.TimerTask + { + public AdapterRequest(LookupI lookup, string id, int retryCount) : base(lookup, id, retryCount) + { + _start = System.DateTime.Now.Ticks; + } + + public override bool retry() + { + return _proxies.Count == 0 && --nRetry_ >= 0; + } + + public bool response(Ice.ObjectPrx proxy, bool isReplicaGroup) + { + if(isReplicaGroup) + { + _proxies.Add(proxy); + if(_latency == 0) + { + _latency = (long)((System.DateTime.Now.Ticks - _start) * lookup_.latencyMultiplier() / 10000.0); + if(_latency == 0) + { + _latency = 1; // 1ms + } + lookup_.timer().cancel(this); + lookup_.timer().schedule(this, _latency); + } + return false; + } + finished(proxy); + return true; + } + + public void finished(Ice.ObjectPrx proxy) + { + if(proxy != null || _proxies.Count == 0) + { + sendResponse(proxy); + return; + } + else if(_proxies.Count == 1) + { + sendResponse(_proxies[0]); + return; + } + + List<Ice.Endpoint> endpoints = new List<Ice.Endpoint>(); + Ice.ObjectPrx result = null; + foreach(Ice.ObjectPrx prx in _proxies) + { + if(result == null) + { + result = prx; + } + endpoints.AddRange(prx.ice_getEndpoints()); + } + sendResponse(result.ice_endpoints(endpoints.ToArray())); + } + + public void runTimerTask() + { + lookup_.adapterRequestTimedOut(this); + } + + private void sendResponse(Ice.ObjectPrx proxy) + { + foreach(Ice.AMD_Locator_findAdapterById cb in callbacks_) + { + cb.ice_response(proxy); + } + callbacks_.Clear(); + } + + private List<Ice.ObjectPrx> _proxies = new List<Ice.ObjectPrx>(); + private long _start; + private long _latency; + }; + + class ObjectRequest : Request<Ice.Identity, Ice.AMD_Locator_findObjectById>, IceInternal.TimerTask + { + public ObjectRequest(LookupI lookup, Ice.Identity id, int retryCount) : base(lookup, id, retryCount) + { + } + + public void response(Ice.ObjectPrx proxy) + { + finished(proxy); + } + + public void finished(Ice.ObjectPrx proxy) + { + foreach(Ice.AMD_Locator_findObjectById cb in callbacks_) + { + cb.ice_response(proxy); + } + callbacks_.Clear(); + } + + public void runTimerTask() + { + lookup_.objectRequestTimedOut(this); + } + }; + + class LookupI : LookupDisp_ + { + public LookupI(LocatorRegistryI registry, LookupPrx lookup, Ice.Properties properties) + { + _registry = registry; + _lookup = lookup; + _timeout = properties.getPropertyAsIntWithDefault("IceDiscovery.Timeout", 300); + _retryCount = properties.getPropertyAsIntWithDefault("IceDiscovery.RetryCount", 3); + _latencyMultiplier = properties.getPropertyAsIntWithDefault("IceDiscovery.LatencyMultiplier", 1); + _domainId = properties.getProperty("IceDiscovery.DomainId"); + _timer = IceInternal.Util.getInstance(lookup.ice_getCommunicator()).timer(); + } + + public void setLookupReply(LookupReplyPrx lookupReply) + { + _lookupReply = lookupReply; + } + + public override void findObjectById(string domainId, Ice.Identity id, IceDiscovery.LookupReplyPrx reply, + Ice.Current c) + { + if(!domainId.Equals(_domainId)) + { + return; // Ignore + } + + Ice.ObjectPrx proxy = _registry.findObject(id); + if(proxy != null) + { + // + // Reply to the mulicast request using the given proxy. + // + getLookupReply(reply, c).begin_foundObjectById(id, proxy); + } + } + + public override void findAdapterById(string domainId, string adapterId, IceDiscovery.LookupReplyPrx reply, + Ice.Current c) + { + if(!domainId.Equals(_domainId)) + { + return; // Ignore + } + + bool isReplicaGroup; + Ice.ObjectPrx proxy = _registry.findAdapter(adapterId, out isReplicaGroup); + if(proxy != null) + { + // + // Reply to the multicast request using the given proxy. + // + getLookupReply(reply, c).begin_foundAdapterById(adapterId, proxy, isReplicaGroup); + } + } + + internal void findObject(Ice.AMD_Locator_findObjectById cb, Ice.Identity id) + { + lock(this) + { + ObjectRequest request; + if(!_objectRequests.TryGetValue(id, out request)) + { + request = new ObjectRequest(this, id, _retryCount); + _objectRequests.Add(id, request); + } + if(request.addCallback(cb)) + { + _lookup.begin_findObjectById(_domainId, id, _lookupReply); + _timer.schedule(request, _timeout); + } + } + } + + internal void findAdapter(Ice.AMD_Locator_findAdapterById cb, string adapterId) + { + lock(this) + { + AdapterRequest request; + if(!_adapterRequests.TryGetValue(adapterId, out request)) + { + request = new AdapterRequest(this, adapterId, _retryCount); + _adapterRequests.Add(adapterId, request); + } + if(request.addCallback(cb)) + { + _lookup.begin_findAdapterById(_domainId, adapterId, _lookupReply); + _timer.schedule(request, _timeout); + } + } + } + + internal void foundObject(Ice.Identity id, Ice.ObjectPrx proxy) + { + lock(this) + { + ObjectRequest request; + if(!_objectRequests.TryGetValue(id, out request)) + { + return; + } + request.response(proxy); + _timer.cancel(request); + _objectRequests.Remove(id); + } + } + + internal void foundAdapter(string adapterId, Ice.ObjectPrx proxy, bool isReplicaGroup) + { + lock(this) + { + AdapterRequest request; + if(!_adapterRequests.TryGetValue(adapterId, out request)) + { + return; + } + + if(request.response(proxy, isReplicaGroup)) + { + _timer.cancel(request); + _adapterRequests.Remove(request.getId()); + } + } + } + + internal void objectRequestTimedOut(ObjectRequest request) + { + lock(this) + { + ObjectRequest r; + if(!_objectRequests.TryGetValue(request.getId(), out r) || r != request) + { + return; + } + + if(request.retry()) + { + _lookup.begin_findObjectById(_domainId, request.getId(), _lookupReply); + _timer.schedule(request, _timeout); + } + else + { + request.finished(null); + _objectRequests.Remove(request.getId()); + _timer.cancel(request); + } + } + } + + internal void adapterRequestTimedOut(AdapterRequest request) + { + lock(this) + { + AdapterRequest r; + if(!_adapterRequests.TryGetValue(request.getId(), out r) || r != request) + { + return; + } + + if(request.retry()) + { + _lookup.begin_findAdapterById(_domainId, request.getId(), _lookupReply); + _timer.schedule(request, _timeout); + } + else + { + request.finished(null); + _adapterRequests.Remove(request.getId()); + _timer.cancel(request); + } + } + } + + internal IceInternal.Timer timer() + { + return _timer; + } + + internal int latencyMultiplier() + { + return _latencyMultiplier; + } + + private LookupReplyPrx getLookupReply(LookupReplyPrx reply, Ice.Current current) + { + // Ice.UDPConnectionInfo info = Ice.UDPConnectionInfoPtr.dynamicCast(current.con.getInfo()); + // if(info) + // { + // Ice.Communicator com = current.adapter.getCommunicator(); + // ostringstream os; + // os << "\"" << com.identityToString(reply.ice_getIdentity()) << "\""; + // os << ":udp -h " << info.remoteAddress << " -p " << info.remotePort; + // return LookupReplyPrx.uncheckedCast(com.stringToProxy(os.str()).ice_datagram()); + // } + // else + { + return reply; + } + } + + private LocatorRegistryI _registry; + private readonly LookupPrx _lookup; + private LookupReplyPrx _lookupReply; + private readonly int _timeout; + private readonly int _retryCount; + private readonly int _latencyMultiplier; + private readonly string _domainId; + + private IceInternal.Timer _timer; + + private Dictionary<Ice.Identity, ObjectRequest> _objectRequests = new Dictionary<Ice.Identity, ObjectRequest>(); + private Dictionary<string, AdapterRequest> _adapterRequests = new Dictionary<string, AdapterRequest>(); + }; + + class LookupReplyI : LookupReplyDisp_ + { + public LookupReplyI(LookupI lookup) + { + _lookup = lookup; + } + + public override void foundObjectById(Ice.Identity id, Ice.ObjectPrx proxy, Ice.Current c) + { + _lookup.foundObject(id, proxy); + } + + public override void foundAdapterById(string adapterId, Ice.ObjectPrx proxy, bool isReplicaGroup, Ice.Current c) + { + _lookup.foundAdapter(adapterId, proxy, isReplicaGroup); + } + + private LookupI _lookup; + }; + +} + diff --git a/csharp/src/IceDiscovery/Makefile b/csharp/src/IceDiscovery/Makefile new file mode 100644 index 00000000000..88581745332 --- /dev/null +++ b/csharp/src/IceDiscovery/Makefile @@ -0,0 +1,49 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IceDiscovery +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs \ + LocatorI.cs \ + LookupI.cs \ + PluginI.cs + +SLICE_SRCS = $(SDIR)/IceDiscovery.ice + +SDIR = $(slicedir)/IceDiscovery +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IceDiscovery/Makefile.mak b/csharp/src/IceDiscovery/Makefile.mak new file mode 100644 index 00000000000..0260afceba1 --- /dev/null +++ b/csharp/src/IceDiscovery/Makefile.mak @@ -0,0 +1,54 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IceDiscovery +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs \ + LocatorI.cs \ + LookupI.cs \ + PluginI.cs + +GEN_SRCS = $(GDIR)\IceDiscovery.cs + +SDIR = $(slicedir)\IceDiscovery +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/IceDiscovery/PluginI.cs b/csharp/src/IceDiscovery/PluginI.cs new file mode 100644 index 00000000000..ec846f016fd --- /dev/null +++ b/csharp/src/IceDiscovery/PluginI.cs @@ -0,0 +1,149 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceDiscovery +{ + using System; + using System.Text; + using System.Collections.Generic; + + public sealed class PluginFactory : Ice.PluginFactory + { + public Ice.Plugin + create(Ice.Communicator communicator, string name, string[] args) + { + return new PluginI(communicator); + } + } + + public sealed class PluginI : Ice.Plugin + { + public + PluginI(Ice.Communicator communicator) + { + _communicator = communicator; + } + + public void initialize() + { + Ice.Properties properties = _communicator.getProperties(); + + bool ipv4 = properties.getPropertyAsIntWithDefault("Ice.IPv4", 1) > 0; + bool preferIPv6 = properties.getPropertyAsInt("Ice.PreferIPv6Address") > 0; + string address; + if(ipv4 && !preferIPv6) + { + address = properties.getPropertyWithDefault("IceDiscovery.Address", "239.255.0.1"); + } + else + { + address = properties.getPropertyWithDefault("IceDiscovery.Address", "ff15::1"); + } + int port = properties.getPropertyAsIntWithDefault("IceDiscovery.Port", 4061); + string intf = properties.getProperty("IceDiscovery.Interface"); + + if(properties.getProperty("IceDiscovery.Multicast.Endpoints").Length == 0) + { + StringBuilder s = new StringBuilder(); + s.Append("udp -h \"").Append(address).Append("\" -p ").Append(port); + if(intf.Length != 0) + { + s.Append(" --interface \"").Append(intf).Append("\""); + } + properties.setProperty("IceDiscovery.Multicast.Endpoints", s.ToString()); + } + if(properties.getProperty("IceDiscovery.Reply.Endpoints").Length == 0) + { + StringBuilder s = new StringBuilder(); + s.Append("udp"); + if(intf.Length != 0) + { + s.Append(" -h \"").Append(intf).Append("\""); + } + properties.setProperty("IceDiscovery.Reply.Endpoints", s.ToString()); + } + if(properties.getProperty("IceDiscovery.Locator.Endpoints").Length == 0) + { + properties.setProperty("IceDiscovery.Locator.AdapterId", Guid.NewGuid().ToString()); + } + + _multicastAdapter = _communicator.createObjectAdapter("IceDiscovery.Multicast"); + _replyAdapter = _communicator.createObjectAdapter("IceDiscovery.Reply"); + _locatorAdapter = _communicator.createObjectAdapter("IceDiscovery.Locator"); + + // + // Setup locatory registry. + // + LocatorRegistryI locatorRegistry = new LocatorRegistryI(_communicator); + Ice.LocatorRegistryPrx locatorRegistryPrx = Ice.LocatorRegistryPrxHelper.uncheckedCast( + _locatorAdapter.addWithUUID(locatorRegistry)); + + string lookupEndpoints = properties.getProperty("IceDiscovery.Lookup"); + if(lookupEndpoints.Length == 0) + { + lookupEndpoints = "udp -h \"" + address + "\" -p " + port; + if(intf.Length > 0) + { + lookupEndpoints += " --interface \"" + intf + "\""; + } + } + + Ice.ObjectPrx lookupPrx = _communicator.stringToProxy("IceDiscovery/Lookup -d:" + lookupEndpoints); + lookupPrx = lookupPrx.ice_collocationOptimized(false); + try + { + lookupPrx.ice_getConnection(); + } + catch(Ice.LocalException ex) + { + StringBuilder b = new StringBuilder(); + b.Append("unable to establish multicast connection, IceDiscovery will be disabled:\n"); + b.Append("proxy = "); + b.Append(lookupPrx.ToString()); + b.Append('\n'); + b.Append(ex.ToString()); + throw new Ice.PluginInitializationException(b.ToString()); + } + + // + // Add lookup and lookup reply Ice objects + // + LookupI lookup = new LookupI(locatorRegistry, LookupPrxHelper.uncheckedCast(lookupPrx), properties); + _multicastAdapter.add(lookup, _communicator.stringToIdentity("IceDiscovery/Lookup")); + + Ice.ObjectPrx lookupReply = _replyAdapter.addWithUUID(new LookupReplyI(lookup)).ice_datagram(); + lookup.setLookupReply(LookupReplyPrxHelper.uncheckedCast(lookupReply)); + + // + // Setup locator on the communicator. + // + Ice.ObjectPrx loc; + loc = _locatorAdapter.addWithUUID( + new LocatorI(lookup, Ice.LocatorRegistryPrxHelper.uncheckedCast(locatorRegistryPrx))); + _communicator.setDefaultLocator(Ice.LocatorPrxHelper.uncheckedCast(loc)); + + _multicastAdapter.activate(); + _replyAdapter.activate(); + _locatorAdapter.activate(); + } + + public void destroy() + { + _multicastAdapter.destroy(); + _replyAdapter.destroy(); + _locatorAdapter.destroy(); + } + + private Ice.Communicator _communicator; + private Ice.ObjectAdapter _multicastAdapter; + private Ice.ObjectAdapter _replyAdapter; + private Ice.ObjectAdapter _locatorAdapter; + } + +}
\ No newline at end of file diff --git a/csharp/src/IceDiscovery/generated/.gitignore b/csharp/src/IceDiscovery/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IceDiscovery/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IceGrid/.depend.mak b/csharp/src/IceGrid/.depend.mak new file mode 100644 index 00000000000..c88e67a088b --- /dev/null +++ b/csharp/src/IceGrid/.depend.mak @@ -0,0 +1,92 @@ + +Admin.cs: \ + "$(slicedir)\IceGrid\Admin.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/Properties.ice" \ + "$(slicedir)/Ice/PropertiesAdmin.ice" \ + "$(slicedir)/Ice/SliceChecksumDict.ice" \ + "$(slicedir)/Glacier2/Session.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/IceGrid/Exception.ice" \ + "$(slicedir)/IceGrid/Descriptor.ice" + +Descriptor.cs: \ + "$(slicedir)\IceGrid\Descriptor.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +Discovery.cs: \ + "$(slicedir)\IceGrid\Discovery.ice" \ + "$(slicedir)/IceGrid/Locator.ice" \ + "$(slicedir)/Ice/Locator.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/ProcessF.ice" + +Exception.cs: \ + "$(slicedir)\IceGrid\Exception.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +FileParser.cs: \ + "$(slicedir)\IceGrid\FileParser.ice" \ + "$(slicedir)/IceGrid/Admin.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/Properties.ice" \ + "$(slicedir)/Ice/PropertiesAdmin.ice" \ + "$(slicedir)/Ice/SliceChecksumDict.ice" \ + "$(slicedir)/Glacier2/Session.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/IceGrid/Exception.ice" \ + "$(slicedir)/IceGrid/Descriptor.ice" + +Locator.cs: \ + "$(slicedir)\IceGrid\Locator.ice" \ + "$(slicedir)/Ice/Locator.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/ProcessF.ice" + +Observer.cs: \ + "$(slicedir)\IceGrid\Observer.ice" \ + "$(slicedir)/Glacier2/Session.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/IceGrid/Exception.ice" \ + "$(slicedir)/IceGrid/Descriptor.ice" \ + "$(slicedir)/IceGrid/Admin.ice" \ + "$(slicedir)/Ice/Properties.ice" \ + "$(slicedir)/Ice/PropertiesAdmin.ice" \ + "$(slicedir)/Ice/SliceChecksumDict.ice" + +Query.cs: \ + "$(slicedir)\IceGrid\Query.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/IceGrid/Exception.ice" + +Registry.cs: \ + "$(slicedir)\IceGrid\Registry.ice" \ + "$(slicedir)/IceGrid/Exception.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/IceGrid/Session.ice" \ + "$(slicedir)/Glacier2/Session.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/IceGrid/Admin.ice" \ + "$(slicedir)/Ice/Properties.ice" \ + "$(slicedir)/Ice/PropertiesAdmin.ice" \ + "$(slicedir)/Ice/SliceChecksumDict.ice" \ + "$(slicedir)/IceGrid/Descriptor.ice" + +Session.cs: \ + "$(slicedir)\IceGrid\Session.ice" \ + "$(slicedir)/Glacier2/Session.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Glacier2/SSLInfo.ice" \ + "$(slicedir)/IceGrid/Exception.ice" + +UserAccountMapper.cs: \ + "$(slicedir)\IceGrid\UserAccountMapper.ice" diff --git a/csharp/src/IceGrid/AssemblyInfo.cs b/csharp/src/IceGrid/AssemblyInfo.cs new file mode 100644 index 00000000000..55fe78eb54d --- /dev/null +++ b/csharp/src/IceGrid/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceGrid")] +[assembly: AssemblyDescription("IceGrid run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceGrid for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceGrid/Makefile b/csharp/src/IceGrid/Makefile new file mode 100644 index 00000000000..ec40bcd8cb7 --- /dev/null +++ b/csharp/src/IceGrid/Makefile @@ -0,0 +1,55 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IceGrid +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs + +SLICE_SRCS = $(SDIR)/Admin.ice \ + $(SDIR)/Descriptor.ice \ + $(SDIR)/Exception.ice \ + $(SDIR)/FileParser.ice \ + $(SDIR)/Locator.ice \ + $(SDIR)/Observer.ice \ + $(SDIR)/Query.ice \ + $(SDIR)/Registry.ice \ + $(SDIR)/Session.ice \ + $(SDIR)/UserAccountMapper.ice + +SDIR = $(slicedir)/IceGrid +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Glacier2) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IceGrid/Makefile.mak b/csharp/src/IceGrid/Makefile.mak new file mode 100644 index 00000000000..f76a1370602 --- /dev/null +++ b/csharp/src/IceGrid/Makefile.mak @@ -0,0 +1,60 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IceGrid +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs + +GEN_SRCS = $(GDIR)\Admin.cs \ + $(GDIR)\Descriptor.cs \ + $(GDIR)\Exception.cs \ + $(GDIR)\FileParser.cs \ + $(GDIR)\Locator.cs \ + $(GDIR)\Observer.cs \ + $(GDIR)\Query.cs \ + $(GDIR)\Registry.cs \ + $(GDIR)\Session.cs \ + $(GDIR)\UserAccountMapper.cs + +SDIR = $(slicedir)\IceGrid +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) /baseaddress:0x24000000 $(MCSFLAGS) -r:$(refdir)\Glacier2.dll -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/IceGrid/generated/.gitignore b/csharp/src/IceGrid/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IceGrid/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IceLocatorDiscovery/AssemblyInfo.cs b/csharp/src/IceLocatorDiscovery/AssemblyInfo.cs new file mode 100644 index 00000000000..0e758129db3 --- /dev/null +++ b/csharp/src/IceLocatorDiscovery/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceLocatorDiscovery")] +[assembly: AssemblyDescription("IceLocatorDiscovery core run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceLocatorDiscovery for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceLocatorDiscovery/Makefile b/csharp/src/IceLocatorDiscovery/Makefile new file mode 100644 index 00000000000..1499c9cd80e --- /dev/null +++ b/csharp/src/IceLocatorDiscovery/Makefile @@ -0,0 +1,47 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IceLocatorDiscovery +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs \ + PluginI.cs + +SLICE_SRCS = $(SDIR)/IceLocatorDiscovery.ice + +SDIR = $(slicedir)/IceLocatorDiscovery +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IceLocatorDiscovery/Makefile.mak b/csharp/src/IceLocatorDiscovery/Makefile.mak new file mode 100644 index 00000000000..bd0a9f729d2 --- /dev/null +++ b/csharp/src/IceLocatorDiscovery/Makefile.mak @@ -0,0 +1,52 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IceLocatorDiscovery +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs \ + PluginI.cs + +GEN_SRCS = $(GDIR)\IceLocatorDiscovery.cs + +SDIR = $(slicedir)\IceLocatorDiscovery +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/IceLocatorDiscovery/PluginI.cs b/csharp/src/IceLocatorDiscovery/PluginI.cs new file mode 100644 index 00000000000..ed000dc0d6c --- /dev/null +++ b/csharp/src/IceLocatorDiscovery/PluginI.cs @@ -0,0 +1,422 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceLocatorDiscovery +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Text; + + public sealed class PluginFactory : Ice.PluginFactory + { + public Ice.Plugin + create(Ice.Communicator communicator, string name, string[] args) + { + return new PluginI(communicator); + } + } + + internal class Request + { + public Request(LocatorI locator, + string operation, + Ice.OperationMode mode, + byte[] inParams, + Dictionary<string, string> context, + Ice.AMD_Object_ice_invoke amdCB) + { + _locator = locator; + _operation = operation; + _mode = mode; + _inParams = inParams; + _context = context; + _amdCB = amdCB; + } + + public void + invoke(Ice.LocatorPrx l) + { + _locatorPrx = l; + Request self = this; + l.begin_ice_invoke(_operation, _mode, _inParams, _context).whenCompleted( + (bool ok, byte[] outParams) => + { + _amdCB.ice_response(ok, outParams); + }, + (Ice.Exception ex) => + { + try + { + throw ex; + } + catch(Ice.RequestFailedException exc) + { + _amdCB.ice_exception(exc); + } + catch(Ice.UnknownException exc) + { + _amdCB.ice_exception(exc); + } + catch(Ice.Exception) + { + _locator.invoke(_locatorPrx, self); // Retry with new locator proxy + } + }); + } + + private readonly LocatorI _locator; + private readonly string _operation; + private readonly Ice.OperationMode _mode; + private readonly Dictionary<string, string> _context; + private readonly byte[] _inParams; + private readonly Ice.AMD_Object_ice_invoke _amdCB; + + private Ice.LocatorPrx _locatorPrx; + } + + internal class VoidLocatorI : Ice.LocatorDisp_ + { + public override void + findObjectById_async(Ice.AMD_Locator_findObjectById amdCB, Ice.Identity id, Ice.Current current) + { + amdCB.ice_response(null); + } + + public override void + findAdapterById_async(Ice.AMD_Locator_findAdapterById amdCB, String id, Ice.Current current) + { + amdCB.ice_response(null); + } + + public override Ice.LocatorRegistryPrx + getRegistry(Ice.Current current) + { + return null; + } + }; + + internal class LocatorI : Ice.BlobjectAsync, IceInternal.TimerTask + { + public + LocatorI(LookupPrx lookup, Ice.Properties properties, string instanceName, Ice.LocatorPrx voidLocator) + { + _lookup = lookup; + _timeout = properties.getPropertyAsIntWithDefault("IceLocatorDiscovery.Timeout", 300); + _retryCount = properties.getPropertyAsIntWithDefault("IceLocatorDiscovery.RetryCount", 3); + _retryDelay = properties.getPropertyAsIntWithDefault("IceLocatorDiscovery.RetryDelay", 2000); + _timer = IceInternal.Util.getInstance(lookup.ice_getCommunicator()).timer(); + _instanceName = instanceName; + _warned = false; + _locator = lookup.ice_getCommunicator().getDefaultLocator(); + _voidLocator = voidLocator; + _pendingRetryCount = 0; + } + + public void + setLookupReply(LookupReplyPrx lookupReply) + { + _lookupReply = lookupReply; + } + + public override void + ice_invoke_async(Ice.AMD_Object_ice_invoke amdCB, byte[] inParams, Ice.Current current) + { + lock(this) + { + invoke(null, new Request(this, current.operation, current.mode, inParams, current.ctx, amdCB)); + } + } + + public void + foundLocator(Ice.LocatorPrx locator) + { + lock(this) + { + if(locator == null || + (_instanceName.Length > 0 && !locator.ice_getIdentity().category.Equals(_instanceName))) + { + return; + } + + // + // If we already have a locator assigned, ensure the given locator + // has the same identity, otherwise ignore it. + // + if(_locator != null && !locator.ice_getIdentity().category.Equals(_locator.ice_getIdentity().category)) + { + if(!_warned) + { + _warned = true; // Only warn once + + locator.ice_getCommunicator().getLogger().warning( + "received Ice locator with different instance name:\n" + + "using = `" + _locator.ice_getIdentity().category + "'\n" + + "received = `" + locator.ice_getIdentity().category + "'\n" + + "This is typically the case if multiple Ice locators with different " + + "instance names are deployed and the property `IceLocatorDiscovery.InstanceName'" + + "is not set."); + + } + return; + } + + if(_pendingRetryCount > 0) // No need to retry, we found a locator + { + _timer.cancel(this); + _pendingRetryCount = 0; + } + + if(_locator != null) + { + // + // We found another locator replica, append its endpoints to the + // current locator proxy endpoints. + // + List<Ice.Endpoint> newEndpoints = new List<Ice.Endpoint>(_locator.ice_getEndpoints()); + foreach(Ice.Endpoint p in locator.ice_getEndpoints()) + { + // + // Only add endpoints if not already in the locator proxy endpoints + // + bool found = false; + foreach(Ice.Endpoint q in newEndpoints) + { + if(p.Equals(q)) + { + found = true; + break; + } + } + if(!found) + { + newEndpoints.Add(p); + } + } + _locator = (Ice.LocatorPrx) _locator.ice_endpoints(newEndpoints.ToArray()); + } + else + { + _locator = locator; + if(_instanceName.Length == 0) + { + _instanceName = _locator.ice_getIdentity().category; // Stick to the first locator + } + } + + // + // Send pending requests if any. + // + foreach(Request req in _pendingRequests) + { + req.invoke(_locator); + } + _pendingRequests.Clear(); + } + } + + public void + invoke(Ice.LocatorPrx locator, Request request) + { + lock(this) + { + if(_locator != null && _locator != locator) + { + request.invoke(_locator); + } + else if(IceInternal.Time.currentMonotonicTimeMillis() < _nextRetry) + { + request.invoke(_voidLocator); // Don't retry to find a locator before the retry delay expires + } + else + { + _locator = null; + + _pendingRequests.Add(request); + + if(_pendingRetryCount == 0) // No request in progress + { + _pendingRetryCount = _retryCount; + _lookup.begin_findLocator(_instanceName, _lookupReply); // Send multicast request. + _timer.schedule(this, _timeout); + } + } + } + } + + public void + runTimerTask() + { + lock(this) + { + if(--_pendingRetryCount > 0) + { + _lookup.begin_findLocator(_instanceName, _lookupReply); // Send multicast request + _timer.schedule(this, _timeout); + } + else + { + Debug.Assert(_pendingRequests.Count > 0, "Pending requests is not empty"); + foreach(Request req in _pendingRequests) + { + req.invoke(_voidLocator); + } + _pendingRequests.Clear(); + _nextRetry = IceInternal.Time.currentMonotonicTimeMillis() + _retryDelay; + } + } + } + + private LookupPrx _lookup; + private int _timeout; + private IceInternal.Timer _timer; + private int _retryCount; + private int _retryDelay; + + private string _instanceName; + private bool _warned; + private LookupReplyPrx _lookupReply; + private Ice.LocatorPrx _locator; + private Ice.LocatorPrx _voidLocator; + + private int _pendingRetryCount; + private List<Request> _pendingRequests = new List<Request>(); + private long _nextRetry; + }; + + internal class LookupReplyI : LookupReplyDisp_ + { + public LookupReplyI(LocatorI locator) + { + _locator = locator; + } + + public override void + foundLocator(Ice.LocatorPrx locator, Ice.Current current) + { + _locator.foundLocator(locator); + } + + private LocatorI _locator; + } + + class PluginI : Ice.Plugin + { + public + PluginI(Ice.Communicator communicator) + { + _communicator = communicator; + } + + public void + initialize() + { + Ice.Properties properties = _communicator.getProperties(); + + bool ipv4 = properties.getPropertyAsIntWithDefault("Ice.IPv4", 1) > 0; + bool preferIPv6 = properties.getPropertyAsInt("Ice.PreferIPv6Address") > 0; + string address; + if(ipv4 && !preferIPv6) + { + address = properties.getPropertyWithDefault("IceLocatorDiscovery.Address", "239.255.0.1"); + } + else + { + address = properties.getPropertyWithDefault("IceLocatorDiscovery.Address", "ff15::1"); + } + int port = properties.getPropertyAsIntWithDefault("IceLocatorDiscovery.Port", 4061); + string intf = properties.getProperty("IceLocatorDiscovery.Interface"); + + if(properties.getProperty("IceLocatorDiscovery.Reply.Endpoints").Length == 0) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("udp"); + if(intf.Length > 0) + { + s.Append(" -h \""); + s.Append(intf); + s.Append("\""); + } + properties.setProperty("IceLocatorDiscovery.Reply.Endpoints", s.ToString()); + } + if(properties.getProperty("IceLocatorDiscovery.Locator.Endpoints").Length == 0) + { + properties.setProperty("IceLocatorDiscovery.Locator.AdapterId", Guid.NewGuid().ToString()); + } + + _replyAdapter = _communicator.createObjectAdapter("IceLocatorDiscovery.Reply"); + _locatorAdapter = _communicator.createObjectAdapter("IceLocatorDiscovery.Locator"); + + // We don't want those adapters to be registered with the locator so clear their locator. + _replyAdapter.setLocator(null); + _locatorAdapter.setLocator(null); + + string lookupEndpoints = properties.getProperty("IceLocatorDiscovery.Lookup"); + if(lookupEndpoints.Length == 0) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("udp -h \""); + s.Append(address); + s.Append("\" -p "); + s.Append(port); + if(intf.Length > 0) + { + s.Append(" --interface \""); + s.Append(intf); + s.Append("\""); + } + lookupEndpoints = s.ToString(); + } + + Ice.ObjectPrx lookupPrx = _communicator.stringToProxy("IceLocatorDiscovery/Lookup -d:" + lookupEndpoints); + lookupPrx = lookupPrx.ice_collocationOptimized(false); // No colloc optimization for the multicast proxy! + try + { + lookupPrx.ice_getConnection(); // Ensure we can establish a connection to the multicast proxy + } + catch (Ice.LocalException ex) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("unable to establish multicast connection, Ice locator discovery will be disabled:\n"); + s.Append("proxy = "); + s.Append(lookupPrx.ToString()); + s.Append("\n"); + s.Append(ex); + throw new Ice.PluginInitializationException(s.ToString()); + } + + Ice.LocatorPrx voidLo = Ice.LocatorPrxHelper.uncheckedCast(_locatorAdapter.addWithUUID(new VoidLocatorI())); + + string instanceName = properties.getProperty("IceLocatorDiscovery.InstanceName"); + Ice.Identity id = new Ice.Identity(); + id.name = "Locator"; + id.category = instanceName.Length > 0 ? instanceName : Guid.NewGuid().ToString(); + + LocatorI locator = new LocatorI(LookupPrxHelper.uncheckedCast(lookupPrx), properties, instanceName, voidLo); + _communicator.setDefaultLocator(Ice.LocatorPrxHelper.uncheckedCast(_locatorAdapter.addWithUUID(locator))); + + Ice.ObjectPrx lookupReply = _replyAdapter.addWithUUID(new LookupReplyI(locator)).ice_datagram(); + locator.setLookupReply(LookupReplyPrxHelper.uncheckedCast(lookupReply)); + + _replyAdapter.activate(); + _locatorAdapter.activate(); + } + + public void + destroy() + { + _replyAdapter.destroy(); + _locatorAdapter.destroy(); + } + + private Ice.Communicator _communicator; + private Ice.ObjectAdapter _locatorAdapter; + private Ice.ObjectAdapter _replyAdapter; + } +} diff --git a/csharp/src/IceLocatorDiscovery/generated/.gitignore b/csharp/src/IceLocatorDiscovery/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IceLocatorDiscovery/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IcePatch2/.depend.mak b/csharp/src/IcePatch2/.depend.mak new file mode 100644 index 00000000000..c73ea5657cb --- /dev/null +++ b/csharp/src/IcePatch2/.depend.mak @@ -0,0 +1,9 @@ + +FileInfo.cs: \ + "$(slicedir)\IcePatch2\FileInfo.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +FileServer.cs: \ + "$(slicedir)\IcePatch2\FileServer.ice" \ + "$(slicedir)/IcePatch2/FileInfo.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" diff --git a/csharp/src/IcePatch2/AssemblyInfo.cs b/csharp/src/IcePatch2/AssemblyInfo.cs new file mode 100644 index 00000000000..8556d48518e --- /dev/null +++ b/csharp/src/IcePatch2/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IcePatch2")] +[assembly: AssemblyDescription("IcePatch2 run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IcePatch2 for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IcePatch2/Makefile b/csharp/src/IcePatch2/Makefile new file mode 100644 index 00000000000..4b64dab84df --- /dev/null +++ b/csharp/src/IcePatch2/Makefile @@ -0,0 +1,47 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IcePatch2 +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs + +SLICE_SRCS = $(SDIR)/FileInfo.ice \ + $(SDIR)/FileServer.ice + +SDIR = $(slicedir)/IcePatch2 +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) -I$(slicedir) --ice + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IcePatch2/Makefile.mak b/csharp/src/IcePatch2/Makefile.mak new file mode 100644 index 00000000000..2dd8c822309 --- /dev/null +++ b/csharp/src/IcePatch2/Makefile.mak @@ -0,0 +1,52 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IcePatch2 +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs + +GEN_SRCS = $(GDIR)\FileInfo.cs \ + $(GDIR)\FileServer.cs + +SDIR = $(slicedir)\IcePatch2 +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) -I$(slicedir) --ice + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) /baseaddress:0x23000000 $(MCSFLAGS) -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/IcePatch2/generated/.gitignore b/csharp/src/IcePatch2/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IcePatch2/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IceSSL/.depend.mak b/csharp/src/IceSSL/.depend.mak new file mode 100644 index 00000000000..96f75256468 --- /dev/null +++ b/csharp/src/IceSSL/.depend.mak @@ -0,0 +1,17 @@ + +ConnectionInfo.cs: \ + "$(slicedir)\IceSSL\ConnectionInfo.ice" \ + "$(slicedir)/Ice/Connection.ice" \ + "$(slicedir)/Ice/ObjectAdapterF.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/Endpoint.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/EndpointF.ice" + +EndpointInfo.cs: \ + "$(slicedir)\IceSSL\EndpointInfo.ice" \ + "$(slicedir)/Ice/Endpoint.ice" \ + "$(slicedir)/Ice/Version.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" \ + "$(slicedir)/Ice/EndpointF.ice" diff --git a/csharp/src/IceSSL/AcceptorI.cs b/csharp/src/IceSSL/AcceptorI.cs new file mode 100644 index 00000000000..5ddc84ff337 --- /dev/null +++ b/csharp/src/IceSSL/AcceptorI.cs @@ -0,0 +1,201 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Net; + using System.Net.Security; + using System.Net.Sockets; + using System.Security.Cryptography.X509Certificates; + using System.Text; + + class AcceptorI : IceInternal.Acceptor + { + public void close() + { + Debug.Assert(_acceptFd == null); + if(_fd != null) + { + IceInternal.Network.closeSocketNoThrow(_fd); + _fd = null; + } + } + + public IceInternal.EndpointI listen() + { + try + { + _addr = IceInternal.Network.doBind(_fd, _addr); + IceInternal.Network.doListen(_fd, _backlog); + } + catch(SystemException) + { + _fd = null; + throw; + } + _endpoint = _endpoint.endpoint(this); + return _endpoint; + } + + public bool startAccept(IceInternal.AsyncCallback callback, object state) + { + // + // The plug-in may not be fully initialized. + // + if(!_instance.initialized()) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: plug-in is not initialized"; + throw ex; + } + + try + { + _result = _fd.BeginAccept(delegate(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + callback(result.AsyncState); + } + }, state); + return _result.CompletedSynchronously; + } + catch(SocketException ex) + { + throw new Ice.SocketException(ex); + } + } + + public void finishAccept() + { + if(_fd != null) + { + Debug.Assert(_result != null); + try + { + _acceptFd = _fd.EndAccept(_result); + _result = null; + } + catch(SocketException ex) + { + _acceptError = ex; + } + } + } + + public IceInternal.Transceiver accept() + { + if(_acceptFd == null) + { + throw _acceptError; + } + + Socket acceptFd = _acceptFd; + _acceptFd = null; + _acceptError = null; + return new TransceiverI(_instance, new IceInternal.StreamSocket(_instance, acceptFd), _adapterName, true); + } + + public string protocol() + { + return _instance.protocol(); + } + + public override string ToString() + { + return IceInternal.Network.addrToString(_addr); + } + + public string toDetailedString() + { + StringBuilder s = new StringBuilder("local address = "); + s.Append(ToString()); + + List<string> intfs = IceInternal.Network.getHostsForEndpointExpand(_addr.Address.ToString(), + _instance.protocolSupport(), + true); + if(intfs.Count != 0) + { + s.Append("\nlocal interfaces = "); + s.Append(String.Join(", ", intfs.ToArray())); + } + return s.ToString(); + } + + internal int effectivePort() + { + return _addr.Port; + } + + internal AcceptorI(EndpointI endpoint, Instance instance, string adapterName, string host, int port) + { + _endpoint = endpoint; + _instance = instance; + _adapterName = adapterName; + _backlog = instance.properties().getPropertyAsIntWithDefault("Ice.TCP.Backlog", 511); + + // + // .NET requires that a certificate be supplied. + // + X509Certificate2Collection certs = instance.certs(); + if(certs.Count == 0) + { + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = "IceSSL: certificate required for server endpoint"; + throw ex; + } + + try + { + int protocol = instance.protocolSupport(); + _addr = IceInternal.Network.getAddressForServer(host, port, protocol, instance.preferIPv6()) as + IPEndPoint; + _fd = IceInternal.Network.createServerSocket(false, _addr.AddressFamily, protocol); + IceInternal.Network.setBlock(_fd, false); + IceInternal.Network.setTcpBufSize(_fd, _instance); + if(IceInternal.AssemblyUtil.platform_ != IceInternal.AssemblyUtil.Platform.Windows) + { + // + // Enable SO_REUSEADDR on Unix platforms to allow + // re-using the socket even if it's in the TIME_WAIT + // state. On Windows, this doesn't appear to be + // necessary and enabling SO_REUSEADDR would actually + // not be a good thing since it allows a second + // process to bind to an address even it's already + // bound by another process. + // + // TODO: using SO_EXCLUSIVEADDRUSE on Windows would + // probably be better but it's only supported by recent + // Windows versions (XP SP2, Windows Server 2003). + // + IceInternal.Network.setReuseAddress(_fd, true); + } + } + catch(System.Exception) + { + _fd = null; + throw; + } + } + + private EndpointI _endpoint; + private Instance _instance; + private string _adapterName; + private Socket _fd; + private Socket _acceptFd; + private System.Exception _acceptError; + private int _backlog; + private IPEndPoint _addr; + private IAsyncResult _result; + } +} diff --git a/csharp/src/IceSSL/AssemblyInfo.cs b/csharp/src/IceSSL/AssemblyInfo.cs new file mode 100644 index 00000000000..f75487dc2b1 --- /dev/null +++ b/csharp/src/IceSSL/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceSSL")] +[assembly: AssemblyDescription("IceSSL run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceSSL for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceSSL/ConnectorI.cs b/csharp/src/IceSSL/ConnectorI.cs new file mode 100644 index 00000000000..7c8a7f4becb --- /dev/null +++ b/csharp/src/IceSSL/ConnectorI.cs @@ -0,0 +1,115 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Net; + using System.Net.Sockets; + + sealed class ConnectorI : IceInternal.Connector + { + public IceInternal.Transceiver connect() + { + // + // The plug-in may not be fully initialized. + // + if(!_instance.initialized()) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: plug-in is not initialized"; + throw ex; + } + + return new TransceiverI(_instance, new IceInternal.StreamSocket(_instance, _proxy, _addr, _sourceAddr), + _host, false); + } + + public short type() + { + return _instance.type(); + } + + // + // Only for use by EndpointI. + // + internal ConnectorI(Instance instance, string host, EndPoint addr, IceInternal.NetworkProxy proxy, + EndPoint sourceAddr, int timeout, string conId) + { + _instance = instance; + _host = host; + _addr = (IPEndPoint)addr; + _proxy = proxy; + _sourceAddr = sourceAddr; + _timeout = timeout; + _connectionId = conId; + + _hashCode = 5381; + IceInternal.HashUtil.hashAdd(ref _hashCode, _addr); + if(_sourceAddr != null) + { + IceInternal.HashUtil.hashAdd(ref _hashCode, _sourceAddr); + } + IceInternal.HashUtil.hashAdd(ref _hashCode, _timeout); + IceInternal.HashUtil.hashAdd(ref _hashCode, _connectionId); + } + + public override bool Equals(object obj) + { + if(!(obj is ConnectorI)) + { + return false; + } + + if(this == obj) + { + return true; + } + + ConnectorI p = (ConnectorI)obj; + if(_timeout != p._timeout) + { + return false; + } + + if(!_connectionId.Equals(p._connectionId)) + { + return false; + } + + if(!IceInternal.Network.addressEquals(_sourceAddr, p._sourceAddr)) + { + return false; + } + + return _addr.Equals(p._addr); + } + + public override string ToString() + { + return IceInternal.Network.addrToString(_proxy == null ? _addr : _proxy.getAddress()); + } + + public override int GetHashCode() + { + return _hashCode; + } + + private Instance _instance; + private string _host; + private IPEndPoint _addr; + private IceInternal.NetworkProxy _proxy; + private EndPoint _sourceAddr; + private int _timeout; + private string _connectionId; + private int _hashCode; + } +} diff --git a/csharp/src/IceSSL/EndpointI.cs b/csharp/src/IceSSL/EndpointI.cs new file mode 100644 index 00000000000..edc04dd09d9 --- /dev/null +++ b/csharp/src/IceSSL/EndpointI.cs @@ -0,0 +1,370 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Diagnostics; + using System.Collections.Generic; + using System.Net; + using System.Globalization; + + sealed class EndpointI : IceInternal.IPEndpointI + { + internal EndpointI(Instance instance, string ho, int po, EndPoint sourceAddr, int ti, string conId, bool co) : + base(instance, ho, po, sourceAddr, conId) + { + _instance = instance; + _timeout = ti; + _compress = co; + } + + internal EndpointI(Instance instance) : + base(instance) + { + _instance = instance; + _timeout = instance.defaultTimeout(); + _compress = false; + } + + internal EndpointI(Instance instance, IceInternal.BasicStream s) : + base(instance, s) + { + _instance = instance; + _timeout = s.readInt(); + _compress = s.readBool(); + } + + private sealed class InfoI : IceSSL.EndpointInfo + { + public InfoI(EndpointI e) + { + _endpoint = e; + } + + override public short type() + { + return _endpoint.type(); + } + + override public bool datagram() + { + return _endpoint.datagram(); + } + + override public bool secure() + { + return _endpoint.secure(); + } + + private EndpointI _endpoint; + } + + // + // Return the endpoint information. + // + public override Ice.EndpointInfo getInfo() + { + InfoI info = new InfoI(this); + fillEndpointInfo(info); + return info; + } + + // + // Return the timeout for the endpoint in milliseconds. 0 means + // non-blocking, -1 means no timeout. + // + public override int timeout() + { + return _timeout; + } + + // + // Return a new endpoint with a different timeout value, provided + // that timeouts are supported by the endpoint. Otherwise the same + // endpoint is returned. + // + public override IceInternal.EndpointI timeout(int timeout) + { + if(timeout == _timeout) + { + return this; + } + else + { + return new EndpointI(_instance, host_, port_, sourceAddr_, timeout, connectionId_, _compress); + } + } + + // + // Return true if the endpoints support bzip2 compress, or false + // otherwise. + // + public override bool compress() + { + return _compress; + } + + // + // Return a new endpoint with a different compression value, + // provided that compression is supported by the + // endpoint. Otherwise the same endpoint is returned. + // + public override IceInternal.EndpointI compress(bool compress) + { + if(compress == _compress) + { + return this; + } + else + { + return new EndpointI(_instance, host_, port_, sourceAddr_, _timeout, connectionId_, compress); + } + } + + // + // Return true if the endpoint is datagram-based. + // + public override bool datagram() + { + return false; + } + + // + // Return a server side transceiver for this endpoint, or null if a + // transceiver can only be created by an acceptor. + // + public override IceInternal.Transceiver transceiver() + { + return null; + } + + // + // Return an acceptor for this endpoint, or null if no acceptor + // is available. + // + public override IceInternal.Acceptor acceptor(string adapterName) + { + return new AcceptorI(this, _instance, adapterName, host_, port_); + } + + public EndpointI endpoint(AcceptorI acceptor) + { + return new EndpointI(_instance, host_, acceptor.effectivePort(), sourceAddr_, _timeout, connectionId_, + _compress); + } + + public override string options() + { + // + // WARNING: Certain features, such as proxy validation in Glacier2, + // depend on the format of proxy strings. Changes to toString() and + // methods called to generate parts of the reference string could break + // these features. Please review for all features that depend on the + // format of proxyToString() before changing this and related code. + // + string s = base.options(); + + if(_timeout == -1) + { + s += " -t infinite"; + } + else + { + s += " -t " + _timeout; + } + + if(_compress) + { + s += " -z"; + } + + return s; + } + + // + // Compare endpoints for sorting purposes + // + public override int CompareTo(IceInternal.EndpointI obj) + { + if(!(obj is EndpointI)) + { + return type() < obj.type() ? -1 : 1; + } + + EndpointI p = (EndpointI)obj; + if(this == p) + { + return 0; + } + + if(_timeout < p._timeout) + { + return -1; + } + else if(p._timeout < _timeout) + { + return 1; + } + + if(!_compress && p._compress) + { + return -1; + } + else if(!p._compress && _compress) + { + return 1; + } + + return base.CompareTo(p); + } + + public override void streamWriteImpl(IceInternal.BasicStream s) + { + base.streamWriteImpl(s); + s.writeInt(_timeout); + s.writeBool(_compress); + } + + public override void hashInit(ref int h) + { + base.hashInit(ref h); + IceInternal.HashUtil.hashAdd(ref h, _timeout); + IceInternal.HashUtil.hashAdd(ref h, _compress); + } + + public override void fillEndpointInfo(Ice.IPEndpointInfo info) + { + base.fillEndpointInfo(info); + info.timeout = _timeout; + info.compress = _compress; + } + + protected override bool checkOption(string option, string argument, string endpoint) + { + if(base.checkOption(option, argument, endpoint)) + { + return true; + } + + switch(option[1]) + { + case 't': + { + if(argument == null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "no argument provided for -t option in endpoint " + endpoint; + throw e; + } + + if(argument.Equals("infinite")) + { + _timeout = -1; + } + else + { + try + { + _timeout = System.Int32.Parse(argument, CultureInfo.InvariantCulture); + if(_timeout < 1) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint; + throw e; + } + } + catch(System.FormatException ex) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(ex); + e.str = "invalid timeout value `" + argument + "' in endpoint " + endpoint; + throw e; + } + } + + return true; + } + + case 'z': + { + if(argument != null) + { + Ice.EndpointParseException e = new Ice.EndpointParseException(); + e.str = "unexpected argument `" + argument + "' provided for -z option in " + endpoint; + throw e; + } + + _compress = true; + return true; + } + + default: + { + return false; + } + } + } + + protected override IceInternal.Connector createConnector(EndPoint addr, IceInternal.NetworkProxy proxy) + { + return new ConnectorI(_instance, host_, addr, proxy, sourceAddr_, _timeout, connectionId_); + } + + protected override IceInternal.IPEndpointI createEndpoint(string host, int port, string connectionId) + { + return new EndpointI(_instance, host, port, sourceAddr_, _timeout, connectionId, _compress); + } + + private Instance _instance; + private int _timeout; + private bool _compress; + } + + internal sealed class EndpointFactoryI : IceInternal.EndpointFactory + { + internal EndpointFactoryI(Instance instance) + { + _instance = instance; + } + + public short type() + { + return _instance.type(); + } + + public string protocol() + { + return _instance.protocol(); + } + + public IceInternal.EndpointI create(List<string> args, bool oaEndpoint) + { + IceInternal.IPEndpointI endpt = new EndpointI(_instance); + endpt.initWithOptions(args, oaEndpoint); + return endpt; + } + + public IceInternal.EndpointI read(IceInternal.BasicStream s) + { + return new EndpointI(_instance, s); + } + + public void destroy() + { + _instance = null; + } + + public IceInternal.EndpointFactory clone(IceInternal.ProtocolInstance instance) + { + return new EndpointFactoryI(new Instance(_instance.engine(), instance.type(), instance.protocol())); + } + + private Instance _instance; + } +} diff --git a/csharp/src/IceSSL/Instance.cs b/csharp/src/IceSSL/Instance.cs new file mode 100644 index 00000000000..79b18435268 --- /dev/null +++ b/csharp/src/IceSSL/Instance.cs @@ -0,0 +1,78 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Security; + using System.Security.Authentication; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Text; + using System.Globalization; + + internal class Instance : IceInternal.ProtocolInstance + { + internal Instance(SSLEngine engine, short type, string protocol) : + base(engine.communicator(), type, protocol, true) + { + _engine = engine; + } + + internal SSLEngine engine() + { + return _engine; + } + + internal int securityTraceLevel() + { + return _engine.securityTraceLevel(); + } + + internal string securityTraceCategory() + { + return _engine.securityTraceCategory(); + } + + internal bool initialized() + { + return _engine.initialized(); + } + + internal X509Certificate2Collection certs() + { + return _engine.certs(); + } + + internal SslProtocols protocols() + { + return _engine.protocols(); + } + + internal int checkCRL() + { + return _engine.checkCRL(); + } + + internal void traceStream(System.Net.Security.SslStream stream, string connInfo) + { + _engine.traceStream(stream, connInfo); + } + + internal void verifyPeer(NativeConnectionInfo info, System.Net.Sockets.Socket fd, string address) + { + _engine.verifyPeer(info, fd, address); + } + + private SSLEngine _engine; + } +} diff --git a/csharp/src/IceSSL/Makefile b/csharp/src/IceSSL/Makefile new file mode 100644 index 00000000000..0dd6b3cdd24 --- /dev/null +++ b/csharp/src/IceSSL/Makefile @@ -0,0 +1,59 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IceSSL +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AcceptorI.cs \ + AssemblyInfo.cs \ + ConnectorI.cs \ + EndpointI.cs \ + Instance.cs \ + Plugin.cs \ + PluginI.cs \ + RFC2253.cs \ + SSLEngine.cs \ + TransceiverI.cs \ + TrustManager.cs \ + Util.cs + +SLICE_SRCS = $(SDIR)/ConnectionInfo.ice \ + $(SDIR)/EndpointInfo.ice + + +SDIR = $(slicedir)/IceSSL +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IceSSL/Makefile.mak b/csharp/src/IceSSL/Makefile.mak new file mode 100644 index 00000000000..776891d49c1 --- /dev/null +++ b/csharp/src/IceSSL/Makefile.mak @@ -0,0 +1,64 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IceSSL +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AcceptorI.cs \ + AssemblyInfo.cs \ + ConnectorI.cs \ + EndpointI.cs \ + Instance.cs \ + Plugin.cs \ + PluginI.cs \ + RFC2253.cs \ + SSLEngine.cs \ + TransceiverI.cs \ + TrustManager.cs \ + Util.cs + +GEN_SRCS = $(GDIR)\ConnectionInfo.cs\ + $(GDIR)\EndpointInfo.cs + + +SDIR = $(slicedir)\IceSSL +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) --ice -I$(slicedir) + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/IceSSL/Plugin.cs b/csharp/src/IceSSL/Plugin.cs new file mode 100644 index 00000000000..89575747441 --- /dev/null +++ b/csharp/src/IceSSL/Plugin.cs @@ -0,0 +1,114 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System.Security; + using System.Security.Cryptography.X509Certificates; + + // + // An application can customize the certificate verification process + // by implementing the CertificateVerifier interface. + // + public interface CertificateVerifier + { + // + // Return true to allow a connection using the provided certificate + // information, or false to reject the connection. + // + bool verify(NativeConnectionInfo info); + } + + /// <summary> + /// A password callback is an alternate way of supplying the plug-in with + /// passwords; this avoids using plain text configuration properties. + /// </summary> + public interface PasswordCallback + { + /// <summary> + /// Obtain the password necessary to access the private key associated with + /// the certificate in the given file. + /// <param name="file">The certificate file name.</param> + /// <returns>The password for the key or null, if no password is necessary.</returns> + /// </summary> + SecureString getPassword(string file); + + /// <summary> + /// Obtain a password for a certificate being imported via an IceSSL.ImportCert + /// property. Return null if no password is necessary. + /// </summary> + /// <param name="file">The certificate file name.</param> + /// <returns>The password for the key or null, if no password is necessary.</returns> + SecureString getImportPassword(string file); + } + + /// <summary> + /// Interface that allows applications to interact with the IceSSL plug-in. + /// </summary> + abstract public class Plugin : Ice.Plugin + { + abstract public void initialize(); + + /// <summary> + /// Specify the certificate authorities certificates to use + /// when validating SSL peer certificates. This must be done + /// before the plug-in is initialized; therefore, the application + /// must define the property Ice.InitPlugins=0, set the certificates, + /// and finally invoke initializePlugins on the PluginManager. + /// When the application supplies its own certificate authorities + /// certificates, the plug-in skips its normal property-based + /// configuration. + /// </summary> + /// <param name="certs">The certificate authorities certificates to use.</param> + abstract public void setCACertificates(X509Certificate2Collection certs); + + /// <summary> + /// Specify the certificates to use for SSL connections. This + /// must be done before the plug-in is initialized; therefore, + /// the application must define the property Ice.InitPlugins=0, + /// set the certificates, and finally invoke initializePlugins + /// on the PluginManager. + /// When the application supplies its own certificates, the + /// plug-in skips its normal property-based configuration. + /// </summary> + /// <param name="certs">The certificates to use for SSL connections.</param> + abstract public void setCertificates(X509Certificate2Collection certs); + + /// <summary> + /// Establish the certificate verifier object. This must be + /// done before any connections are established. + /// </summary> + /// <param name="verifier">The certificate verifier.</param> + abstract public void setCertificateVerifier(CertificateVerifier verifier); + + /// <summary> + /// Obtain the certificate verifier object. + /// </summary> + /// <returns>The certificate verifier (null if not set).</returns> + abstract public CertificateVerifier getCertificateVerifier(); + + /// <summary> + /// Establish the password callback object. This must be + /// done before the plug-in is initialized. + /// </summary> + /// <param name="callback">The password callback.</param> + abstract public void setPasswordCallback(PasswordCallback callback); + + /// <summary> + /// Returns the password callback. + /// </summary> + /// <returns>The password callback (null if not set).</returns> + abstract public PasswordCallback getPasswordCallback(); + + /// <summary> + /// This method is for internal use. + /// </summary> + abstract public void destroy(); + } +} diff --git a/csharp/src/IceSSL/PluginI.cs b/csharp/src/IceSSL/PluginI.cs new file mode 100644 index 00000000000..cbe540123fb --- /dev/null +++ b/csharp/src/IceSSL/PluginI.cs @@ -0,0 +1,93 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System.Security.Cryptography.X509Certificates; + +namespace IceSSL +{ + /// <summary> + /// Plug-in factories must implement this interface. + /// </summary> + public sealed class PluginFactory : Ice.PluginFactory + { + /// <summary> + /// Returns a new plug-in. + /// </summary> + /// <param name="communicator">The communicator for the plug-in.</param> + /// <param name="name">The name of the plug-in.</param> + /// <param name="args">The arguments that are specified in the plug-in's configuration.</param> + /// + /// <returns>The new plug-in. null can be returned to indicate + /// that a general error occurred. Alternatively, create can throw + /// PluginInitializationException to provide more detailed information.</returns> + public Ice.Plugin create(Ice.Communicator communicator, string name, string[] args) + { + return new PluginI(communicator); + } + } + + public sealed class PluginI : Plugin + { + public PluginI(Ice.Communicator communicator) + { + IceInternal.ProtocolPluginFacade facade = IceInternal.Util.getProtocolPluginFacade(communicator); + + _engine = new SSLEngine(facade); + + // + // Register the endpoint factory. We have to do this now, rather than + // in initialize, because the communicator may need to interpret + // proxies before the plug-in is fully initialized. + // + EndpointFactoryI factory = new EndpointFactoryI(new Instance(_engine, IceSSL.EndpointType.value, "ssl")); + facade.addEndpointFactory(factory); + } + + public override void initialize() + { + _engine.initialize(); + } + + public override void destroy() + { + } + + public override void setCACertificates(X509Certificate2Collection certs) + { + _engine.setCACertificates(certs); + } + + public override void setCertificates(X509Certificate2Collection certs) + { + _engine.setCertificates(certs); + } + + public override void setCertificateVerifier(CertificateVerifier verifier) + { + _engine.setCertificateVerifier(verifier); + } + + public override CertificateVerifier getCertificateVerifier() + { + return _engine.getCertificateVerifier(); + } + + public override void setPasswordCallback(PasswordCallback callback) + { + _engine.setPasswordCallback(callback); + } + + public override PasswordCallback getPasswordCallback() + { + return _engine.getPasswordCallback(); + } + + private SSLEngine _engine; + } +} diff --git a/csharp/src/IceSSL/RFC2253.cs b/csharp/src/IceSSL/RFC2253.cs new file mode 100644 index 00000000000..117ece7c955 --- /dev/null +++ b/csharp/src/IceSSL/RFC2253.cs @@ -0,0 +1,504 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +// +// See RFC 2253 and RFC 1779. +// +namespace IceSSL +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using System.Text; + + class RFC2253 + { + internal class ParseException : System.Exception + { + internal ParseException() + { + } + + internal ParseException(string reason) + { + this.reason = reason; + } + + internal string + ice_name() + { + return "RFC2253::ParseException"; + } + + internal string reason; + } + + internal struct RDNPair + { + internal string key; + internal string value; + } + + internal class RDNEntry + { + internal List<RDNPair> rdn = new List<RDNPair>(); + internal bool negate = false; + } + + internal static List<RDNEntry> parse(string data) + { + List<RDNEntry> results = new List<RDNEntry>(); + RDNEntry current = new RDNEntry(); + int pos = 0; + while(pos < data.Length) + { + eatWhite(data, ref pos); + if(pos < data.Length && data[pos] == '!') + { + if(current.rdn.Count > 0) + { + throw new ParseException("negation symbol '!' must appear at start of list"); + } + ++pos; + current.negate = true; + } + current.rdn.Add(parseNameComponent(data, ref pos)); + eatWhite(data, ref pos); + if(pos < data.Length && data[pos] == ',') + { + ++pos; + } + else if(pos < data.Length && data[pos] == ';') + { + ++pos; + results.Add(current); + current = new RDNEntry(); + } + else if(pos < data.Length) + { + throw new ParseException("expected ',' or ';' at `" + data.Substring(pos) + "'"); + } + } + if(current.rdn.Count > 0) + { + results.Add(current); + } + + return results; + } + + internal static List<RDNPair> parseStrict(string data) + { + List<RDNPair> results = new List<RDNPair>(); + int pos = 0; + while(pos < data.Length) + { + results.Add(parseNameComponent(data, ref pos)); + eatWhite(data, ref pos); + if(pos < data.Length && (data[pos] == ',' || data[pos] == ';')) + { + ++pos; + } + else if(pos < data.Length) + { + throw new ParseException("expected ',' or ';' at `" + data.Substring(pos) + "'"); + } + } + return results; + } + + public static string unescape(string data) + { + if(data.Length == 0) + { + return data; + } + + if(data[0] == '"') + { + if(data[data.Length - 1] != '"') + { + throw new ParseException("unescape: missing \""); + } + // + // Return the string without quotes. + // + return data.Substring(1, data.Length - 2); + } + + // + // Unescape the entire string. + // + StringBuilder result = new StringBuilder(); + if(data[0] == '#') + { + int pos = 1; + while(pos < data.Length) + { + result.Append(unescapeHex(data, pos)); + pos += 2; + } + } + else + { + int pos = 0; + while (pos < data.Length) + { + if(data[pos] != '\\') + { + result.Append(data[pos]); + ++pos; + } + else + { + ++pos; + if(pos >= data.Length) + { + throw new ParseException("unescape: invalid escape sequence"); + } + if(special.IndexOf(data[pos]) != -1 || data[pos] != '\\' || data[pos] != '"') + { + result.Append(data[pos]); + ++pos; + } + else + { + result.Append(unescapeHex(data, pos)); + pos += 2; + } + } + } + } + return result.ToString(); + } + + private static int hexToInt(char v) + { + if(v >= '0' && v <= '9') + { + return v - '0'; + } + if(v >= 'a' && v <= 'f') + { + return 10 + (v - 'a'); + } + if(v >= 'A' && v <= 'F') + { + return 10 + (v - 'A'); + } + throw new ParseException("unescape: invalid hex pair"); + } + + private static char unescapeHex(string data, int pos) + { + Debug.Assert(pos < data.Length); + if(pos + 2 >= data.Length) + { + throw new ParseException("unescape: invalid hex pair"); + } + return (char)(hexToInt(data[pos]) * 16 + hexToInt(data[pos + 1])); + } + + private static RDNPair parseNameComponent(string data, ref int pos) + { + RDNPair result = parseAttributeTypeAndValue(data, ref pos); + while(pos < data.Length) + { + eatWhite(data, ref pos); + if(pos < data.Length && data[pos] == '+') + { + ++pos; + } + else + { + break; + } + RDNPair p = parseAttributeTypeAndValue(data, ref pos); + result.value += "+"; + result.value += p.key; + result.value += '='; + result.value += p.value; + } + return result; + } + + private static RDNPair parseAttributeTypeAndValue(string data, ref int pos) + { + RDNPair p = new RDNPair(); + p.key = parseAttributeType(data, ref pos); + eatWhite(data, ref pos); + if(pos >= data.Length) + { + throw new ParseException("invalid attribute type/value pair (unexpected end of data)"); + } + if(data[pos] != '=') + { + throw new ParseException("invalid attribute type/value pair (missing =). remainder: " + + data.Substring(pos)); + } + ++pos; + p.value = parseAttributeValue(data, ref pos); + return p; + } + + private static string parseAttributeType(string data, ref int pos) + { + eatWhite(data, ref pos); + if(pos >= data.Length) + { + throw new ParseException("invalid attribute type (expected end of data)"); + } + + string result = ""; + + // + // RFC 1779. + // <key> ::= 1*( <keychar> ) | "OID." <oid> | "oid." <oid> + // <oid> ::= <digitstring> | <digitstring> "." <oid> + // RFC 2253: + // attributeType = (ALPHA 1*keychar) | oid + // keychar = ALPHA | DIGIT | "-" + // oid = 1*DIGIT *("." 1*DIGIT) + // + // In section 4 of RFC 2253 the document says: + // Implementations MUST allow an oid in the attribute type to be + // prefixed by one of the character strings "oid." or "OID.". + // + // Here we must also check for "oid." and "OID." before parsing + // according to the ALPHA KEYCHAR* rule. + // + // First the OID case. + // + if(Char.IsDigit(data[pos]) || + (data.Length - pos >= 4 && (data.Substring(pos, 4).Equals("oid.") || + data.Substring(pos, 4).Equals("OID.")))) + { + if(!Char.IsDigit(data[pos])) + { + result += data.Substring(pos, 4); + pos += 4; + } + + while(true) + { + // 1*DIGIT + while(pos < data.Length && Char.IsDigit(data[pos])) + { + result += data[pos]; + ++pos; + } + // "." 1*DIGIT + if(pos < data.Length && data[pos] == '.') + { + result += data[pos]; + ++pos; + // 1*DIGIT must follow "." + if(pos < data.Length && !Char.IsDigit(data[pos])) + { + throw new ParseException("invalid attribute type (expected end of data)"); + } + } + else + { + break; + } + } + } + else if(Char.IsUpper(data[pos]) || Char.IsLower(data[pos])) + { + // + // The grammar is wrong in this case. It should be ALPHA + // KEYCHAR* otherwise it will not accept "O" as a valid + // attribute type. + // + result += data[pos]; + ++pos; + // 1* KEYCHAR + while(pos < data.Length && + (Char.IsDigit(data[pos]) || + Char.IsUpper(data[pos]) || + Char.IsLower(data[pos]) || + data[pos] == '-')) + { + result += data[pos]; + ++pos; + } + } + else + { + throw new ParseException("invalid attribute type"); + } + return result; + } + + private static string parseAttributeValue(string data, ref int pos) + { + eatWhite(data, ref pos); + if(pos >= data.Length) + { + return ""; + } + + // + // RFC 2253 + // # hexstring + // + StringBuilder result = new StringBuilder(); + if(data[pos] == '#') + { + result.Append(data[pos]); + ++pos; + while(true) + { + string h = parseHexPair(data, ref pos, true); + if(h.Length == 0) + { + break; + } + result.Append(h); + } + } + // + // RFC 2253 + // QUOTATION *( quotechar | pair ) QUOTATION ; only from v2 + // quotechar = <any character except "\" or QUOTATION > + // + else if(data[pos] == '"') + { + result.Append(data[pos]); + ++pos; + while(true) + { + if(pos >= data.Length) + { + throw new ParseException("invalid attribute value (unexpected end of data)"); + } + // final terminating " + if(data[pos] == '"') + { + result.Append(data[pos]); + ++pos; + break; + } + // any character except '\' + else if(data[pos] != '\\') + { + result.Append(data[pos]); + ++pos; + } + // pair '\' + else + { + result.Append(parsePair(data, ref pos)); + } + } + } + // + // RFC 2253 + // * (stringchar | pair) + // stringchar = <any character except one of special, "\" or QUOTATION > + // + else + { + while(pos < data.Length) + { + if(data[pos] == '\\') + { + result.Append(parsePair(data, ref pos)); + } + else if(special.IndexOf(data[pos]) == -1 && data[pos] != '"') + { + result.Append(data[pos]); + ++pos; + } + else + { + break; + } + } + } + return result.ToString(); + } + + // + // RFC2253: + // pair = "\" ( special | "\" | QUOTATION | hexpair ) + // + private static string parsePair(string data, ref int pos) + { + string result = ""; + + Debug.Assert(data[pos] == '\\'); + result += data[pos]; + ++pos; + + if(pos >= data.Length) + { + throw new ParseException("invalid escape format (unexpected end of data)"); + } + + if(special.IndexOf(data[pos]) != -1 || data[pos] != '\\' || + data[pos] != '"') + { + result += data[pos]; + ++pos; + return result; + } + return parseHexPair(data, ref pos, false); + } + + // + // RFC 2253 + // hexpair = hexchar hexchar + // + private static string parseHexPair(string data, ref int pos, bool allowEmpty) + { + string result = ""; + if(pos < data.Length && hexvalid.IndexOf(data[pos]) != -1) + { + result += data[pos]; + ++pos; + } + if(pos < data.Length && hexvalid.IndexOf(data[pos]) != -1) + { + result += data[pos]; + ++pos; + } + if(result.Length != 2) + { + if(allowEmpty && result.Length == 0) + { + return result; + } + throw new ParseException("invalid hex format"); + } + return result; + } + + // + // RFC 2253: + // + // Implementations MUST allow for space (' ' ASCII 32) characters to be + // present between name-component and ',', between attributeTypeAndValue + // and '+', between attributeType and '=', and between '=' and + // attributeValue. These space characters are ignored when parsing. + // + private static void eatWhite(string data, ref int pos) + { + while(pos < data.Length && data[pos] == ' ') + { + ++pos; + } + } + + private static string special = ",=+<>#;"; + private static string hexvalid = "0123456789abcdefABCDEF"; + } +} diff --git a/csharp/src/IceSSL/SSLEngine.cs b/csharp/src/IceSSL/SSLEngine.cs new file mode 100644 index 00000000000..deac22369a3 --- /dev/null +++ b/csharp/src/IceSSL/SSLEngine.cs @@ -0,0 +1,1226 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Security; + using System.Security.Authentication; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Text; + using System.Globalization; + + internal class SSLEngine + { + internal SSLEngine(IceInternal.ProtocolPluginFacade facade) + { + _communicator = facade.getCommunicator(); + _logger = _communicator.getLogger(); + _facade = facade; + _securityTraceLevel = _communicator.getProperties().getPropertyAsIntWithDefault("IceSSL.Trace.Security", 0); + _securityTraceCategory = "Security"; + _initialized = false; + _trustManager = new TrustManager(_communicator); + +#if !UNITY + _tls12Support = false; + try + { + Enum.Parse(typeof(System.Security.Authentication.SslProtocols), "Tls12"); + _tls12Support = true; + } + catch(Exception) + { + } +#endif + } + + internal void initialize() + { + if(_initialized) + { + return; + } + + const string prefix = "IceSSL."; + Ice.Properties properties = communicator().getProperties(); + + // + // Check for a default directory. We look in this directory for + // files mentioned in the configuration. + // + _defaultDir = properties.getProperty(prefix + "DefaultDir"); + +#if UNITY + _certStore = null; +#else + string keySet = properties.getPropertyWithDefault(prefix + "KeySet", "DefaultKeySet"); + if(!keySet.Equals("DefaultKeySet") && !keySet.Equals("UserKeySet") && !keySet.Equals("MachineKeySet")) + { + _logger.warning("Invalid IceSSL.KeySet value `" + keySet + "' adjusted to `DefaultKeySet'"); + keySet = "DefaultKeySet"; + } + + _certStore = properties.getPropertyWithDefault(prefix + "CertStore", "CurrentUser"); + if(_certStore != "CurrentUser" && _certStore != "LocalMachine") + { + _logger.warning("Invalid IceSSL.CertStore value `" + _certStore + "' adjusted to `CurrentUser'"); + _certStore = "CurrentUser"; + } + + X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.DefaultKeySet; + if(keySet.Equals("UserKeySet")) + { + keyStorageFlags = X509KeyStorageFlags.UserKeySet; + } + else if(keySet.Equals("MachineKeySet")) + { + keyStorageFlags = X509KeyStorageFlags.MachineKeySet; + } + + if(properties.getPropertyAsIntWithDefault(prefix + "PersistKeySet", 0) > 0) + { + keyStorageFlags |= X509KeyStorageFlags.PersistKeySet; + } + + // + // Process IceSSL.ImportCert.* properties. + // + Dictionary<string, string> certs = properties.getPropertiesForPrefix(prefix + "ImportCert."); + foreach(KeyValuePair<string, string> entry in certs) + { + string name = entry.Key; + string val = entry.Value; + if(val.Length > 0) + { + importCertificate(name, val, keyStorageFlags); + } + } +#endif + + // + // Protocols selects which protocols to enable, by default we only enable TLS1.0 + // TLS1.1 and TLS1.2 to avoid security issues with SSLv3 + // + _protocols = parseProtocols( + properties.getPropertyAsListWithDefault(prefix + "Protocols", +#if UNITY + new string[]{"TLS1_0"})); +#else + _tls12Support ? new string[]{"TLS1_0", "TLS1_1", "TLS1_2"} : + new string[]{"TLS1_0", "TLS1_1"})); +#endif + // + // CheckCertName determines whether we compare the name in a peer's + // certificate against its hostname. + // + _checkCertName = properties.getPropertyAsIntWithDefault(prefix + "CheckCertName", 0) > 0; + + // + // VerifyDepthMax establishes the maximum length of a peer's certificate + // chain, including the peer's certificate. A value of 0 means there is + // no maximum. + // + _verifyDepthMax = properties.getPropertyAsIntWithDefault(prefix + "VerifyDepthMax", 2); + + // + // CheckCRL determines whether the certificate revocation list is checked, and how strictly. + // + _checkCRL = properties.getPropertyAsIntWithDefault(prefix + "CheckCRL", 0); + +#if !UNITY + // + // Check for a certificate verifier. + // + string certVerifierClass = properties.getProperty(prefix + "CertVerifier"); + if(certVerifierClass.Length > 0) + { + if(_verifier != null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: certificate verifier already installed"; + throw e; + } + + Type cls = _facade.findType(certVerifierClass); + if(cls == null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to load certificate verifier class " + certVerifierClass; + throw e; + } + + try + { + _verifier = (CertificateVerifier)IceInternal.AssemblyUtil.createInstance(cls); + } + catch(Exception ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: unable to instantiate certificate verifier class " + certVerifierClass; + throw e; + } + + if(_verifier == null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to instantiate certificate verifier class " + certVerifierClass; + throw e; + } + } + + // + // Check for a password callback. + // + string passwordCallbackClass = properties.getProperty(prefix + "PasswordCallback"); + if(passwordCallbackClass.Length > 0) + { + if(_passwordCallback != null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: password callback already installed"; + throw e; + } + + Type cls = _facade.findType(passwordCallbackClass); + if(cls == null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to load password callback class " + passwordCallbackClass; + throw e; + } + + try + { + _passwordCallback = (PasswordCallback)IceInternal.AssemblyUtil.createInstance(cls); + } + catch(Exception ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: unable to load password callback class " + passwordCallbackClass; + throw e; + } + + if(_passwordCallback == null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unable to load password callback class " + passwordCallbackClass; + throw e; + } + } + + // + // If the user hasn't supplied a certificate collection, we need to examine + // the property settings. + // + if(_certs == null) + { + // + // If IceSSL.CertFile is defined, load a certificate from a file and + // add it to the collection. + // + // TODO: tracing? + _certs = new X509Certificate2Collection(); + string certFile = properties.getProperty(prefix + "CertFile"); + string passwordStr = properties.getProperty(prefix + "Password"); + + if(certFile.Length > 0) + { + if(!checkPath(ref certFile)) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: certificate file not found: " + certFile; + throw e; + } + + SecureString password = null; + if(passwordStr.Length > 0) + { + password = createSecureString(passwordStr); + } + else if(_passwordCallback != null) + { + password = _passwordCallback.getPassword(certFile); + } + + try + { + X509Certificate2 cert; + if(password != null) + { + cert = new X509Certificate2(certFile, password, keyStorageFlags); + } + else + { + cert = new X509Certificate2(certFile, "", keyStorageFlags); + } + _certs.Add(cert); + } + catch(CryptographicException ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: error while attempting to load certificate from " + certFile; + throw e; + } + } + + // + // If IceSSL.FindCert.* properties are defined, add the selected certificates + // to the collection. + // + // TODO: tracing? + const string findPrefix = prefix + "FindCert."; + Dictionary<string, string> certProps = properties.getPropertiesForPrefix(findPrefix); + if(certProps.Count > 0) + { + foreach(KeyValuePair<string, string> entry in certProps) + { + string name = entry.Key; + string val = entry.Value; + if(val.Length > 0) + { + string storeSpec = name.Substring(findPrefix.Length); + X509Certificate2Collection coll = findCertificates(name, storeSpec, val); + _certs.AddRange(coll); + } + } + if(_certs.Count == 0) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: no certificates found"; + throw e; + } + } + } + + if(_caCerts == null) + { + string certAuthFile = properties.getProperty(prefix + "CertAuthFile"); + if(certAuthFile.Length > 0) + { + if(!checkPath(ref certAuthFile)) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: CA certificate file not found: " + certAuthFile; + throw e; + } + + _caCerts = new X509Certificate2Collection(); + try + { + _caCerts.Add(new X509Certificate2(certAuthFile)); + } + catch(CryptographicException ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: error while attempting to load CA certificate from " + certAuthFile; + throw e; + } + } + } +#endif + + _initialized = true; + } + + internal string certStore() + { + return _certStore; + } + + internal X509Certificate2Collection caCerts() + { + return _caCerts; + } + + internal void setCACertificates(X509Certificate2Collection caCerts) + { + if(_initialized) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: plug-in is already initialized"; + throw e; + } + + _caCerts = caCerts; + } + + internal void setCertificates(X509Certificate2Collection certs) + { + if(_initialized) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: plug-in is already initialized"; + throw e; + } + + _certs = certs; + } + + internal void setCertificateVerifier(CertificateVerifier verifier) + { + _verifier = verifier; + } + + internal CertificateVerifier getCertificateVerifier() + { + return _verifier; + } + + internal void setPasswordCallback(PasswordCallback callback) + { + _passwordCallback = callback; + } + + internal PasswordCallback getPasswordCallback() + { + return _passwordCallback; + } + + internal Ice.Communicator communicator() + { + return _facade.getCommunicator(); + } + + internal int securityTraceLevel() + { + return _securityTraceLevel; + } + + internal string securityTraceCategory() + { + return _securityTraceCategory; + } + + internal bool initialized() + { + return _initialized; + } + + internal X509Certificate2Collection certs() + { + return _certs; + } + + internal SslProtocols protocols() + { + return _protocols; + } + + internal int checkCRL() + { + return _checkCRL; + } + + internal void traceStream(System.Net.Security.SslStream stream, string connInfo) + { + System.Text.StringBuilder s = new System.Text.StringBuilder(); + s.Append("SSL connection summary"); + if(connInfo.Length > 0) + { + s.Append("\n"); + s.Append(connInfo); + } + s.Append("\nauthenticated = " + (stream.IsAuthenticated ? "yes" : "no")); + s.Append("\nencrypted = " + (stream.IsEncrypted ? "yes" : "no")); + s.Append("\nsigned = " + (stream.IsSigned ? "yes" : "no")); + s.Append("\nmutually authenticated = " + (stream.IsMutuallyAuthenticated ? "yes" : "no")); +#if !UNITY + s.Append("\nhash algorithm = " + stream.HashAlgorithm + "/" + stream.HashStrength); + s.Append("\ncipher algorithm = " + stream.CipherAlgorithm + "/" + stream.CipherStrength); + s.Append("\nkey exchange algorithm = " + stream.KeyExchangeAlgorithm + "/" + stream.KeyExchangeStrength); + s.Append("\nprotocol = " + stream.SslProtocol); +#endif + _logger.trace(_securityTraceCategory, s.ToString()); + } + + internal void verifyPeer(NativeConnectionInfo info, System.Net.Sockets.Socket fd, string address) + { + // + // For an outgoing connection, we compare the proxy address (if any) against + // fields in the server's certificate (if any). + // + if(info.nativeCerts != null && info.nativeCerts.Length > 0 && address.Length > 0) + { + // + // Extract the IP addresses and the DNS names from the subject + // alternative names. + // + List<string> dnsNames = null; + List<string> ipAddresses = null; + + // + // Search for "subject alternative name" extensions. The OID value + // of interest is 2.5.29.17 and the encoded data has the following + // ASN.1 syntax: + // + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + // + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER + // } + // + foreach(X509Extension ext in info.nativeCerts[0].Extensions) + { + if(ext.Oid.Value.Equals("2.5.29.17") && ext.RawData.Length > 0) + { + byte[] data = ext.RawData; + if(data.Length < 2 || data[0] != 0x30) // ASN.1 sequence + { + continue; + } + + int seqLen, pos; + if(!decodeASN1Length(data, 1, out seqLen, out pos)) + { + continue; + } + + while(pos < data.Length) + { + int tag = data[pos]; + + int len; + if(!decodeASN1Length(data, pos + 1, out len, out pos)) + { + break; + } + + if(tag == 0x82) + { + // + // Extract DNS name. + // + StringBuilder b = new StringBuilder(); + for(int j = pos; j < pos + len; ++j) + { + b.Append((char)data[j]); + } + if(dnsNames == null) + { + dnsNames = new List<string>(); + } + dnsNames.Add(b.ToString().ToUpperInvariant()); + } + else if(tag == 0x87) + { + // + // Extract IP address. + // + char sep = len == 4 ? '.' : ':'; + StringBuilder b = new StringBuilder(); + for(int j = pos; j < pos + len; ++j) + { + if(j > pos) + { + b.Append(sep); + } + b.Append(data[j].ToString(CultureInfo.InvariantCulture)); + } + if(ipAddresses == null) + { + ipAddresses = new List<string>(); + } + ipAddresses.Add(b.ToString().ToUpperInvariant()); + } + + pos += len; + } + } + } + + // + // Compare the peer's address against the common name as well as + // the dnsName and ipAddress values in the subject alternative name. + // + string dn = info.nativeCerts[0].Subject; + string addrLower = address.ToUpperInvariant(); + bool certNameOK = false; + { + string cn = "cn=" + addrLower; + int pos = dn.ToLower(CultureInfo.InvariantCulture).IndexOf(cn, StringComparison.Ordinal); + if(pos >= 0) + { + // + // Ensure we match the entire common name. + // + certNameOK = (pos + cn.Length == dn.Length) || (dn[pos + cn.Length] == ','); + } + } + + // + // Compare the peer's address against the dnsName and ipAddress + // values in the subject alternative name. + // + if(!certNameOK && ipAddresses != null) + { + certNameOK = ipAddresses.Contains(addrLower); + } + if(!certNameOK && dnsNames != null) + { + certNameOK = dnsNames.Contains(addrLower); + } + + // + // Log a message if the name comparison fails. If CheckCertName is defined, + // we also raise an exception to abort the connection. Don't log a message if + // CheckCertName is not defined and a verifier is present. + // + if(!certNameOK && (_checkCertName || (_securityTraceLevel >= 1 && _verifier == null))) + { + StringBuilder sb = new StringBuilder(); + sb.Append("IceSSL: "); + if(!_checkCertName) + { + sb.Append("ignoring "); + } + sb.Append("certificate validation failure:\npeer certificate does not have `"); + sb.Append(address); + sb.Append("' as its commonName or in its subjectAltName extension"); + if(dn.Length > 0) + { + sb.Append("\nSubject DN: "); + sb.Append(dn); + } + if(dnsNames != null) + { + sb.Append("\nDNS names found in certificate: "); + for(int j = 0; j < dnsNames.Count; ++j) + { + if(j > 0) + { + sb.Append(", "); + } + sb.Append(dnsNames[j]); + } + } + if(ipAddresses != null) + { + sb.Append("\nIP addresses found in certificate: "); + for(int j = 0; j < ipAddresses.Count; ++j) + { + if(j > 0) + { + sb.Append(", "); + } + sb.Append(ipAddresses[j]); + } + } + string msg = sb.ToString(); + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, msg); + } + if(_checkCertName) + { + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; + } + } + } + + if(_verifyDepthMax > 0 && info.nativeCerts != null && info.nativeCerts.Length > _verifyDepthMax) + { + string msg = (info.incoming ? "incoming" : "outgoing") + " connection rejected:\n" + + "length of peer's certificate chain (" + info.nativeCerts.Length + ") exceeds maximum of " + + _verifyDepthMax + "\n" + + IceInternal.Network.fdToString(fd); + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, msg); + } + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; + } + + if(!_trustManager.verify(info)) + { + string msg = (info.incoming ? "incoming" : "outgoing") + " connection rejected by trust manager\n" + + IceInternal.Network.fdToString(fd); + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, msg); + } + + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = "IceSSL: " + msg; + throw ex; + } + + if(_verifier != null && !_verifier.verify(info)) + { + string msg = (info.incoming ? "incoming" : "outgoing") + + " connection rejected by certificate verifier\n" + IceInternal.Network.fdToString(fd); + if(_securityTraceLevel >= 1) + { + _logger.trace(_securityTraceCategory, msg); + } + + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = "IceSSL: " + msg; + throw ex; + } + } + + // + // Parse a string of the form "location.name" into two parts. + // + private static void parseStore(string prop, string store, ref StoreLocation loc, ref StoreName name, + ref string sname) + { + int pos = store.IndexOf('.'); + if(pos == -1) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: property `" + prop + "' has invalid format"; + throw e; + } + + string sloc = store.Substring(0, pos).ToUpperInvariant(); + if(sloc.Equals("CURRENTUSER")) + { + loc = StoreLocation.CurrentUser; + } + else if(sloc.Equals("LOCALMACHINE")) + { + loc = StoreLocation.LocalMachine; + } + else + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unknown store location `" + sloc + "' in " + prop; + throw e; + } + + sname = store.Substring(pos + 1); + if(sname.Length == 0) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: invalid store name in " + prop; + throw e; + } + + // + // Try to convert the name into the StoreName enumeration. + // + try + { + name = (StoreName)Enum.Parse(typeof(StoreName), sname, true); + sname = null; + } + catch(ArgumentException) + { + // Ignore - assume the user is selecting a non-standard store. + } + } + + private bool checkPath(ref string path) + { + if(File.Exists(path)) + { + return true; + } + + if(_defaultDir.Length > 0) + { + string s = _defaultDir + Path.DirectorySeparatorChar + path; + if(File.Exists(s)) + { + path = s; + return true; + } + } + + return false; + } + +#if !UNITY + private void importCertificate(string propName, string propValue, X509KeyStorageFlags keyStorageFlags) + { + // + // Expecting a property of the following form: + // + // IceSSL.ImportCert.<location>.<name>=<file>[;password] + // + const string prefix = "IceSSL.ImportCert."; + StoreLocation loc = 0; + StoreName name = 0; + string sname = null; + parseStore(propName, propName.Substring(prefix.Length), ref loc, ref name, ref sname); + + // + // Extract the filename and password. Either or both can be quoted. + // + string[] arr = splitString(propValue, ';'); + if(arr == null) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unmatched quote in `" + propValue + "'"; + throw e; + } + if(arr.Length == 0) + { + return; + } + string file = arr[0]; + string passwordStr = null; + if(arr.Length > 1) + { + passwordStr = arr[1]; + } + + // + // Open the X509 certificate store. + // + X509Store store = null; + try + { + if(sname != null) + { + store = new X509Store(sname, loc); + } + else + { + store = new X509Store(name, loc); + } + store.Open(OpenFlags.ReadWrite); + } + catch(Exception ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: failure while opening store specified by " + propName; + throw e; + } + + if(!checkPath(ref file)) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: certificate file not found:\n" + file; + throw e; + } + + SecureString password = null; + if(passwordStr != null) + { + password = createSecureString(passwordStr); + } + else if(_passwordCallback != null) + { + password = _passwordCallback.getImportPassword(file); + } + + // + // Add the certificate to the store. + // + try + { + X509Certificate2 cert; + if(password != null) + { + cert = new X509Certificate2(file, password, keyStorageFlags); + } + else + { + cert = new X509Certificate2(file, "", keyStorageFlags); + } + store.Add(cert); + } + catch(Exception ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: failure while adding certificate file:\n" + file; + throw e; + } + finally + { + store.Close(); + } + } +#endif + + // + // Split strings using a delimiter. Quotes are supported. + // Returns null for an unmatched quote. + // + private static string[] splitString(string str, char delim) + { + ArrayList l = new ArrayList(); + char[] arr = new char[str.Length]; + int pos = 0; + + while(pos < str.Length) + { + int n = 0; + char quoteChar = '\0'; + if(str[pos] == '"' || str[pos] == '\'') + { + quoteChar = str[pos]; + ++pos; + } + bool trim = true; + while(pos < str.Length) + { + if(quoteChar != '\0' && str[pos] == '\\' && pos + 1 < str.Length && str[pos + 1] == quoteChar) + { + ++pos; + } + else if(quoteChar != '\0' && str[pos] == quoteChar) + { + trim = false; + ++pos; + quoteChar = '\0'; + break; + } + else if(str[pos] == delim) + { + if(quoteChar == '\0') + { + ++pos; + break; + } + } + if(pos < str.Length) + { + arr[n++] = str[pos++]; + } + } + if(quoteChar != '\0') + { + return null; // Unmatched quote. + } + if(n > 0) + { + string s = new string(arr, 0, n); + if(trim) + { + s = s.Trim(); + } + if(s.Length > 0) + { + l.Add(s); + } + } + } + + return (string[])l.ToArray(typeof(string)); + } + + private SslProtocols parseProtocols(string[] arr) + { + SslProtocols result = SslProtocols.Default; + + if(arr.Length > 0) + { + result = 0; + for(int i = 0; i < arr.Length; ++i) + { + string protocol = null; + string s = arr[i].ToUpperInvariant(); + switch(s) + { + case "SSL3": + case "SSLV3": + { + protocol = "Ssl3"; + break; + } + case "TLS": + case "TLS1": + case "TLS1_0": + case "TLSV1": + case "TLSV1_0": + { + protocol = "Tls"; + break; + } + case "TLS1_1": + case "TLSV1_1": + { + protocol = "Tls11"; + break; + } + case "TLS1_2": + case "TLSV1_2": + { + protocol = "Tls12"; + break; + } + default: + { + break; + } + } + + try + { + SslProtocols value = (SslProtocols)Enum.Parse(typeof(SslProtocols), protocol); + result |= value; + } + catch(Exception) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unrecognized protocol `" + s + "'"; + throw e; + } + } + } + return result; + } + +#if !UNITY + private static X509Certificate2Collection findCertificates(string prop, string storeSpec, string value) + { + StoreLocation storeLoc = 0; + StoreName storeName = 0; + string storeNameStr = null; + parseStore(prop, storeSpec, ref storeLoc, ref storeName, ref storeNameStr); + + // + // Open the X509 certificate store. + // + X509Store store = null; + try + { + if(storeNameStr != null) + { + store = new X509Store(storeNameStr, storeLoc); + } + else + { + store = new X509Store(storeName, storeLoc); + } + store.Open(OpenFlags.ReadOnly); + } + catch(Exception ex) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(ex); + e.reason = "IceSSL: failure while opening store specified by " + prop; + throw e; + } + + // + // Start with all of the certificates in the collection and filter as necessary. + // + // - If the value is "*", return all certificates. + // - Otherwise, search using key:value pairs. The following keys are supported: + // + // Issuer + // IssuerDN + // Serial + // Subject + // SubjectDN + // SubjectKeyId + // Thumbprint + // + // A value must be enclosed in single or double quotes if it contains whitespace. + // + X509Certificate2Collection result = new X509Certificate2Collection(); + result.AddRange(store.Certificates); + try + { + if(value != "*") + { + int start = 0; + int pos; + while((pos = value.IndexOf(':', start)) != -1) + { + // + // Parse the X509FindType. + // + string field = value.Substring(start, pos - start).Trim().ToUpperInvariant(); + X509FindType findType; + if(field.Equals("SUBJECT")) + { + findType = X509FindType.FindBySubjectName; + } + else if(field.Equals("SUBJECTDN")) + { + findType = X509FindType.FindBySubjectDistinguishedName; + } + else if(field.Equals("ISSUER")) + { + findType = X509FindType.FindByIssuerName; + } + else if(field.Equals("ISSUERDN")) + { + findType = X509FindType.FindByIssuerDistinguishedName; + } + else if(field.Equals("THUMBPRINT")) + { + findType = X509FindType.FindByThumbprint; + } + else if(field.Equals("SUBJECTKEYID")) + { + findType = X509FindType.FindBySubjectKeyIdentifier; + } + else if(field.Equals("SERIAL")) + { + findType = X509FindType.FindBySerialNumber; + } + else + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unknown key in `" + value + "'"; + throw e; + } + + // + // Parse the argument. + // + start = pos + 1; + while(start < value.Length && (value[start] == ' ' || value[start] == '\t')) + { + ++start; + } + if(start == value.Length) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: missing argument in `" + value + "'"; + throw e; + } + + string arg; + if(value[start] == '"' || value[start] == '\'') + { + int end = start; + ++end; + while(end < value.Length) + { + if(value[end] == value[start] && value[end - 1] != '\\') + { + break; + } + ++end; + } + if(end == value.Length || value[end] != value[start]) + { + Ice.PluginInitializationException e = new Ice.PluginInitializationException(); + e.reason = "IceSSL: unmatched quote in `" + value + "'"; + throw e; + } + ++start; + arg = value.Substring(start, end - start); + start = end + 1; + } + else + { + char[] ws = new char[] { ' ', '\t' }; + int end = value.IndexOfAny(ws, start); + if(end == -1) + { + arg = value.Substring(start); + start = value.Length; + } + else + { + arg = value.Substring(start, end - start); + start = end + 1; + } + } + + // + // Execute the query. + // + // TODO: allow user to specify a value for validOnly? + // + bool validOnly = false; + result = result.Find(findType, arg, validOnly); + } + } + } + finally + { + store.Close(); + } + + return result; + } + + private static SecureString createSecureString(string s) + { + SecureString result = new SecureString(); + foreach(char ch in s) + { + result.AppendChar(ch); + } + return result; + } +#endif + + private static bool decodeASN1Length(byte[] data, int start, out int len, out int next) + { + len = 0; + next = 0; + + if(start + 1 > data.Length) + { + return false; + } + + len = data[start]; + int len2 = 0; + if(len > 0x80) // Composed length + { + len2 = len - 0x80; + if(start + len2 + 1 > data.Length) + { + return false; + } + len = 0; + for(int i = 0; i < len2; i++) + { + len *= 256; + len += data[start + i + 1]; + } + } + else if(len == 0x80) // Undefined length encoding + { + return false; + } + + next = start + len2 + 1; + return (next + len <= data.Length); + } + + private Ice.Communicator _communicator; + private Ice.Logger _logger; + private IceInternal.ProtocolPluginFacade _facade; + private int _securityTraceLevel; + private string _securityTraceCategory; + private bool _initialized; + private string _defaultDir; + private SslProtocols _protocols; + private bool _checkCertName; + private int _verifyDepthMax; + private int _checkCRL; + private X509Certificate2Collection _certs; + private string _certStore; + private X509Certificate2Collection _caCerts; + private CertificateVerifier _verifier; + private PasswordCallback _passwordCallback; + private TrustManager _trustManager; +#if !UNITY + private bool _tls12Support; +#endif + } +} diff --git a/csharp/src/IceSSL/TransceiverI.cs b/csharp/src/IceSSL/TransceiverI.cs new file mode 100644 index 00000000000..84902367dc9 --- /dev/null +++ b/csharp/src/IceSSL/TransceiverI.cs @@ -0,0 +1,754 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Collections.Generic; + using System.IO; + using System.Net; + using System.Net.Security; + using System.Net.Sockets; + using System.Security.Authentication; + using System.Security.Cryptography.X509Certificates; + using System.Threading; + using System.Text; + + sealed class TransceiverI : IceInternal.Transceiver + { + public Socket fd() + { + return _stream.fd(); + } + + public int initialize(IceInternal.Buffer readBuffer, IceInternal.Buffer writeBuffer, ref bool hasMoreData) + { + int status = _stream.connect(readBuffer, writeBuffer, ref hasMoreData); + if(status != IceInternal.SocketOperation.None) + { + return status; + } + + _stream.setBlock(true); // SSL requires a blocking socket + + if(_sslStream == null) + { + NetworkStream ns = new NetworkStream(_stream.fd(), false); + _sslStream = new SslStream(ns, false, new RemoteCertificateValidationCallback(validationCallback), + new LocalCertificateSelectionCallback(selectCertificate)); + return IceInternal.SocketOperation.Connect; + } + + Debug.Assert(_sslStream.IsAuthenticated); + _authenticated = true; + _instance.verifyPeer(getNativeConnectionInfo(), _stream.fd(), _host); + + if(_instance.securityTraceLevel() >= 1) + { + _instance.traceStream(_sslStream, _stream.ToString()); + } + return IceInternal.SocketOperation.None; + } + + public int closing(bool initiator, Ice.LocalException ex) + { + // If we are initiating the connection closure, wait for the peer + // to close the TCP/IP connection. Otherwise, close immediately. + return initiator ? IceInternal.SocketOperation.Read : IceInternal.SocketOperation.None; + } + + public void close() + { + if(_sslStream != null) + { + _sslStream.Close(); // Closing the stream also closes the socket. + _sslStream = null; + } + + _stream.close(); + } + + public IceInternal.EndpointI bind() + { + Debug.Assert(false); + return null; + } + + public void destroy() + { + _stream.destroy(); + } + + public int write(IceInternal.Buffer buf) + { + // + // Force caller to use async write. + // + return buf.b.hasRemaining() ? IceInternal.SocketOperation.Write : IceInternal.SocketOperation.None; + } + + public int read(IceInternal.Buffer buf, ref bool hasMoreData) + { + // + // Force caller to use async read. + // + return buf.b.hasRemaining() ? IceInternal.SocketOperation.Read : IceInternal.SocketOperation.None; + } + + public bool startRead(IceInternal.Buffer buf, IceInternal.AsyncCallback callback, object state) + { + if(!_stream.isConnected()) + { + return _stream.startRead(buf, callback, state); + } + + Debug.Assert(_sslStream != null && _sslStream.IsAuthenticated); + + int packetSz = _stream.getRecvPacketSize(buf.b.remaining()); + try + { + _readCallback = callback; + _readResult = _sslStream.BeginRead(buf.b.rawBytes(), buf.b.position(), packetSz, readCompleted, state); + return _readResult.CompletedSynchronously; + } + catch(IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + if(IceInternal.Network.timeout(ex)) + { + throw new Ice.TimeoutException(); + } + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + } + + public void finishRead(IceInternal.Buffer buf) + { + if(!_stream.isConnected()) + { + _stream.finishRead(buf); + return; + } + else if(_sslStream == null) // Transceiver was closed + { + _readResult = null; + return; + } + + Debug.Assert(_readResult != null); + try + { + int ret = _sslStream.EndRead(_readResult); + _readResult = null; + + if(ret == 0) + { + throw new Ice.ConnectionLostException(); + } + Debug.Assert(ret > 0); + buf.b.position(buf.b.position() + ret); + } + catch(Ice.LocalException) + { + throw; + } + catch(IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + if(IceInternal.Network.timeout(ex)) + { + throw new Ice.TimeoutException(); + } + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + } + + public bool startWrite(IceInternal.Buffer buf, IceInternal.AsyncCallback callback, object state, + out bool completed) + { + if(!_stream.isConnected()) + { + return _stream.startWrite(buf, callback, state, out completed); + } + + Debug.Assert(_sslStream != null); + if(!_authenticated) + { + completed = false; + return startAuthenticate(callback, state); + } + + // + // We limit the packet size for beingWrite to ensure connection timeouts are based + // on a fixed packet size. + // + int packetSize = _stream.getSendPacketSize(buf.b.remaining()); + try + { + _writeCallback = callback; + _writeResult = _sslStream.BeginWrite(buf.b.rawBytes(), buf.b.position(), packetSize, writeCompleted, + state); + completed = packetSize == buf.b.remaining(); + return _writeResult.CompletedSynchronously; + } + catch(IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + if(IceInternal.Network.timeout(ex)) + { + throw new Ice.TimeoutException(); + } + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + } + + public void finishWrite(IceInternal.Buffer buf) + { + if(!_stream.isConnected()) + { + _stream.finishWrite(buf); + return; + } + else if(_sslStream == null) // Transceiver was closed + { + if(_stream.getSendPacketSize(buf.b.remaining()) == buf.b.remaining()) // Sent last packet + { + buf.b.position(buf.b.limit()); // Assume all the data was sent for at-most-once semantics. + } + _writeResult = null; + return; + } + else if(!_authenticated) + { + finishAuthenticate(); + return; + } + + Debug.Assert(_writeResult != null); + int sent = _stream.getSendPacketSize(buf.b.remaining()); + try + { + _sslStream.EndWrite(_writeResult); + _writeResult = null; + buf.b.position(buf.b.position() + sent); + } + catch(IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + throw new Ice.ConnectionLostException(ex); + } + if(IceInternal.Network.timeout(ex)) + { + throw new Ice.TimeoutException(); + } + throw new Ice.SocketException(ex); + } + catch(ObjectDisposedException ex) + { + throw new Ice.ConnectionLostException(ex); + } + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + } + + public string protocol() + { + return _instance.protocol(); + } + + public Ice.ConnectionInfo getInfo() + { + return getNativeConnectionInfo(); + } + + public void checkSendSize(IceInternal.Buffer buf) + { + } + + public void setBufferSize(int rcvSize, int sndSize) + { + _stream.setBufferSize(rcvSize, sndSize); + } + + public override string ToString() + { + return _stream.ToString(); + } + + public string toDetailedString() + { + return ToString(); + } + + // + // Only for use by ConnectorI, AcceptorI. + // + internal TransceiverI(Instance instance, IceInternal.StreamSocket stream, string hostOrAdapterName, bool incoming) + { + _instance = instance; + _stream = stream; + _incoming = incoming; + if(_incoming) + { + _adapterName = hostOrAdapterName; + } + else + { + _host = hostOrAdapterName; + } + _sslStream = null; + + if(_incoming) + { + // + // Determine whether a certificate is required from the peer. + // + _verifyPeer = _instance.properties().getPropertyAsIntWithDefault("IceSSL.VerifyPeer", 2); + } + else + { + _verifyPeer = 0; + } + + _chain = new X509Chain(_instance.engine().certStore() == "LocalMachine"); + + if(_instance.checkCRL() == 0) + { + _chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + } + + X509Certificate2Collection caCerts = _instance.engine().caCerts(); + if(caCerts != null) + { +#if !UNITY + // + // We need to set this flag to be able to use a certificate authority from the extra store. + // + _chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; +#endif + + foreach(X509Certificate2 cert in caCerts) + { + _chain.ChainPolicy.ExtraStore.Add(cert); + } + } + } + + private NativeConnectionInfo getNativeConnectionInfo() + { + IceSSL.NativeConnectionInfo info = new IceSSL.NativeConnectionInfo(); + if(_stream.fd() != null) + { + IPEndPoint localEndpoint = (IPEndPoint)IceInternal.Network.getLocalAddress(_stream.fd()); + info.localAddress = localEndpoint.Address.ToString(); + info.localPort = localEndpoint.Port; + IPEndPoint remoteEndpoint = (IPEndPoint)IceInternal.Network.getRemoteAddress(_stream.fd()); + if(remoteEndpoint != null) + { + info.remoteAddress = remoteEndpoint.Address.ToString(); + info.remotePort = remoteEndpoint.Port; + } + info.rcvSize = IceInternal.Network.getRecvBufferSize(_stream.fd()); + info.sndSize = IceInternal.Network.getSendBufferSize(_stream.fd()); + } + if(_sslStream != null) + { +#if UNITY + info.cipher = ""; +#else + info.cipher = _sslStream.CipherAlgorithm.ToString(); + if(_chain.ChainElements != null && _chain.ChainElements.Count > 0) + { + info.nativeCerts = new X509Certificate2[_chain.ChainElements.Count]; + for(int i = 0; i < _chain.ChainElements.Count; ++i) + { + info.nativeCerts[i] = _chain.ChainElements[i].Certificate; + } + } +#endif + + List<string> certs = new List<string>(); +#if !UNITY + if(info.nativeCerts != null) + { + foreach(X509Certificate2 cert in info.nativeCerts) + { + StringBuilder s = new StringBuilder(); + s.Append("-----BEGIN CERTIFICATE-----\n"); + s.Append(Convert.ToBase64String(cert.Export(X509ContentType.Cert))); + s.Append("\n-----END CERTIFICATE-----"); + certs.Add(s.ToString()); + } + } +#endif + info.certs = certs.ToArray(); + } + info.adapterName = _adapterName; + info.incoming = _incoming; + return info; + } + + private bool startAuthenticate(IceInternal.AsyncCallback callback, object state) + { + try + { + _writeCallback = callback; + if(!_incoming) + { + // + // Client authentication. + // + _writeResult = _sslStream.BeginAuthenticateAsClient(_host, + _instance.certs(), + _instance.protocols(), + _instance.checkCRL() > 0, + writeCompleted, + state); + } + else + { +#if UNITY + throw new Ice.FeatureNotSupportedException("ssl server socket"); +#else + // + // Server authentication. + // + // Get the certificate collection and select the first one. + // + X509Certificate2Collection certs = _instance.certs(); + X509Certificate2 cert = null; + if(certs.Count > 0) + { + cert = certs[0]; + } + + _writeResult = _sslStream.BeginAuthenticateAsServer(cert, + _verifyPeer > 1, + _instance.protocols(), + _instance.checkCRL() > 0, + writeCompleted, + state); +#endif + } + } + catch(IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + // + // This situation occurs when connectToSelf is called; the "remote" end + // closes the socket immediately. + // + throw new Ice.ConnectionLostException(); + } + throw new Ice.SocketException(ex); + } +#if !UNITY + catch(AuthenticationException ex) + { + Ice.SecurityException e = new Ice.SecurityException(ex); + e.reason = ex.Message; + throw e; + } +#endif + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + + Debug.Assert(_writeResult != null); + return _writeResult.CompletedSynchronously; + } + + private void finishAuthenticate() + { + Debug.Assert(_writeResult != null); + + try + { + if(!_incoming) + { + _sslStream.EndAuthenticateAsClient(_writeResult); + } + else + { + _sslStream.EndAuthenticateAsServer(_writeResult); + } + } + catch(IOException ex) + { + if(IceInternal.Network.connectionLost(ex)) + { + // + // This situation occurs when connectToSelf is called; the "remote" end + // closes the socket immediately. + // + throw new Ice.ConnectionLostException(); + } + throw new Ice.SocketException(ex); + } +#if !UNITY + catch(AuthenticationException ex) + { + Ice.SecurityException e = new Ice.SecurityException(ex); + e.reason = ex.Message; + throw e; + } +#endif + catch(Exception ex) + { + throw new Ice.SyscallException(ex); + } + } + + private X509Certificate selectCertificate( + object sender, + string targetHost, + X509CertificateCollection localCertificates, + X509Certificate remoteCertificate, + string[] acceptableIssuers) + { + X509Certificate2Collection certs = _instance.engine().certs(); + + // + // Use the first certificate that match the acceptable issuers. + // + if(acceptableIssuers != null && acceptableIssuers.Length > 0 && certs != null && certs.Count > 0) + { + foreach(X509Certificate certificate in certs) + { + if(Array.IndexOf(acceptableIssuers, certificate.Issuer) != -1) + { + return certificate; + } + } + } + + // + // Send first certificate + // + if(certs.Count > 0) + { + return certs[0]; + } + return null; + } + + private bool validationCallback(object sender, X509Certificate certificate, X509Chain chainEngine, + SslPolicyErrors policyErrors) + { +#if !UNITY + SslPolicyErrors sslPolicyErrors = policyErrors; + bool valid = false; + if(certificate != null) + { + sslPolicyErrors = SslPolicyErrors.None; + valid = _chain.Build(new X509Certificate2(certificate)); + if(_chain.ChainStatus.Length > 0) + { + sslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors; + } + } + + string message = ""; + int errors = (int)sslPolicyErrors; + if((errors & (int)SslPolicyErrors.RemoteCertificateNotAvailable) > 0) + { + // + // The RemoteCertificateNotAvailable case does not appear to be possible + // for an outgoing connection. Since .NET requires an authenticated + // connection, the remote peer closes the socket if it does not have a + // certificate to provide. + // + + if(_incoming) + { + if(_verifyPeer > 1) + { + if(_instance.securityTraceLevel() >= 1) + { + _instance.logger().trace(_instance.securityTraceCategory(), + "SSL certificate validation failed - client certificate not provided"); + } + return false; + } + errors ^= (int)SslPolicyErrors.RemoteCertificateNotAvailable; + message = message + "\nremote certificate not provided (ignored)"; + } + } + + if((errors & (int)SslPolicyErrors.RemoteCertificateNameMismatch) > 0) + { + // + // Ignore this error here; we'll check the peer certificate in verifyPeer(). + // + errors ^= (int)SslPolicyErrors.RemoteCertificateNameMismatch; + } + + if((errors & (int)SslPolicyErrors.RemoteCertificateChainErrors) > 0) + { + if(_chain.ChainStatus != null) + { + int errorCount = _chain.ChainStatus.Length; + foreach(X509ChainStatus status in _chain.ChainStatus) + { + if(status.Status == X509ChainStatusFlags.UntrustedRoot && + _instance.engine().caCerts() != null && valid) + { + // + // Untrusted root is OK when using our custom chain engine if + // the CA certificate is present in the chain policy extra store. + // + X509ChainElement e = _chain.ChainElements[_chain.ChainElements.Count - 1]; + if(_chain.ChainPolicy.ExtraStore.Contains(e.Certificate)) + { + --errorCount; + } + } + else if(status.Status == X509ChainStatusFlags.Revoked) + { + if(_instance.checkCRL() > 0) + { + message = message + "\ncertificate revoked"; + } + else + { + message = message + "\ncertificate revoked (ignored)"; + --errorCount; + } + } + else if(status.Status == X509ChainStatusFlags.RevocationStatusUnknown) + { + // + // If a certificate's revocation status cannot be determined, the strictest + // policy is to reject the connection. + // + if(_instance.checkCRL() > 1) + { + message = message + "\ncertificate revocation status unknown"; + } + else + { + message = message + "\ncertificate revocation status unknown (ignored)"; + --errorCount; + } + } + else if(status.Status == X509ChainStatusFlags.NoError) + { + --errorCount; + } + else + { + message = message + "\ncertificate chain error: " + status.Status.ToString(); + } + } + + if(errorCount == 0) + { + errors ^= (int)SslPolicyErrors.RemoteCertificateChainErrors; + } + } + } + + if(errors > 0) + { + if(_instance.securityTraceLevel() >= 1) + { + if(message.Length > 0) + { + _instance.logger().trace(_instance.securityTraceCategory(), + "SSL certificate validation failed:" + message); + } + else + { + _instance.logger().trace(_instance.securityTraceCategory(), + "SSL certificate validation failed"); + } + } + return false; + } + else if(message.Length > 0 && _instance.securityTraceLevel() >= 1) + { + _instance.logger().trace(_instance.securityTraceCategory(), "SSL certificate validation status:" + + message); + } +#endif + + return true; + } + + internal void readCompleted(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + _readCallback(result.AsyncState); + } + } + + internal void writeCompleted(IAsyncResult result) + { + if(!result.CompletedSynchronously) + { + _writeCallback(result.AsyncState); + } + } + + private Instance _instance; + private IceInternal.StreamSocket _stream; + private string _host = ""; + private string _adapterName = ""; + private bool _incoming; + private SslStream _sslStream; + private int _verifyPeer; + private bool _authenticated; + private IAsyncResult _writeResult; + private IAsyncResult _readResult; + private IceInternal.AsyncCallback _readCallback; + private IceInternal.AsyncCallback _writeCallback; + private X509Chain _chain; + } +} diff --git a/csharp/src/IceSSL/TrustManager.cs b/csharp/src/IceSSL/TrustManager.cs new file mode 100644 index 00000000000..98bbd1ff59f --- /dev/null +++ b/csharp/src/IceSSL/TrustManager.cs @@ -0,0 +1,343 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Security.Cryptography.X509Certificates; + using System.Text; + + sealed class TrustManager + { + internal TrustManager(Ice.Communicator communicator) + { + Debug.Assert(communicator != null); + communicator_ = communicator; + Ice.Properties properties = communicator.getProperties(); + traceLevel_ = properties.getPropertyAsInt("IceSSL.Trace.Security"); + string key = null; + try + { + key = "IceSSL.TrustOnly"; + parse(properties.getProperty(key), rejectAll_, acceptAll_); + key = "IceSSL.TrustOnly.Client"; + parse(properties.getProperty(key), rejectClient_, acceptClient_); + key = "IceSSL.TrustOnly.Server"; + parse(properties.getProperty(key), rejectAllServer_, acceptAllServer_); + Dictionary<string, string> dict = properties.getPropertiesForPrefix("IceSSL.TrustOnly.Server."); + foreach(KeyValuePair<string, string> entry in dict) + { + key = entry.Key; + string name = key.Substring("IceSSL.TrustOnly.Server.".Length); + List<List<RFC2253.RDNPair>> reject = new List<List<RFC2253.RDNPair>>(); + List<List<RFC2253.RDNPair>> accept = new List<List<RFC2253.RDNPair>>(); + parse(entry.Value, reject, accept); + if(reject.Count > 0) + { + rejectServer_[name] = reject; + } + if(accept.Count > 0) + { + acceptServer_[name] = accept; + } + } + } + catch(RFC2253.ParseException e) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: invalid property " + key + ":\n" + e.reason; + throw ex; + } + } + + internal bool verify(NativeConnectionInfo info) + { + List<List<List<RFC2253.RDNPair>>> reject = new List<List<List<RFC2253.RDNPair>>>(), + accept = new List<List<List<RFC2253.RDNPair>>>(); + + if(rejectAll_.Count != 0) + { + reject.Add(rejectAll_); + } + if(info.incoming) + { + if(rejectAllServer_.Count != 0) + { + reject.Add(rejectAllServer_); + } + if(info.adapterName.Length > 0) + { + List<List<RFC2253.RDNPair>> p = null; + if(rejectServer_.TryGetValue(info.adapterName, out p)) + { + reject.Add(p); + } + } + } + else + { + if(rejectClient_.Count != 0) + { + reject.Add(rejectClient_); + } + } + + if(acceptAll_.Count != 0) + { + accept.Add(acceptAll_); + } + if(info.incoming) + { + if(acceptAllServer_.Count != 0) + { + accept.Add(acceptAllServer_); + } + if(info.adapterName.Length > 0) + { + List<List<RFC2253.RDNPair>> p = null; + if(acceptServer_.TryGetValue(info.adapterName, out p)) + { + accept.Add(p); + } + } + } + else + { + if(acceptClient_.Count != 0) + { + accept.Add(acceptClient_); + } + } + + // + // If there is nothing to match against, then we accept the cert. + // + if(reject.Count == 0 && accept.Count == 0) + { + return true; + } + + // + // If there is no certificate then we match false. + // + if(info.nativeCerts != null && info.nativeCerts.Length > 0) + { +#if UNITY + throw new Ice.FeatureNotSupportedException("certificate subjectName not available"); +#else + X500DistinguishedName subjectDN = info.nativeCerts[0].SubjectName; + string subjectName = subjectDN.Name; + Debug.Assert(subjectName != null); + try + { + // + // Decompose the subject DN into the RDNs. + // + if(traceLevel_ > 0) + { + if(info.incoming) + { + communicator_.getLogger().trace("Security", "trust manager evaluating client:\n" + + "subject = " + subjectName + "\n" + + "adapter = " + info.adapterName + "\n" + + "local addr = " + info.localAddress + ":" + info.localPort + "\n" + + "remote addr = " + info.remoteAddress + ":" + info.remotePort); + } + else + { + communicator_.getLogger().trace("Security", "trust manager evaluating server:\n" + + "subject = " + subjectName + "\n" + + "local addr = " + info.localAddress + ":" + info.localPort + "\n" + + "remote addr = " + info.remoteAddress + ":" + info.remotePort); + } + } + + List<RFC2253.RDNPair> dn = RFC2253.parseStrict(subjectName); + + // + // Unescape the DN. Note that this isn't done in + // the parser in order to keep the various RFC2253 + // implementations as close as possible. + // + for(int i = 0; i < dn.Count; ++i) + { + RFC2253.RDNPair p = dn[i]; + p.value = RFC2253.unescape(p.value); + dn[i] = p; + } + + // + // Fail if we match anything in the reject set. + // + foreach(List<List<RFC2253.RDNPair>> matchSet in reject) + { + if(traceLevel_ > 0) + { + StringBuilder s = new StringBuilder("trust manager rejecting PDNs:\n"); + stringify(matchSet, s); + communicator_.getLogger().trace("Security", s.ToString()); + } + if(match(matchSet, dn)) + { + return false; + } + } + + // + // Succeed if we match anything in the accept set. + // + foreach(List<List<RFC2253.RDNPair>> matchSet in accept) + { + if(traceLevel_ > 0) + { + StringBuilder s = new StringBuilder("trust manager accepting PDNs:\n"); + stringify(matchSet, s); + communicator_.getLogger().trace("Security", s.ToString()); + } + if(match(matchSet, dn)) + { + return true; + } + } + } + catch(RFC2253.ParseException e) + { + communicator_.getLogger().warning( + "IceSSL: unable to parse certificate DN `" + subjectName + "'\nreason: " + e.reason); + } + + // + // At this point we accept the connection if there are no explicit accept rules. + // + return accept.Count == 0; +#endif + } + + return false; + } + + private bool match(List<List<RFC2253.RDNPair>> matchSet, List<RFC2253.RDNPair> subject) + { + foreach(List<RFC2253.RDNPair> item in matchSet) + { + if(matchRDNs(item, subject)) + { + return true; + } + } + return false; + } + + private bool matchRDNs(List<RFC2253.RDNPair> match, List<RFC2253.RDNPair> subject) + { + foreach(RFC2253.RDNPair matchRDN in match) + { + bool found = false; + foreach(RFC2253.RDNPair subjectRDN in subject) + { + if(matchRDN.key.Equals(subjectRDN.key)) + { + found = true; + if(!matchRDN.value.Equals(subjectRDN.value)) + { + return false; + } + } + } + if(!found) + { + return false; + } + } + return true; + } + + // Note that unlike the C++ & Java implementation this returns unescaped data. + void parse(string value, List<List<RFC2253.RDNPair>> reject, List<List<RFC2253.RDNPair>> accept) + { + // + // As with the Java implementation, the DN that comes from + // the X500DistinguishedName does not necessarily match + // the user's input form. Therefore we need to normalize the + // data to match the C# forms. + // + List<RFC2253.RDNEntry> l = RFC2253.parse(value); + for(int i = 0; i < l.Count; ++i) + { + List<RFC2253.RDNPair> dn = l[i].rdn; + for(int j = 0; j < dn.Count; ++j) + { + RFC2253.RDNPair pair = dn[j]; + // Normalize the RDN key. + if (pair.key == "emailAddress") + { + pair.key = "E"; + } + else if (pair.key == "ST") + { + pair.key = "S"; + } + // Unescape the value. + pair.value = RFC2253.unescape(pair.value); + dn[j] = pair; + } + if(l[i].negate) + { + reject.Add(l[i].rdn); + } + else + { + accept.Add(l[i].rdn); + } + } + } + + private static void stringify(List<List<RFC2253.RDNPair>> matchSet, StringBuilder s) + { + bool addSemi = false; + foreach(List<RFC2253.RDNPair> rdnSet in matchSet) + { + if(addSemi) + { + s.Append(';'); + } + addSemi = true; + bool addComma = false; + foreach(RFC2253.RDNPair rdn in rdnSet) + { + if(addComma) + { + s.Append(','); + } + addComma = true; + s.Append(rdn.key); + s.Append('='); + s.Append(rdn.value); + } + } + } + + private Ice.Communicator communicator_; + private int traceLevel_; + + private List<List<RFC2253.RDNPair>> rejectAll_ = new List<List<RFC2253.RDNPair>>(); + private List<List<RFC2253.RDNPair>> rejectClient_ = new List<List<RFC2253.RDNPair>>(); + private List<List<RFC2253.RDNPair>> rejectAllServer_ = new List<List<RFC2253.RDNPair>>(); + private Dictionary<string, List<List<RFC2253.RDNPair>>> rejectServer_ = + new Dictionary<string, List<List<RFC2253.RDNPair>>>(); + + private List<List<RFC2253.RDNPair>> acceptAll_ = new List<List<RFC2253.RDNPair>>(); + private List<List<RFC2253.RDNPair>> acceptClient_ = new List<List<RFC2253.RDNPair>>(); + private List<List<RFC2253.RDNPair>> acceptAllServer_ = new List<List<RFC2253.RDNPair>>(); + private Dictionary<string, List<List<RFC2253.RDNPair>>> acceptServer_ = + new Dictionary<string, List<List<RFC2253.RDNPair>>>(); + } +} diff --git a/csharp/src/IceSSL/Util.cs b/csharp/src/IceSSL/Util.cs new file mode 100644 index 00000000000..9fa6b8d0142 --- /dev/null +++ b/csharp/src/IceSSL/Util.cs @@ -0,0 +1,44 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +namespace IceSSL +{ + using System; + using System.Diagnostics; + using System.Security.Cryptography.X509Certificates; + + /// <summary> + /// This class provides information about a connection to applications + /// that require information about a peer, for example, to implement + /// a CertificateVerifier. + /// </summary> + public sealed class NativeConnectionInfo : ConnectionInfo + { + /// <summary> + /// The certificate chain. This may be null if the peer did not + /// supply a certificate. The peer's certificate (if any) is the + /// first one in the chain. + /// </summary> + public System.Security.Cryptography.X509Certificates.X509Certificate2[] nativeCerts; + } + + public sealed class Util + { + public static X509Certificate2 createCertificate(string certPEM) + { + char[] chars = certPEM.ToCharArray(); + byte[] bytes = new byte[chars.Length]; + for(int i = 0; i < chars.Length; ++i) + { + bytes[i] = (byte)chars[i]; + } + return new X509Certificate2(bytes); + } + } +} diff --git a/csharp/src/IceSSL/generated/.gitignore b/csharp/src/IceSSL/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IceSSL/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/IceStorm/.depend.mak b/csharp/src/IceStorm/.depend.mak new file mode 100644 index 00000000000..de028ef4015 --- /dev/null +++ b/csharp/src/IceStorm/.depend.mak @@ -0,0 +1,13 @@ + +IceStorm.cs: \ + "$(slicedir)\IceStorm\IceStorm.ice" \ + "$(slicedir)/Ice/Identity.ice" \ + "$(slicedir)/Ice/SliceChecksumDict.ice" \ + "$(slicedir)/IceStorm/Metrics.ice" \ + "$(slicedir)/Ice/Metrics.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" + +Metrics.cs: \ + "$(slicedir)\IceStorm\Metrics.ice" \ + "$(slicedir)/Ice/Metrics.ice" \ + "$(slicedir)/Ice/BuiltinSequences.ice" diff --git a/csharp/src/IceStorm/AssemblyInfo.cs b/csharp/src/IceStorm/AssemblyInfo.cs new file mode 100644 index 00000000000..3ee62207196 --- /dev/null +++ b/csharp/src/IceStorm/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("IceStorm")] +[assembly: AssemblyDescription("IceStorm run-time support")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("IceStorm for .NET")] +[assembly: AssemblyCopyright("Copyright (c) 2003-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("Ice")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] +[assembly: AssemblyDelaySign(false)] + +[assembly: ComVisible(false)] diff --git a/csharp/src/IceStorm/Makefile b/csharp/src/IceStorm/Makefile new file mode 100644 index 00000000000..2a02bcef619 --- /dev/null +++ b/csharp/src/IceStorm/Makefile @@ -0,0 +1,47 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +PKG = IceStorm +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)/$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs + +SLICE_SRCS = $(SDIR)/IceStorm.ice \ + $(SDIR)/Metrics.ice + +SDIR = $(slicedir)/IceStorm +GDIR = generated + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS := $(MCSFLAGS) -keyfile:$(KEYFILE) +MCSFLAGS := $(MCSFLAGS) /doc:$(assembliesdir)/$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS := $(SLICE2CSFLAGS) -I$(slicedir) --ice + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) $(MCSFLAGS) $(call ref,Ice) $(subst /,$(DSEP),$^) + +install:: all + (cd $(assembliesdir); $(call installassembly,$(LIBNAME),$(PKG)); $(call installpolicy,$(POLICY)); \ + $(call installmdb,$(LIBNAME).mdb); \ + $(call installdata,$(PKG).xml,$(DESTDIR)$(install_assembliesdir))) + +ifeq ($(GACINSTALL),yes) +install:: all + $(call installdata,../../lib/pkgconfig/$(PKG).pc,$(DESTDIR)$(install_pkgconfigdir)) +endif + +clean:: + -rm -f $(assembliesdir)/$(PKG).xml diff --git a/csharp/src/IceStorm/Makefile.mak b/csharp/src/IceStorm/Makefile.mak new file mode 100644 index 00000000000..7094b571653 --- /dev/null +++ b/csharp/src/IceStorm/Makefile.mak @@ -0,0 +1,52 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +PKG = IceStorm +LIBNAME = $(PKG).dll +TARGETS = $(assembliesdir)\$(LIBNAME) +POLICY_TARGET = $(POLICY).dll + +SRCS = AssemblyInfo.cs + +GEN_SRCS = $(GDIR)\IceStorm.cs \ + $(GDIR)\Metrics.cs + +SDIR = $(slicedir)\IceStorm +GDIR = generated + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCSFLAGS = $(MCSFLAGS) -target:library -out:$(TARGETS) -warnaserror- +MCSFLAGS = $(MCSFLAGS) -keyfile:"$(KEYFILE)" +MCSFLAGS = $(MCSFLAGS) /doc:$(assembliesdir)\$(PKG).xml /nowarn:1591 + +SLICE2CSFLAGS = $(SLICE2CSFLAGS) -I$(slicedir) --ice + +$(TARGETS):: $(SRCS) $(GEN_SRCS) + $(MCS) /baseaddress:0x21000000 $(MCSFLAGS) -r:$(refdir)\Ice.dll $(SRCS) $(GEN_SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(assembliesdir)\$(PKG).pdb +!endif + +clean:: + del /q $(assembliesdir)\$(PKG).xml + +install:: all + copy $(assembliesdir)\$(LIBNAME) "$(install_assembliesdir)" + copy $(assembliesdir)\$(PKG).xml "$(install_assembliesdir)" +!if "$(generate_policies)" == "yes" + copy $(assembliesdir)\$(POLICY_TARGET) "$(install_assembliesdir)" +!endif +!if "$(DEBUG)" == "yes" + copy $(assembliesdir)\$(PKG).pdb "$(install_assembliesdir)" +!endif diff --git a/csharp/src/IceStorm/generated/.gitignore b/csharp/src/IceStorm/generated/.gitignore new file mode 100644 index 00000000000..39af5887579 --- /dev/null +++ b/csharp/src/IceStorm/generated/.gitignore @@ -0,0 +1 @@ +# Dummy file, so that git retains this otherwise empty directory. diff --git a/csharp/src/Makefile b/csharp/src/Makefile new file mode 100644 index 00000000000..4b9a3e17400 --- /dev/null +++ b/csharp/src/Makefile @@ -0,0 +1,21 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = .. + +include $(top_srcdir)/config/Make.rules.cs + +SUBDIRS = Ice IceStorm Glacier2 IcePatch2 IceGrid IceBox IceDiscovery IceLocatorDiscovery + +$(EVERYTHING):: + @for subdir in $(SUBDIRS); \ + do \ + echo "making $@ in $$subdir"; \ + ( cd $$subdir && $(MAKE) $@ ) || exit 1; \ + done diff --git a/csharp/src/Makefile.mak b/csharp/src/Makefile.mak new file mode 100644 index 00000000000..1c73e5fd7ae --- /dev/null +++ b/csharp/src/Makefile.mak @@ -0,0 +1,26 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = .. + +!include $(top_srcdir)\config\Make.rules.mak.cs + +SUBDIRS = Ice IceStorm Glacier2 IcePatch2 IceGrid +!if "$(COMPACT)" != "yes" && "$(SILVERLIGHT)" != "yes" +SUBDIRS = $(SUBDIRS) IceSSL IceDiscovery IceLocatorDiscovery +!endif + +!if "$(SILVERLIGHT)" != "yes" +SUBDIRS = $(SUBDIRS) IceBox PolicyServer +!endif + +$(EVERYTHING):: + @for %i in ( $(SUBDIRS) ) do \ + @echo "making $@ in %i" && \ + cmd /c "cd %i && $(MAKE) -nologo -f Makefile.mak $@" || exit 1 diff --git a/csharp/src/PolicyServer/AssemblyInfo.cs b/csharp/src/PolicyServer/AssemblyInfo.cs new file mode 100644 index 00000000000..37d7139fc91 --- /dev/null +++ b/csharp/src/PolicyServer/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: CLSCompliant(true)] + +[assembly: AssemblyTitle("PolicyServer")] +[assembly: AssemblyDescription("Ice for Silveright policy server")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ZeroC, Inc.")] +[assembly: AssemblyProduct("Ice PolicyServer")] +[assembly: AssemblyCopyright("Copyright © 2012-2015 ZeroC, Inc.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("3.6.0")] + +[assembly: ComVisible(false)] +[assembly: Guid("ddd45a23-8a57-4389-aad4-401a2ae2a7fa")] diff --git a/csharp/src/PolicyServer/Makefile b/csharp/src/PolicyServer/Makefile new file mode 100644 index 00000000000..244f1c54e0b --- /dev/null +++ b/csharp/src/PolicyServer/Makefile @@ -0,0 +1,26 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../.. + +POLICYSERVER = $(bindir)/policyserver.exe +TARGETS = $(POLICYSERVER) +SRCS = AssemblyInfo.cs PolicyServer.cs + +include $(top_srcdir)/config/Make.rules.cs + +MCSFLAGS := $(MCSFLAGS) -target:exe + + +$(POLICYSERVER): $(SRCS) $(assembliesdir)/$(LIBNAME) + $(MCS) $(MCSFLAGS) -out:$@ $(SRCS) + +install:: all + $(call installprogram,$(POLICYSERVER),$(DESTDIR)$(install_bindir)) + $(call installmdb,$(POLICYSERVER).mdb) diff --git a/csharp/src/PolicyServer/Makefile.mak b/csharp/src/PolicyServer/Makefile.mak new file mode 100644 index 00000000000..46c89d13b6c --- /dev/null +++ b/csharp/src/PolicyServer/Makefile.mak @@ -0,0 +1,44 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +# +# This copy of Ice for Silverlight is licensed to you under the terms +# described in the ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\.. + +TARGETS = $(bindir)\policyserver.exe + +SRCS = PolicyServer.cs \ + AssemblyInfo.cs + +!include $(top_srcdir)\config\Make.rules.mak.cs + +MCS = csc -nologo + +MCSFLAGS = -warnaserror -d:MAKEFILE_BUILD +!if "$(DEBUG)" == "yes" +MCSFLAGS = $(MCSFLAGS) -debug -define:DEBUG +!endif + +!if "$(OPTIMIZE)" == "yes" +MCSFLAGS = $(MCSFLAGS) -optimize+ +!endif + +MCSFLAGS = $(MCSFLAGS) -target:exe + +$(bindir)\policyserver.exe: $(SRCS) + $(MCS) $(MCSFLAGS) -out:$@ $(SRCS) + +!if "$(DEBUG)" == "yes" +clean:: + del /q $(bindir)\policyserver.pdb +!endif + +install:: all + copy $(bindir)\policyserver.exe "$(install_bindir)" +!if "$(DEBUG)" == "yes" + copy $(bindir)\policyserver.pdb "$(install_bindir)" +!endif
\ No newline at end of file diff --git a/csharp/src/PolicyServer/PolicyServer.cs b/csharp/src/PolicyServer/PolicyServer.cs new file mode 100644 index 00000000000..153db23627d --- /dev/null +++ b/csharp/src/PolicyServer/PolicyServer.cs @@ -0,0 +1,127 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Text; + +namespace PolicyService +{ + +// +// This class implements a simple policy server for +// Silverlight. +// +public sealed class PolicyServer : IDisposable +{ + public PolicyServer(string ipAddress, string policyFile) + { + _ipAddress = ipAddress; + _policyFile = policyFile; + } + + public void Start() + { + IPAddress address = null; + try + { + address = IPAddress.Parse(_ipAddress); + } + catch(System.FormatException ex) + { + Console.WriteLine("Invalid IP address format: " + _ipAddress); + Console.WriteLine(ex.ToString()); + return; + } + // Read policy file + try + { + _policyBytes = File.ReadAllBytes(_policyFile); + } + catch(System.IO.IOException ex) + { + Console.WriteLine("Error reading policy file: " + _policyFile); + Console.WriteLine(ex.ToString()); + return; + } + _policyListener = new TcpListener(address, 943); + try + { + // Listen for policy requests + _policyListener.Start(); + } + catch(SocketException ex) + { + Console.WriteLine("Error starting Policy Server:\n" + ex.ToString()); + return; + } + Console.WriteLine("Policy Server started..."); + // Start policy response thread + Thread policyThread = new Thread(ServePolicy); + policyThread.IsBackground = true; + policyThread.Start(); + policyThread.Join(); + } + + private void ServePolicy() + { + while(true) + { + Console.WriteLine("Accepting Policy Requests..."); + using(Socket client = _policyListener.AcceptSocket()) + { + Console.WriteLine("Policy Request Accepted..."); + + // Get policy request header + byte[] buffer = new byte[1024]; + int bytesReceived = client.Receive(buffer); + + // Basic check of request header + string header = Encoding.UTF8.GetString(buffer, 0, bytesReceived); + if(header == "<policy-file-request/>") + { + client.Send(_policyBytes, 0, _policyBytes.Length, SocketFlags.None); + } + } + } + } + + public void Dispose() + { + } + + private string _ipAddress = ""; + private string _policyFile = ""; + private byte[] _policyBytes = null; // byte array used to store the response in memory. + private TcpListener _policyListener = null; +} + +class Program +{ + static void Main(string[] args) + { + if(args.Length == 2) + { + // Start our Policy Service + using (PolicyServer server = new PolicyServer(args[0], args[1])) + { + server.Start(); + } + } + else + { + Console.WriteLine("Usage: PolicyServer <ip-address> <policy-file>"); + } + } +} + +} |