// // Copyright (c) ZeroC, Inc. All rights reserved. // 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).iceReference(); 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. // if(_ref.getInstance().traceLevels().location >= 1) { locatorInfo.trace("retrieved adapter for well-known object from locator, " + "adding to locator cache", _ref, r); } 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) { RequestCallback callback = new RequestCallback(@ref, ttl, cb); lock(this) { if(!_response && _exception == null) { _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(); } return; } } if(_response) { callback.response(_locatorInfo, _proxy); } else { Debug.Assert(_exception != null); callback.exception(_locatorInfo, _exception); } } 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; Monitor.PulseAll(this); } foreach(RequestCallback callback in _callbacks) { callback.response(_locatorInfo, proxy); } } public void exception(Ice.Exception ex) { lock(this) { _locatorInfo.finishRequest(_ref, _wellKnownRefs, null, ex is Ice.UserException); _exception = ex; Monitor.PulseAll(this); } foreach(RequestCallback callback in _callbacks) { callback.exception(_locatorInfo, ex); } } protected abstract void send(); readonly protected LocatorInfo _locatorInfo; readonly protected Reference _ref; private List _callbacks = new List(); private List _wellKnownRefs = new List(); 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( response, 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(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 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()) { if(@ref.getInstance().traceLevels().location >= 1) { trace("found adapter for well-known object in locator cache", @ref, r); } 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 for adapter from locator cache", rf, endpoints); } } else { Reference r = _table.removeObjectReference(rf.getIdentity()); if(r != null) { if(!r.isIndirect()) { if(rf.getInstance().traceLevels().location >= 2) { trace("removed endpoints for well-known object from locator cache", rf, r.getEndpoints()); } } else if(!r.isWellKnown()) { if(rf.getInstance().traceLevels().location >= 2) { trace("removed adapter for well-known object from locator cache", rf, r); } 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("well-known proxy = " + r.ToString() + "\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 trace(string msg, Reference r, Reference resolved) { Debug.Assert(r.isWellKnown()); System.Text.StringBuilder s = new System.Text.StringBuilder(); s.Append(msg); s.Append("\n"); s.Append("well-known proxy = "); s.Append(r.ToString()); s.Append("\n"); s.Append("adapter = "); s.Append(resolved.getAdapterId()); 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 = " + Ice.Util.identityToString(@ref.getIdentity(), instance.toStringMode())); instance.initializationData().logger.trace(instance.traceLevels().locationCat, s.ToString()); } Ice.NotRegisteredException e = new Ice.NotRegisteredException(ex); e.kindOfObject = "object"; e.id = Ice.Util.identityToString(@ref.getIdentity(), instance.toStringMode()); 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 endpoints\n"); if(@ref.getAdapterId().Length > 0) { s.Append("adapter = " + @ref.getAdapterId() + "\n"); } else { s.Append("well-known proxy = " + @ref.ToString() + "\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) { if(@ref.isWellKnown()) { trace("found endpoints for well-known proxy in locator cache", @ref, endpoints); } else { trace("found endpoints for adapter in locator cache", @ref, endpoints); } } else { if(@ref.isWellKnown()) { trace("retrieved endpoints for well-known proxy from locator, adding to locator cache", @ref, endpoints); } else { trace("retrieved endpoints for adapter from locator, adding to locator cache", @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("well-known object\n"); s.Append("well-known proxy = " + @ref.ToString()); } 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 well-known object\nwell-known proxy = "); s.Append(@ref.ToString()); 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 wellKnownRefs, Ice.ObjectPrx proxy, bool notRegistered) { Ice.ObjectPrxHelperBase @base = proxy as Ice.ObjectPrxHelperBase; if(proxy == null || @base.iceReference().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.iceReference().isIndirect()) { // Cache the adapter endpoints. _table.addAdapterEndpoints(@ref.getAdapterId(), @base.iceReference().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.iceReference().isWellKnown()) { // Cache the well-known object reference. _table.addObjectReference(@ref.getIdentity(), @base.iceReference()); } 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 _adapterRequests = new Dictionary(); private Dictionary _objectRequests = new Dictionary(); } public sealed class LocatorManager { struct LocatorKey { public LocatorKey(Ice.LocatorPrx prx) { Reference r = ((Ice.ObjectPrxHelperBase)prx).iceReference(); _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; HashUtil.hashAdd(ref h, _id); HashUtil.hashAdd(ref h, _encoding); return h; } private Ice.Identity _id; private Ice.EncodingVersion _encoding; } internal LocatorManager(Ice.Properties properties) { _table = new Dictionary(); _locatorTables = new Dictionary(); _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 _table; private Dictionary _locatorTables; private readonly bool _background; } sealed class LocatorTable { internal LocatorTable() { _adapterEndpointsTable = new Dictionary(); _objectTable = new Dictionary(); } internal void clear() { lock(this) { _adapterEndpointsTable.Clear(); _objectTable.Clear(); } } internal 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, EndpointI[] endpoints) { lock(this) { _adapterEndpointsTable[adapter] = new EndpointTableEntry(Time.currentMonotonicTimeMillis(), endpoints); } } internal 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, EndpointI[] endpoints) { this.time = time; this.endpoints = endpoints; } public long time; public 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 _adapterEndpointsTable; private Dictionary _objectTable; } }