/* ======================================================================== * Copyright (c) 2005-2020 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Opc.Ua.Bindings; using Opc.Ua.Security.Certificates; namespace Opc.Ua.Server { /// /// The standard implementation of a UA server. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] public partial class StandardServer : SessionServerBase { #region Constructors /// /// Initializes the object with default values. /// public StandardServer() { } #endregion #region IDisposable Members /// /// An overrideable version of the Dispose. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_serverInternal"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_registrationTimer"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "m_configurationWatcher")] protected override void Dispose(bool disposing) { if (disposing) { // halt any outstanding timer. if (m_registrationTimer != null) { Utils.SilentDispose(m_registrationTimer); m_registrationTimer = null; } // close the watcher. if (m_configurationWatcher != null) { Utils.SilentDispose(m_configurationWatcher); m_configurationWatcher = null; } // close the server. if (m_serverInternal != null) { Utils.SilentDispose(m_serverInternal); m_serverInternal = null; } } base.Dispose(disposing); } #endregion #region IServer Methods /// /// Invokes the FindServers service. /// /// The request header. /// The endpoint URL. /// The locale ids. /// The server uris. /// List of Servers that meet criteria specified in the request. /// /// Returns a object /// public override ResponseHeader FindServers( RequestHeader requestHeader, string endpointUrl, StringCollection localeIds, StringCollection serverUris, out ApplicationDescriptionCollection servers) { servers = new ApplicationDescriptionCollection(); ValidateRequest(requestHeader); lock (m_lock) { // parse the url provided by the client. IList baseAddresses = BaseAddresses; Uri parsedEndpointUrl = Utils.ParseUri(endpointUrl); if (parsedEndpointUrl != null) { baseAddresses = FilterByEndpointUrl(parsedEndpointUrl, baseAddresses); } // check if nothing to do. if (baseAddresses.Count == 0) { servers = new ApplicationDescriptionCollection(); return CreateResponse(requestHeader, StatusCodes.Good); } // build list of unique servers. Dictionary uniqueServers = new Dictionary(); foreach (EndpointDescription description in GetEndpoints()) { ApplicationDescription server = description.Server; // skip servers that have been processed. if (uniqueServers.ContainsKey(server.ApplicationUri)) { continue; } // check client is filtering by server uri. if (serverUris != null && serverUris.Count > 0) { if (!serverUris.Contains(server.ApplicationUri)) { continue; } } // localize the application name if requested. LocalizedText applicationName = server.ApplicationName; if (localeIds != null && localeIds.Count > 0) { applicationName = m_serverInternal.ResourceManager.Translate(localeIds, applicationName); } // get the application description. ApplicationDescription application = TranslateApplicationDescription( parsedEndpointUrl, server, baseAddresses, applicationName); uniqueServers.Add(server.ApplicationUri, application); // add to list of servers to return. servers.Add(application); } } return CreateResponse(requestHeader, StatusCodes.Good); } /// /// Invokes the GetEndpoints service. /// /// The request header. /// The endpoint URL. /// The locale ids. /// The profile uris. /// The endpoints supported by the server. /// /// Returns a object /// public override ResponseHeader GetEndpoints( RequestHeader requestHeader, string endpointUrl, StringCollection localeIds, StringCollection profileUris, out EndpointDescriptionCollection endpoints) { endpoints = null; ValidateRequest(requestHeader); lock (m_lock) { // filter by profile. IList baseAddresses = FilterByProfile(profileUris, BaseAddresses); // get the descriptions. endpoints = GetEndpointDescriptions( endpointUrl, baseAddresses, localeIds); } return CreateResponse(requestHeader, StatusCodes.Good); } /// /// Returns the endpoints that match the base addresss and endpoint url. /// protected EndpointDescriptionCollection GetEndpointDescriptions( string endpointUrl, IList baseAddresses, StringCollection localeIds) { EndpointDescriptionCollection endpoints = null; // parse the url provided by the client. Uri parsedEndpointUrl = Utils.ParseUri(endpointUrl); if (parsedEndpointUrl != null) { baseAddresses = FilterByEndpointUrl(parsedEndpointUrl, baseAddresses); } // check if nothing to do. if (baseAddresses.Count != 0) { // localize the application name if requested. LocalizedText applicationName = this.ServerDescription.ApplicationName; if (localeIds != null && localeIds.Count > 0) { applicationName = m_serverInternal.ResourceManager.Translate(localeIds, applicationName); } // translate the application description. ApplicationDescription application = TranslateApplicationDescription( parsedEndpointUrl, base.ServerDescription, baseAddresses, applicationName); // translate the endpoint descriptions. endpoints = TranslateEndpointDescriptions( parsedEndpointUrl, baseAddresses, this.Endpoints, application); } return endpoints; } /// /// Invokes the CreateSession service. /// /// The request header. /// Application description for the client application. /// The server URI. /// The endpoint URL. /// Name for the Session assigned by the client. /// The client nonce. /// The client certificate. /// The requested session timeout. /// Size of the max response message. /// The unique public identifier assigned by the Server to the Session. /// The unique private identifier assigned by the Server to the Session. /// The revised session timeout. /// The server nonce. /// The server certificate. /// The server endpoints. /// The server software certificates. /// The server signature. /// Size of the max request message. /// /// Returns a object /// public override ResponseHeader CreateSession( RequestHeader requestHeader, ApplicationDescription clientDescription, string serverUri, string endpointUrl, string sessionName, byte[] clientNonce, byte[] clientCertificate, double requestedSessionTimeout, uint maxResponseMessageSize, out NodeId sessionId, out NodeId authenticationToken, out double revisedSessionTimeout, out byte[] serverNonce, out byte[] serverCertificate, out EndpointDescriptionCollection serverEndpoints, out SignedSoftwareCertificateCollection serverSoftwareCertificates, out SignatureData serverSignature, out uint maxRequestMessageSize) { sessionId = 0; revisedSessionTimeout = 0; serverNonce = null; serverCertificate = null; serverSoftwareCertificates = null; serverSignature = null; maxRequestMessageSize = (uint)MessageContext.MaxMessageSize; OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSession); try { // check the server uri. if (!String.IsNullOrEmpty(serverUri)) { if (serverUri != this.Configuration.ApplicationUri) { throw new ServiceResultException(StatusCodes.BadServerUriInvalid); } } bool requireEncryption = ServerBase.RequireEncryption(context.ChannelContext.EndpointDescription); if (!requireEncryption && clientCertificate != null) { requireEncryption = true; } // validate client application instance certificate. X509Certificate2 parsedClientCertificate = null; if (requireEncryption && clientCertificate != null && clientCertificate.Length > 0) { try { X509Certificate2Collection clientCertificateChain = Utils.ParseCertificateChainBlob(clientCertificate); parsedClientCertificate = clientCertificateChain[0]; if (context.SecurityPolicyUri != SecurityPolicies.None) { string certificateApplicationUri = X509Utils.GetApplicationUriFromCertificate(parsedClientCertificate); // verify if applicationUri from ApplicationDescription matches the applicationUri in the client certificate. if (!String.IsNullOrEmpty(certificateApplicationUri) && !String.IsNullOrEmpty(clientDescription.ApplicationUri) && certificateApplicationUri != clientDescription.ApplicationUri) { throw ServiceResultException.Create( StatusCodes.BadCertificateUriInvalid, "The URI specified in the ApplicationDescription does not match the URI in the Certificate."); } CertificateValidator.Validate(clientCertificateChain); } } catch (Exception e) { OnApplicationCertificateError(clientCertificate, new ServiceResult(e)); } } // verify the nonce provided by the client. if (clientNonce != null) { if (clientNonce.Length < m_minNonceLength) { throw new ServiceResultException(StatusCodes.BadNonceInvalid); } // ignore nonce if security policy set to none if (context.SecurityPolicyUri == SecurityPolicies.None) { clientNonce = null; } } // create the session. Session session = ServerInternal.SessionManager.CreateSession( context, requireEncryption ? InstanceCertificate : null, sessionName, clientNonce, clientDescription, endpointUrl, parsedClientCertificate, requestedSessionTimeout, maxResponseMessageSize, out sessionId, out authenticationToken, out serverNonce, out revisedSessionTimeout); lock (m_lock) { // return the application instance certificate for the server. if (requireEncryption) { // check if complete chain should be sent. if (Configuration.SecurityConfiguration.SendCertificateChain && InstanceCertificateChain != null && InstanceCertificateChain.Count > 0) { List serverCertificateChain = new List(); for (int i = 0; i < InstanceCertificateChain.Count; i++) { serverCertificateChain.AddRange(InstanceCertificateChain[i].RawData); } serverCertificate = serverCertificateChain.ToArray(); } else { serverCertificate = InstanceCertificate.RawData; } } // return the endpoints supported by the server. serverEndpoints = GetEndpointDescriptions(endpointUrl, BaseAddresses, null); // return the software certificates assigned to the server. serverSoftwareCertificates = new SignedSoftwareCertificateCollection(ServerProperties.SoftwareCertificates); // sign the nonce provided by the client. serverSignature = null; // sign the client nonce (if provided). if (parsedClientCertificate != null && clientNonce != null) { byte[] dataToSign = Utils.Append(parsedClientCertificate.RawData, clientNonce); serverSignature = SecurityPolicies.Sign(InstanceCertificate, context.SecurityPolicyUri, dataToSign); } } lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.CurrentSessionCount++; ServerInternal.ServerDiagnostics.CumulatedSessionCount++; } Utils.Trace("Server - SESSION CREATED. SessionId={0}", sessionId); return CreateResponse(requestHeader, StatusCodes.Good); } catch (ServiceResultException e) { Utils.Trace("Server - SESSION CREATE failed. {0}", e.Message); lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedSessionCount++; ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedSessionCount++; ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException((DiagnosticsMasks)requestHeader.ReturnDiagnostics, new StringCollection(), e); } finally { OnRequestComplete(context); } } /// /// Invokes the ActivateSession service. /// /// The request header. /// The client signature. /// The client software certificates. /// The locale ids. /// The user identity token. /// The user token signature. /// The server nonce. /// The results. /// The diagnostic infos. /// /// Returns a object /// public override ResponseHeader ActivateSession( RequestHeader requestHeader, SignatureData clientSignature, SignedSoftwareCertificateCollection clientSoftwareCertificates, StringCollection localeIds, ExtensionObject userIdentityToken, SignatureData userTokenSignature, out byte[] serverNonce, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { serverNonce = null; results = null; diagnosticInfos = null; OperationContext context = ValidateRequest(requestHeader, RequestType.ActivateSession); try { // validate client's software certificates. List softwareCertificates = new List(); if (context.SecurityPolicyUri != SecurityPolicies.None) { bool diagnosticsExist = false; if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { diagnosticInfos = new DiagnosticInfoCollection(); } results = new StatusCodeCollection(); diagnosticInfos = new DiagnosticInfoCollection(); foreach (SignedSoftwareCertificate signedCertificate in clientSoftwareCertificates) { SoftwareCertificate softwareCertificate = null; ServiceResult result = SoftwareCertificate.Validate( CertificateValidator, signedCertificate.CertificateData, out softwareCertificate); if (ServiceResult.IsBad(result)) { results.Add(result.Code); // add diagnostics if requested. if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { DiagnosticInfo diagnosticInfo = ServerUtils.CreateDiagnosticInfo(ServerInternal, context, result); diagnosticInfos.Add(diagnosticInfo); diagnosticsExist = true; } } else { softwareCertificates.Add(softwareCertificate); results.Add(StatusCodes.Good); // add diagnostics if requested. if ((context.DiagnosticsMask & DiagnosticsMasks.OperationAll) != 0) { diagnosticInfos.Add(null); } } } if (!diagnosticsExist && diagnosticInfos != null) { diagnosticInfos.Clear(); } } // check if certificates meet the server's requirements. ValidateSoftwareCertificates(softwareCertificates); // activate the session. bool identityChanged = ServerInternal.SessionManager.ActivateSession( context, requestHeader.AuthenticationToken, clientSignature, softwareCertificates, userIdentityToken, userTokenSignature, localeIds, out serverNonce); if (identityChanged) { // TBD - call Node Manager and Subscription Manager. } Utils.Trace("Server - SESSION ACTIVATED."); return CreateResponse(requestHeader, StatusCodes.Good); } catch (ServiceResultException e) { Utils.Trace("Server - SESSION ACTIVATE failed. {0}", e.Message); lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedSessionCount++; ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedSessionCount++; ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException((DiagnosticsMasks)requestHeader.ReturnDiagnostics, localeIds, e); } finally { OnRequestComplete(context); } } /// /// Returns whether the error is a security error. /// /// The error. /// /// true if the error is one of the security errors, otherwise false. /// protected bool IsSecurityError(StatusCode error) { switch (error.CodeBits) { case StatusCodes.BadUserSignatureInvalid: case StatusCodes.BadUserAccessDenied: case StatusCodes.BadSecurityPolicyRejected: case StatusCodes.BadSecurityModeRejected: case StatusCodes.BadSecurityChecksFailed: case StatusCodes.BadSecureChannelTokenUnknown: case StatusCodes.BadSecureChannelIdInvalid: case StatusCodes.BadNoValidCertificates: case StatusCodes.BadIdentityTokenInvalid: case StatusCodes.BadIdentityTokenRejected: case StatusCodes.BadIdentityChangeNotSupported: case StatusCodes.BadCertificateUseNotAllowed: case StatusCodes.BadCertificateUriInvalid: case StatusCodes.BadCertificateUntrusted: case StatusCodes.BadCertificateTimeInvalid: case StatusCodes.BadCertificateRevoked: case StatusCodes.BadCertificateRevocationUnknown: case StatusCodes.BadCertificateIssuerUseNotAllowed: case StatusCodes.BadCertificateIssuerTimeInvalid: case StatusCodes.BadCertificateIssuerRevoked: case StatusCodes.BadCertificateIssuerRevocationUnknown: case StatusCodes.BadCertificateInvalid: case StatusCodes.BadCertificateHostNameInvalid: case StatusCodes.BadApplicationSignatureInvalid: { return true; } } return false; } /// /// Creates the response header. /// /// The object that contains description for the RequestHeader DataType. /// The exception used to create DiagnosticInfo assigned to the ServiceDiagnostics. /// Returns a description for the ResponseHeader DataType. protected ResponseHeader CreateResponse(RequestHeader requestHeader, ServiceResultException exception) { ResponseHeader responseHeader = new ResponseHeader(); responseHeader.ServiceResult = exception.StatusCode; responseHeader.Timestamp = DateTime.UtcNow; responseHeader.RequestHandle = requestHeader.RequestHandle; StringTable stringTable = new StringTable(); responseHeader.ServiceDiagnostics = new DiagnosticInfo(exception, (DiagnosticsMasks)requestHeader.ReturnDiagnostics, true, stringTable); responseHeader.StringTable = stringTable.ToArray(); return responseHeader; } /// /// Invokes the CloseSession service. /// /// The request header. /// if set to true subscriptions are deleted. /// /// Returns a object /// public override ResponseHeader CloseSession(RequestHeader requestHeader, bool deleteSubscriptions) { OperationContext context = ValidateRequest(requestHeader, RequestType.CloseSession); try { ServerInternal.CloseSession(context, context.Session.Id, deleteSubscriptions); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the Cancel service. /// /// The request header. /// The request handle assigned to the request. /// The number of cancelled requests. /// /// Returns a object /// public override ResponseHeader Cancel( RequestHeader requestHeader, uint requestHandle, out uint cancelCount) { cancelCount = 0; OperationContext context = ValidateRequest(requestHeader, RequestType.Cancel); try { m_serverInternal.RequestManager.CancelRequests(requestHandle, out cancelCount); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the Browse service. /// /// The request header. /// The view. /// The maximum number of references to return for each node. /// The list of nodes to browse. /// The list of results for the passed starting nodes and filters. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader Browse( RequestHeader requestHeader, ViewDescription view, uint requestedMaxReferencesPerNode, BrowseDescriptionCollection nodesToBrowse, out BrowseResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { results = null; diagnosticInfos = null; OperationContext context = ValidateRequest(requestHeader, RequestType.Browse); try { if (nodesToBrowse == null || nodesToBrowse.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.Browse( context, view, requestedMaxReferencesPerNode, nodesToBrowse, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the BrowseNext service. /// /// The request header. /// if set to true the continuation points are released. /// A list of continuation points returned in a previous Browse or BrewseNext call. /// The list of resulted references for browse. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader BrowseNext( RequestHeader requestHeader, bool releaseContinuationPoints, ByteStringCollection continuationPoints, out BrowseResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { results = null; diagnosticInfos = null; OperationContext context = ValidateRequest(requestHeader, RequestType.BrowseNext); try { if (continuationPoints == null || continuationPoints.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.BrowseNext( context, releaseContinuationPoints, continuationPoints, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the RegisterNodes service. /// /// The request header. /// The list of NodeIds to register. /// The list of NodeIds identifying the registered nodes. /// /// Returns a object /// public override ResponseHeader RegisterNodes( RequestHeader requestHeader, NodeIdCollection nodesToRegister, out NodeIdCollection registeredNodeIds) { registeredNodeIds = null; OperationContext context = ValidateRequest(requestHeader, RequestType.RegisterNodes); try { if (nodesToRegister == null || nodesToRegister.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.RegisterNodes( context, nodesToRegister, out registeredNodeIds); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the UnregisterNodes service. /// /// The request header. /// The list of NodeIds to unregister /// /// Returns a object /// public override ResponseHeader UnregisterNodes(RequestHeader requestHeader, NodeIdCollection nodesToUnregister) { OperationContext context = ValidateRequest(requestHeader, RequestType.UnregisterNodes); try { if (nodesToUnregister == null || nodesToUnregister.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.UnregisterNodes( context, nodesToUnregister); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the TranslateBrowsePathsToNodeIds service. /// /// The request header. /// The list of browse paths for which NodeIds are being requested. /// The list of results for the list of browse paths. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader TranslateBrowsePathsToNodeIds( RequestHeader requestHeader, BrowsePathCollection browsePaths, out BrowsePathResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { results = null; diagnosticInfos = null; OperationContext context = ValidateRequest(requestHeader, RequestType.TranslateBrowsePathsToNodeIds); try { if (browsePaths == null || browsePaths.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.TranslateBrowsePathsToNodeIds( context, browsePaths, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the Read service. /// /// The request header. /// The Maximum age of the value to be read in milliseconds. /// The type of timestamps to be returned for the requested Variables. /// The list of Nodes and their Attributes to read. /// The list of returned Attribute values /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader Read( RequestHeader requestHeader, double maxAge, TimestampsToReturn timestampsToReturn, ReadValueIdCollection nodesToRead, out DataValueCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Read); try { if (nodesToRead == null || nodesToRead.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.Read( context, maxAge, timestampsToReturn, nodesToRead, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the HistoryRead service. /// /// The request header. /// The history read details. /// The timestamps to return. /// if set to true continuation points are released. /// The nodes to read. /// The results. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader HistoryRead( RequestHeader requestHeader, ExtensionObject historyReadDetails, TimestampsToReturn timestampsToReturn, bool releaseContinuationPoints, HistoryReadValueIdCollection nodesToRead, out HistoryReadResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.HistoryRead); try { if (nodesToRead == null || nodesToRead.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.HistoryRead( context, historyReadDetails, timestampsToReturn, releaseContinuationPoints, nodesToRead, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the Write service. /// /// The request header. /// The list of Nodes, Attributes, and values to write. /// The list of write result status codes for each write operation. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader Write( RequestHeader requestHeader, WriteValueCollection nodesToWrite, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Write); try { if (nodesToWrite == null || nodesToWrite.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.Write( context, nodesToWrite, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the HistoryUpdate service. /// /// The request header. /// The details defined for the update. /// The list of update results for the history update details. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader HistoryUpdate( RequestHeader requestHeader, ExtensionObjectCollection historyUpdateDetails, out HistoryUpdateResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.HistoryUpdate); try { if (historyUpdateDetails == null || historyUpdateDetails.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } m_serverInternal.NodeManager.HistoryUpdate( context, historyUpdateDetails, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the CreateSubscription service. /// /// The request header. /// The cyclic rate that the Subscription is being requested to return Notifications to the Client. /// The client-requested lifetime count for the Subscription /// The requested max keep alive count. /// The maximum number of notifications that the Client wishes to receive in a single Publish response. /// If set to true publishing is enabled for the Subscription. /// The relative priority of the Subscription. /// The Server-assigned identifier for the Subscription. /// The actual publishing interval that the Server will use. /// The revised lifetime count. /// The revised max keep alive count. /// /// Returns a object /// public override ResponseHeader CreateSubscription( RequestHeader requestHeader, double requestedPublishingInterval, uint requestedLifetimeCount, uint requestedMaxKeepAliveCount, uint maxNotificationsPerPublish, bool publishingEnabled, byte priority, out uint subscriptionId, out double revisedPublishingInterval, out uint revisedLifetimeCount, out uint revisedMaxKeepAliveCount) { OperationContext context = ValidateRequest(requestHeader, RequestType.CreateSubscription); try { ServerInternal.SubscriptionManager.CreateSubscription( context, requestedPublishingInterval, requestedLifetimeCount, requestedMaxKeepAliveCount, maxNotificationsPerPublish, publishingEnabled, priority, out subscriptionId, out revisedPublishingInterval, out revisedLifetimeCount, out revisedMaxKeepAliveCount); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the DeleteSubscriptions service. /// /// The request header. /// The list of Subscriptions to delete. /// The list of result StatusCodes for the Subscriptions to delete. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader DeleteSubscriptions( RequestHeader requestHeader, UInt32Collection subscriptionIds, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.DeleteSubscriptions); try { if (subscriptionIds == null || subscriptionIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.DeleteSubscriptions( context, subscriptionIds, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the Publish service. /// /// The request header. /// The list of acknowledgements for one or more Subscriptions. /// The subscription identifier. /// The available sequence numbers. /// If set to true the number of Notifications that were ready to be sent could not be sent in a single response. /// The NotificationMessage that contains the list of Notifications. /// The list of results for the acknowledgements. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader Publish( RequestHeader requestHeader, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out NotificationMessage notificationMessage, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Publish); try { /* // check if there is an odd delay. if (DateTime.UtcNow > requestHeader.Timestamp.AddMilliseconds(100)) { Utils.Trace( "WARNING. Unexpected delay receiving Publish request. Time={0:hh:mm:ss.fff}, ReceiveTime={1:hh:mm:ss.fff}", DateTime.UtcNow, requestHeader.Timestamp); } */ Utils.Trace("PUBLISH #{0} RECEIVED. TIME={1:hh:mm:ss.fff}", requestHeader.RequestHandle, requestHeader.Timestamp); notificationMessage = ServerInternal.SubscriptionManager.Publish( context, subscriptionAcknowledgements, null, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out results, out diagnosticInfos); /* if (notificationMessage != null) { Utils.Trace( "PublishResponse: SubId={0} SeqNo={1}, PublishTime={2:mm:ss.fff}, Time={3:mm:ss.fff}", subscriptionId, notificationMessage.SequenceNumber, notificationMessage.PublishTime, DateTime.UtcNow); } */ return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Begins an asynchronous publish operation. /// /// The request. public virtual void BeginPublish(IEndpointIncomingRequest request) { PublishRequest input = (PublishRequest)request.Request; OperationContext context = ValidateRequest(input.RequestHeader, RequestType.Publish); try { AsyncPublishOperation operation = new AsyncPublishOperation(context, request, this); uint subscriptionId = 0; UInt32Collection availableSequenceNumbers = null; bool moreNotifications = false; NotificationMessage notificationMessage = null; StatusCodeCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; notificationMessage = ServerInternal.SubscriptionManager.Publish( context, input.SubscriptionAcknowledgements, operation, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out results, out diagnosticInfos); // request completed asychrnously. if (notificationMessage != null) { OnRequestComplete(context); operation.Response.ResponseHeader = CreateResponse(input.RequestHeader, context.StringTable); operation.Response.SubscriptionId = subscriptionId; operation.Response.AvailableSequenceNumbers = availableSequenceNumbers; operation.Response.MoreNotifications = moreNotifications; operation.Response.Results = results; operation.Response.DiagnosticInfos = diagnosticInfos; operation.Response.NotificationMessage = notificationMessage; Utils.Trace("PUBLISH: #{0} Completed Synchronously", input.RequestHeader.RequestHandle); request.OperationCompleted(operation.Response, null); } } catch (ServiceResultException e) { OnRequestComplete(context); lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } } /// /// Completes an asynchronous publish operation. /// /// The request. public virtual void CompletePublish(IEndpointIncomingRequest request) { AsyncPublishOperation operation = (AsyncPublishOperation)request.Calldata; OperationContext context = operation.Context; try { if (ServerInternal.SubscriptionManager.CompletePublish(context, operation)) { operation.Response.ResponseHeader = CreateResponse(request.Request.RequestHeader, context.StringTable); request.OperationCompleted(operation.Response, null); OnRequestComplete(context); } } catch (ServiceResultException e) { OnRequestComplete(context); lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } } /// /// Invokes the Republish service. /// /// The request header. /// The subscription id. /// The sequence number of a specific NotificationMessage to be republished. /// The requested NotificationMessage. /// /// Returns a object /// public override ResponseHeader Republish( RequestHeader requestHeader, uint subscriptionId, uint retransmitSequenceNumber, out NotificationMessage notificationMessage) { OperationContext context = ValidateRequest(requestHeader, RequestType.Republish); try { notificationMessage = ServerInternal.SubscriptionManager.Republish( context, subscriptionId, retransmitSequenceNumber); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the ModifySubscription service. /// /// The request header. /// The subscription id. /// The cyclic rate that the Subscription is being requested to return Notifications to the Client. /// The client-requested lifetime count for the Subscription. /// The requested max keep alive count. /// The maximum number of notifications that the Client wishes to receive in a single Publish response. /// The relative priority of the Subscription. /// The revised publishing interval. /// The revised lifetime count. /// The revised max keep alive count. /// /// Returns a object /// public override ResponseHeader ModifySubscription( RequestHeader requestHeader, uint subscriptionId, double requestedPublishingInterval, uint requestedLifetimeCount, uint requestedMaxKeepAliveCount, uint maxNotificationsPerPublish, byte priority, out double revisedPublishingInterval, out uint revisedLifetimeCount, out uint revisedMaxKeepAliveCount) { OperationContext context = ValidateRequest(requestHeader, RequestType.ModifySubscription); try { ServerInternal.SubscriptionManager.ModifySubscription( context, subscriptionId, requestedPublishingInterval, requestedLifetimeCount, requestedMaxKeepAliveCount, maxNotificationsPerPublish, priority, out revisedPublishingInterval, out revisedLifetimeCount, out revisedMaxKeepAliveCount); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the SetPublishingMode service. /// /// The request header. /// If set to true publishing of NotificationMessages is enabled for the Subscription. /// The list of subscription ids. /// The list of StatusCodes for the Subscriptions to enable/disable. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader SetPublishingMode( RequestHeader requestHeader, bool publishingEnabled, UInt32Collection subscriptionIds, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.SetPublishingMode); try { if (subscriptionIds == null || subscriptionIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.SetPublishingMode( context, publishingEnabled, subscriptionIds, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the SetTriggering service. /// /// The request header. /// The subscription id. /// The id for the MonitoredItem used as the triggering item. /// The list of ids of the items to report that are to be added as triggering links. /// The list of ids of the items to report for the triggering links to be deleted. /// The list of StatusCodes for the items to add. /// The list of diagnostic information for the links to add. /// The list of StatusCodes for the items to delete. /// The list of diagnostic information for the links to delete. /// /// Returns a object /// public override ResponseHeader SetTriggering( RequestHeader requestHeader, uint subscriptionId, uint triggeringItemId, UInt32Collection linksToAdd, UInt32Collection linksToRemove, out StatusCodeCollection addResults, out DiagnosticInfoCollection addDiagnosticInfos, out StatusCodeCollection removeResults, out DiagnosticInfoCollection removeDiagnosticInfos) { addResults = null; addDiagnosticInfos = null; removeResults = null; removeDiagnosticInfos = null; OperationContext context = ValidateRequest(requestHeader, RequestType.SetTriggering); try { if ((linksToAdd == null || linksToAdd.Count == 0) && (linksToRemove == null || linksToRemove.Count == 0)) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.SetTriggering( context, subscriptionId, triggeringItemId, linksToAdd, linksToRemove, out addResults, out addDiagnosticInfos, out removeResults, out removeDiagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the CreateMonitoredItems service. /// /// The request header. /// The subscription id that will report notifications. /// The type of timestamps to be returned for the MonitoredItems. /// The list of MonitoredItems to be created and assigned to the specified subscription /// The list of results for the MonitoredItems to create. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader CreateMonitoredItems( RequestHeader requestHeader, uint subscriptionId, TimestampsToReturn timestampsToReturn, MonitoredItemCreateRequestCollection itemsToCreate, out MonitoredItemCreateResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.CreateMonitoredItems); try { if (itemsToCreate == null || itemsToCreate.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.CreateMonitoredItems( context, subscriptionId, timestampsToReturn, itemsToCreate, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the ModifyMonitoredItems service. /// /// The request header. /// The subscription id. /// The type of timestamps to be returned for the MonitoredItems. /// The list of MonitoredItems to modify. /// The list of results for the MonitoredItems to modify. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader ModifyMonitoredItems( RequestHeader requestHeader, uint subscriptionId, TimestampsToReturn timestampsToReturn, MonitoredItemModifyRequestCollection itemsToModify, out MonitoredItemModifyResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.ModifyMonitoredItems); try { if (itemsToModify == null || itemsToModify.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.ModifyMonitoredItems( context, subscriptionId, timestampsToReturn, itemsToModify, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the DeleteMonitoredItems service. /// /// The request header. /// The subscription id. /// The list of MonitoredItems to delete. /// The list of results for the MonitoredItems to delete. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader DeleteMonitoredItems( RequestHeader requestHeader, uint subscriptionId, UInt32Collection monitoredItemIds, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.DeleteMonitoredItems); try { if (monitoredItemIds == null || monitoredItemIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.DeleteMonitoredItems( context, subscriptionId, monitoredItemIds, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the SetMonitoringMode service. /// /// The request header. /// The subscription id. /// The monitoring mode to be set for the MonitoredItems. /// The list of MonitoredItems to modify. /// The list of results for the MonitoredItems to modify. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader SetMonitoringMode( RequestHeader requestHeader, uint subscriptionId, MonitoringMode monitoringMode, UInt32Collection monitoredItemIds, out StatusCodeCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.SetMonitoringMode); try { if (monitoredItemIds == null || monitoredItemIds.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } ServerInternal.SubscriptionManager.SetMonitoringMode( context, subscriptionId, monitoringMode, monitoredItemIds, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } /// /// Invokes the Call service. /// /// The request header. /// The methods to call. /// The results. /// The diagnostic information for the results. /// /// Returns a object /// public override ResponseHeader Call( RequestHeader requestHeader, CallMethodRequestCollection methodsToCall, out CallMethodResultCollection results, out DiagnosticInfoCollection diagnosticInfos) { OperationContext context = ValidateRequest(requestHeader, RequestType.Call); try { if (methodsToCall == null || methodsToCall.Count == 0) { throw new ServiceResultException(StatusCodes.BadNothingToDo); } uint maxNodesPerMethodCall = 0; try { maxNodesPerMethodCall = ServerInternal.ServerObject.ServerCapabilities.OperationLimits.MaxNodesPerMethodCall.Value; } catch { //ignore erros } if (maxNodesPerMethodCall > 0 && methodsToCall.Count > maxNodesPerMethodCall) { throw new ServiceResultException(StatusCodes.BadTooManyOperations); } m_serverInternal.NodeManager.Call( context, methodsToCall, out results, out diagnosticInfos); return CreateResponse(requestHeader, context.StringTable); } catch (ServiceResultException e) { lock (ServerInternal.DiagnosticsWriteLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } finally { OnRequestComplete(context); } } #endregion #region Public Methods used by the Host Process /// /// The state object associated with the server. /// It provides the shared components for the Server. /// /// The current instance. public IServerInternal CurrentInstance { get { lock (m_lock) { if (m_serverInternal == null) { throw new ServiceResultException(StatusCodes.BadServerHalted); } return m_serverInternal; } } } /// /// Returns the current status of the server. /// /// Returns a ServerStatusDataType object public ServerStatusDataType GetStatus() { lock (m_lock) { if (m_serverInternal == null) { throw new ServiceResultException(StatusCodes.BadServerHalted); } return m_serverInternal.Status.Value; } } /// /// Registers the server with the discovery server. /// /// Boolean value. public async Task RegisterWithDiscoveryServer() { ApplicationConfiguration configuration = string.IsNullOrEmpty(base.Configuration.SourceFilePath) ? base.Configuration : await ApplicationConfiguration.Load(new FileInfo(base.Configuration.SourceFilePath), ApplicationType.Server, null, false); CertificateValidationEventHandler registrationCertificateValidator = new CertificateValidationEventHandler(RegistrationValidator_CertificateValidation); configuration.CertificateValidator.CertificateValidation += registrationCertificateValidator; try { // try each endpoint. if (m_registrationEndpoints != null) { foreach (ConfiguredEndpoint endpoint in m_registrationEndpoints.Endpoints) { RegistrationClient client = null; int i = 0; while (i++ < 2) { try { // update from the server. bool updateRequired = true; lock (m_registrationLock) { updateRequired = endpoint.UpdateBeforeConnect; } if (updateRequired) { endpoint.UpdateFromServer(); } lock (m_registrationLock) { endpoint.UpdateBeforeConnect = false; } RequestHeader requestHeader = new RequestHeader(); requestHeader.Timestamp = DateTime.UtcNow; // create the client. client = RegistrationClient.Create( configuration, endpoint.Description, endpoint.Configuration, base.InstanceCertificate); client.OperationTimeout = 10000; // register the server. if (m_useRegisterServer2) { ExtensionObjectCollection discoveryConfiguration = new ExtensionObjectCollection(); StatusCodeCollection configurationResults = null; DiagnosticInfoCollection diagnosticInfos = null; MdnsDiscoveryConfiguration mdnsDiscoveryConfig = new MdnsDiscoveryConfiguration(); mdnsDiscoveryConfig.ServerCapabilities = configuration.ServerConfiguration.ServerCapabilities; mdnsDiscoveryConfig.MdnsServerName = Utils.GetHostName(); ExtensionObject extensionObject = new ExtensionObject(mdnsDiscoveryConfig); discoveryConfiguration.Add(extensionObject); client.RegisterServer2( requestHeader, m_registrationInfo, discoveryConfiguration, out configurationResults, out diagnosticInfos); } else { client.RegisterServer(requestHeader, m_registrationInfo); } return true; } catch (Exception e) { Utils.Trace("RegisterServer{0} failed for at: {1}. Exception={2}", m_useRegisterServer2 ? "2" : "", endpoint.EndpointUrl, e.Message); m_useRegisterServer2 = !m_useRegisterServer2; } finally { if (client != null) { try { client.Close(); client = null; } catch (Exception e) { Utils.Trace("Could not cleanly close connection with LDS. Exception={0}", e.Message); } } } } } // retry to start with RegisterServer2 if both failed m_useRegisterServer2 = true; } } finally { if (configuration != null) { configuration.CertificateValidator.CertificateValidation -= registrationCertificateValidator; } } return false; } /// /// Checks that the domains in the certificate match the current host. /// private void RegistrationValidator_CertificateValidation(CertificateValidator sender, CertificateValidationEventArgs e) { System.Net.IPAddress[] targetAddresses = Utils.GetHostAddresses(Utils.GetHostName()); foreach (string domain in X509Utils.GetDomainsFromCertficate(e.Certificate)) { System.Net.IPAddress[] actualAddresses = Utils.GetHostAddresses(domain); foreach (System.Net.IPAddress actualAddress in actualAddresses) { foreach (System.Net.IPAddress targetAddress in targetAddresses) { if (targetAddress.Equals(actualAddress)) { e.Accept = true; return; } } } } } /// /// Registers the server endpoints with the LDS. /// /// The state. private async void OnRegisterServer(object state) { try { lock (m_registrationLock) { // halt any outstanding timer. if (m_registrationTimer != null) { m_registrationTimer.Dispose(); m_registrationTimer = null; } } if (await RegisterWithDiscoveryServer()) { // schedule next registration. lock (m_registrationLock) { if (m_maxRegistrationInterval > 0) { m_registrationTimer = new Timer( OnRegisterServer, this, m_maxRegistrationInterval, Timeout.Infinite); m_lastRegistrationInterval = m_minRegistrationInterval; Utils.Trace("Register server succeeded. Registering again in {0} ms", m_maxRegistrationInterval); } } } else { lock (m_registrationLock) { if (m_registrationTimer == null) { // calculate next registration attempt. m_lastRegistrationInterval *= 2; if (m_lastRegistrationInterval > m_maxRegistrationInterval) { m_lastRegistrationInterval = m_maxRegistrationInterval; } Utils.Trace("Register server failed. Trying again in {0} ms", m_lastRegistrationInterval); // create timer. m_registrationTimer = new Timer(OnRegisterServer, this, m_lastRegistrationInterval, Timeout.Infinite); } } } } catch (Exception e) { Utils.Trace(e, "Unexpected exception handling registration timer."); } } #endregion #region Protected Members used for Request Processing /// /// The synchronization object. /// protected object Lock => m_lock; /// /// The state object associated with the server. /// /// The server internal data. protected ServerInternalData ServerInternal { get { ServerInternalData serverInternal = m_serverInternal; if (serverInternal == null) { throw new ServiceResultException(StatusCodes.BadServerHalted); } return serverInternal; } } /// /// Verifies that the request header is valid. /// /// The request header. protected override void ValidateRequest(RequestHeader requestHeader) { // check for server error. ServiceResult error = ServerError; if (ServiceResult.IsBad(error)) { throw new ServiceResultException(error); } // check server state. ServerInternalData serverInternal = m_serverInternal; if (serverInternal == null || !serverInternal.IsRunning) { throw new ServiceResultException(StatusCodes.BadServerHalted); } base.ValidateRequest(requestHeader); } /// /// Updates the server state. /// /// The state. protected virtual void SetServerState(ServerState state) { lock (m_lock) { if (ServiceResult.IsBad(ServerError)) { throw new ServiceResultException(ServerError); } if (m_serverInternal == null) { throw new ServiceResultException(StatusCodes.BadServerHalted); } m_serverInternal.CurrentState = state; } } /// /// Reports an error during initialization after the base server object has been started. /// /// The error. protected virtual void SetServerError(ServiceResult error) { lock (m_lock) { ServerError = error; } } /// /// Handles an error when validating the application instance certificate provided by a client. /// /// The client certificate. /// The result. protected virtual void OnApplicationCertificateError(byte[] clientCertificate, ServiceResult result) { throw new ServiceResultException(result); } /// /// Inspects the software certificates provided by the server. /// /// The software certificates. protected virtual void ValidateSoftwareCertificates(List softwareCertificates) { // always accept valid certificates. } /// /// Verifies that the request header is valid. /// /// The request header. /// Type of the request. /// protected virtual OperationContext ValidateRequest(RequestHeader requestHeader, RequestType requestType) { base.ValidateRequest(requestHeader); if (!ServerInternal.IsRunning) { throw new ServiceResultException(StatusCodes.BadServerHalted); } OperationContext context = ServerInternal.SessionManager.ValidateRequest(requestHeader, requestType); Utils.Trace( (int)Utils.TraceMasks.Service, "{0} Validated. ID={1}", context.RequestType, context.RequestId); // notify the request manager. ServerInternal.RequestManager.RequestReceived(context); return context; } /// /// Translates an exception. /// /// The context. /// The ServiceResultException e. /// Returns an exception thrown when a UA defined error occurs, the return type is . protected virtual ServiceResultException TranslateException(OperationContext context, ServiceResultException e) { IList preferredLocales = null; if (context != null && context.Session != null) { preferredLocales = context.Session.PreferredLocales; } return TranslateException(context.DiagnosticsMask, preferredLocales, e); } /// /// Translates an exception. /// /// The fields to return. /// The preferred locales. /// The ServiceResultException e. /// Returns an exception thrown when a UA defined error occurs, the return type is . protected virtual ServiceResultException TranslateException(DiagnosticsMasks diagnosticsMasks, IList preferredLocales, ServiceResultException e) { if (e == null) { return null; } // check if inner result required. ServiceResult innerResult = null; if ((diagnosticsMasks & (DiagnosticsMasks.ServiceInnerDiagnostics | DiagnosticsMasks.ServiceInnerStatusCode)) != 0) { innerResult = e.InnerResult; } // check if translated text required. LocalizedText translatedText = null; if ((diagnosticsMasks & DiagnosticsMasks.ServiceLocalizedText) != 0) { translatedText = e.LocalizedText; } // create new result object. ServiceResult result = new ServiceResult( e.StatusCode, e.SymbolicId, e.NamespaceUri, translatedText, e.AdditionalInfo, innerResult); // translate result. result = m_serverInternal.ResourceManager.Translate(preferredLocales, result); return new ServiceResultException(result); } /// /// Translates a service result. /// /// The fields to return. /// The preferred locales. /// The result. /// Returns a class that combines the status code and diagnostic info structures. protected virtual ServiceResult TranslateResult(DiagnosticsMasks diagnosticsMasks, IList preferredLocales, ServiceResult result) { if (result == null) { return null; } return m_serverInternal.ResourceManager.Translate(preferredLocales, result); } /// /// Verifies that the request header is valid. /// /// The operation context. protected virtual void OnRequestComplete(OperationContext context) { lock (m_lock) { if (m_serverInternal == null) { throw new ServiceResultException(StatusCodes.BadServerHalted); } m_serverInternal.RequestManager.RequestCompleted(context); } } #endregion #region Protected Members used for Initialization /// /// Raised when the configuration changes. /// /// The sender. /// The instance containing the event data. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")] protected virtual async void OnConfigurationChanged(object sender, ConfigurationWatcherEventArgs args) { try { ApplicationConfiguration configuration = await ApplicationConfiguration.Load( new FileInfo(args.FilePath), Configuration.ApplicationType, Configuration.GetType()); OnUpdateConfiguration(configuration); } catch (Exception e) { Utils.Trace(e, "Could not load updated configuration file from: {0}", args); } } /// /// Called when the server configuration is changed on disk. /// /// The configuration. /// /// Servers are free to ignore changes if it is difficult/impossible to apply them without a restart. /// protected override void OnUpdateConfiguration(ApplicationConfiguration configuration) { lock (m_lock) { // update security configuration. configuration.SecurityConfiguration.Validate(); Configuration.SecurityConfiguration.TrustedIssuerCertificates = configuration.SecurityConfiguration.TrustedIssuerCertificates; Configuration.SecurityConfiguration.TrustedPeerCertificates = configuration.SecurityConfiguration.TrustedPeerCertificates; Configuration.SecurityConfiguration.RejectedCertificateStore = configuration.SecurityConfiguration.RejectedCertificateStore; Configuration.CertificateValidator.Update(Configuration.SecurityConfiguration).Wait(); // update trace configuration. Configuration.TraceConfiguration = configuration.TraceConfiguration; if (Configuration.TraceConfiguration == null) { Configuration.TraceConfiguration = new TraceConfiguration(); } Configuration.TraceConfiguration.ApplySettings(); } } /// /// Called before the server starts. /// /// The configuration. protected override void OnServerStarting(ApplicationConfiguration configuration) { lock (m_lock) { base.OnServerStarting(configuration); // save minimum nonce length. m_minNonceLength = configuration.SecurityConfiguration.NonceLength; // try first RegisterServer2 m_useRegisterServer2 = true; } } /// /// Creates the endpoints and creates the hosts. /// /// The configuration. /// The server description. /// The endpoints. /// /// Returns IList of a host for a UA service. /// protected override IList InitializeServiceHosts( ApplicationConfiguration configuration, out ApplicationDescription serverDescription, out EndpointDescriptionCollection endpoints) { serverDescription = null; endpoints = null; Dictionary hosts = new Dictionary(); // ensure at least one security policy exists. if (configuration.ServerConfiguration.SecurityPolicies.Count == 0) { configuration.ServerConfiguration.SecurityPolicies.Add(new ServerSecurityPolicy()); } // ensure at least one user token policy exists. if (configuration.ServerConfiguration.UserTokenPolicies.Count == 0) { UserTokenPolicy userTokenPolicy = new UserTokenPolicy(); userTokenPolicy.TokenType = UserTokenType.Anonymous; userTokenPolicy.PolicyId = userTokenPolicy.TokenType.ToString(); configuration.ServerConfiguration.UserTokenPolicies.Add(userTokenPolicy); } // set server description. serverDescription = new ApplicationDescription(); serverDescription.ApplicationUri = configuration.ApplicationUri; serverDescription.ApplicationName = new LocalizedText("en-US", configuration.ApplicationName); serverDescription.ApplicationType = configuration.ApplicationType; serverDescription.ProductUri = configuration.ProductUri; serverDescription.DiscoveryUrls = GetDiscoveryUrls(); endpoints = new EndpointDescriptionCollection(); IList endpointsForHost = null; foreach (var scheme in Utils.DefaultUriSchemes) { var binding = TransportBindings.Listeners.GetBinding(scheme); if (binding != null) { endpointsForHost = binding.CreateServiceHost( this, hosts, configuration, configuration.ServerConfiguration.BaseAddresses, serverDescription, configuration.ServerConfiguration.SecurityPolicies, InstanceCertificate, InstanceCertificateChain ); endpoints.AddRange(endpointsForHost); } } return new List(hosts.Values); } /// /// Returns the service contract to use. /// protected override Type GetServiceContract() { return typeof(ISessionEndpoint); } /// /// Returns an instance of the endpoint to use. /// protected override EndpointBase GetEndpointInstance(ServerBase server) { return new SessionEndpoint(server); } /// /// Starts the server application. /// /// The configuration. protected override void StartApplication(ApplicationConfiguration configuration) { base.StartApplication(configuration); lock (m_lock) { try { // create the datastore for the instance. m_serverInternal = new ServerInternalData( ServerProperties, configuration, MessageContext, new CertificateValidator(), InstanceCertificate); // create the manager responsible for providing localized string resources. ResourceManager resourceManager = CreateResourceManager(m_serverInternal, configuration); // create the manager responsible for incoming requests. RequestManager requestManager = CreateRequestManager(m_serverInternal, configuration); // create the master node manager. MasterNodeManager masterNodeManager = CreateMasterNodeManager(m_serverInternal, configuration); // add the node manager to the datastore. m_serverInternal.SetNodeManager(masterNodeManager); // put the node manager into a state that allows it to be used by other objects. masterNodeManager.Startup(); // create the manager responsible for handling events. EventManager eventManager = CreateEventManager(m_serverInternal, configuration); // creates the server object. m_serverInternal.CreateServerObject( eventManager, resourceManager, requestManager); // do any additional processing now that the node manager is up and running. OnNodeManagerStarted(m_serverInternal); // create the manager responsible for aggregates. m_serverInternal.AggregateManager = CreateAggregateManager(m_serverInternal, configuration); // start the session manager. SessionManager sessionManager = CreateSessionManager(m_serverInternal, configuration); sessionManager.Startup(); // start the subscription manager. SubscriptionManager subscriptionManager = CreateSubscriptionManager(m_serverInternal, configuration); subscriptionManager.Startup(); // add the session manager to the datastore. m_serverInternal.SetSessionManager(sessionManager, subscriptionManager); ServerError = null; // setup registration information. lock (m_registrationLock) { m_maxRegistrationInterval = configuration.ServerConfiguration.MaxRegistrationInterval; ApplicationDescription serverDescription = ServerDescription; m_registrationInfo = new RegisteredServer(); m_registrationInfo.ServerUri = serverDescription.ApplicationUri; m_registrationInfo.ServerNames.Add(serverDescription.ApplicationName); m_registrationInfo.ProductUri = serverDescription.ProductUri; m_registrationInfo.ServerType = serverDescription.ApplicationType; m_registrationInfo.GatewayServerUri = null; m_registrationInfo.IsOnline = true; m_registrationInfo.SemaphoreFilePath = null; // add all discovery urls. string computerName = Utils.GetHostName(); for (int ii = 0; ii < BaseAddresses.Count; ii++) { UriBuilder uri = new UriBuilder(BaseAddresses[ii].DiscoveryUrl); if (String.Compare(uri.Host, "localhost", StringComparison.OrdinalIgnoreCase) == 0) { uri.Host = computerName; } m_registrationInfo.DiscoveryUrls.Add(uri.ToString()); } // build list of registration endpoints. m_registrationEndpoints = new ConfiguredEndpointCollection(configuration); EndpointDescription endpoint = configuration.ServerConfiguration.RegistrationEndpoint; if (endpoint == null) { endpoint = new EndpointDescription(); endpoint.EndpointUrl = Utils.Format(Utils.DiscoveryUrls[0], "localhost"); endpoint.SecurityLevel = ServerSecurityPolicy.CalculateSecurityLevel(MessageSecurityMode.SignAndEncrypt, SecurityPolicies.Basic256Sha256); endpoint.SecurityMode = MessageSecurityMode.SignAndEncrypt; endpoint.SecurityPolicyUri = SecurityPolicies.Basic256Sha256; endpoint.Server.ApplicationType = ApplicationType.DiscoveryServer; } m_registrationEndpoints.Add(endpoint); m_minRegistrationInterval = 1000; m_lastRegistrationInterval = m_minRegistrationInterval; // start registration timer. if (m_registrationTimer != null) { m_registrationTimer.Dispose(); m_registrationTimer = null; } if (m_maxRegistrationInterval > 0) { m_registrationTimer = new Timer(OnRegisterServer, this, m_minRegistrationInterval, Timeout.Infinite); } } // set the server status as running. SetServerState(ServerState.Running); // all initialization is complete. OnServerStarted(m_serverInternal); // monitor the configuration file. if (!String.IsNullOrEmpty(configuration.SourceFilePath)) { m_configurationWatcher = new ConfigurationWatcher(configuration); m_configurationWatcher.Changed += new EventHandler(this.OnConfigurationChanged); } CertificateValidator.CertificateUpdate += OnCertificateUpdate; } catch (Exception e) { Utils.Trace(e, "Unexpected error starting application"); m_serverInternal = null; ServiceResult error = ServiceResult.Create(e, StatusCodes.BadInternalError, "Unexpected error starting application"); ServerError = error; throw new ServiceResultException(error); } } } /// /// Called before the server stops /// protected override void OnServerStopping() { ShutDownDelay(); // halt any outstanding timer. lock (m_registrationLock) { if (m_registrationTimer != null) { m_registrationTimer.Dispose(); m_registrationTimer = null; } } // attempt graceful shutdown the server. try { if (m_maxRegistrationInterval > 0) { // unregister from Discovery Server m_registrationInfo.IsOnline = false; RegisterWithDiscoveryServer().Wait(); } lock (m_lock) { if (m_serverInternal != null) { m_serverInternal.SubscriptionManager.Shutdown(); m_serverInternal.SessionManager.Shutdown(); m_serverInternal.NodeManager.Shutdown(); } } } catch (Exception e) { ServerError = new ServiceResult(e); } finally { // ensure that everything is cleaned up. if (m_serverInternal != null) { Utils.SilentDispose(m_serverInternal); m_serverInternal = null; } } } /// /// Implements the server shutdown delay if session are connected. /// protected void ShutDownDelay() { try { // check for connected clients. IList currentessions = this.ServerInternal.SessionManager.GetSessions(); if (currentessions.Count > 0) { // provide some time for the connected clients to detect the shutdown state. ServerInternal.Status.Value.ShutdownReason = new LocalizedText("en-US", "Application closed."); ServerInternal.Status.Variable.ShutdownReason.Value = new LocalizedText("en-US", "Application closed."); ServerInternal.Status.Value.State = ServerState.Shutdown; ServerInternal.Status.Variable.State.Value = ServerState.Shutdown; ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true); for (int timeTillShutdown = Configuration.ServerConfiguration.ShutdownDelay; timeTillShutdown > 0; timeTillShutdown--) { ServerInternal.Status.Value.SecondsTillShutdown = (uint)timeTillShutdown; ServerInternal.Status.Variable.SecondsTillShutdown.Value = (uint)timeTillShutdown; ServerInternal.Status.Variable.ClearChangeMasks(ServerInternal.DefaultSystemContext, true); // exit if all client connections are closed. if (ServerInternal.SessionManager.GetSessions().Count == 0) { break; } Thread.Sleep(1000); } } } catch { // ignore error during shutdown procedure. } } /// /// Creates the request manager for the server. /// /// The server. /// The configuration. /// /// Returns an object that manages requests from within the server, return type is . /// protected virtual RequestManager CreateRequestManager(IServerInternal server, ApplicationConfiguration configuration) { return new RequestManager(server); } /// /// Creates the aggregate manager used by the server. /// /// The server. /// The application configuration. /// The manager. protected virtual AggregateManager CreateAggregateManager(IServerInternal server, ApplicationConfiguration configuration) { AggregateManager manager = new AggregateManager(server); manager.RegisterFactory(ObjectIds.AggregateFunction_Interpolative, BrowseNames.AggregateFunction_Interpolative, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Average, BrowseNames.AggregateFunction_Average, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_TimeAverage, BrowseNames.AggregateFunction_TimeAverage, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_TimeAverage2, BrowseNames.AggregateFunction_TimeAverage2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Total, BrowseNames.AggregateFunction_Total, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Total2, BrowseNames.AggregateFunction_Total2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Minimum, BrowseNames.AggregateFunction_Minimum, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Maximum, BrowseNames.AggregateFunction_Maximum, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_MinimumActualTime, BrowseNames.AggregateFunction_MinimumActualTime, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_MaximumActualTime, BrowseNames.AggregateFunction_MaximumActualTime, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Range, BrowseNames.AggregateFunction_Range, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Minimum2, BrowseNames.AggregateFunction_Minimum2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Maximum2, BrowseNames.AggregateFunction_Maximum2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_MinimumActualTime2, BrowseNames.AggregateFunction_MinimumActualTime2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_MaximumActualTime2, BrowseNames.AggregateFunction_MaximumActualTime2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Range2, BrowseNames.AggregateFunction_Range2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Count, BrowseNames.AggregateFunction_Count, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_AnnotationCount, BrowseNames.AggregateFunction_AnnotationCount, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_DurationInStateZero, BrowseNames.AggregateFunction_DurationInStateZero, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_DurationInStateNonZero, BrowseNames.AggregateFunction_DurationInStateNonZero, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_NumberOfTransitions, BrowseNames.AggregateFunction_NumberOfTransitions, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Start, BrowseNames.AggregateFunction_Start, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_End, BrowseNames.AggregateFunction_End, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_Delta, BrowseNames.AggregateFunction_Delta, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_StartBound, BrowseNames.AggregateFunction_StartBound, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_EndBound, BrowseNames.AggregateFunction_EndBound, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_DeltaBounds, BrowseNames.AggregateFunction_DeltaBounds, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_DurationGood, BrowseNames.AggregateFunction_DurationGood, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_DurationBad, BrowseNames.AggregateFunction_DurationBad, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_PercentGood, BrowseNames.AggregateFunction_PercentGood, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_PercentBad, BrowseNames.AggregateFunction_PercentBad, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_WorstQuality, BrowseNames.AggregateFunction_WorstQuality, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_WorstQuality2, BrowseNames.AggregateFunction_WorstQuality2, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_StandardDeviationPopulation, BrowseNames.AggregateFunction_StandardDeviationPopulation, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_VariancePopulation, BrowseNames.AggregateFunction_VariancePopulation, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_StandardDeviationSample, BrowseNames.AggregateFunction_StandardDeviationSample, Aggregators.CreateStandardCalculator); manager.RegisterFactory(ObjectIds.AggregateFunction_VarianceSample, BrowseNames.AggregateFunction_VarianceSample, Aggregators.CreateStandardCalculator); return manager; } /// /// Creates the resource manager for the server. /// /// The server. /// The configuration. /// Returns an object that manages access to localized resources, the return type is . protected virtual ResourceManager CreateResourceManager(IServerInternal server, ApplicationConfiguration configuration) { ResourceManager resourceManager = new ResourceManager(server, configuration); // load default text for all status codes. resourceManager.LoadDefaultText(); return resourceManager; } /// /// Creates the master node manager for the server. /// /// The server. /// The configuration. /// Returns the master node manager for the server, the return type is . protected virtual MasterNodeManager CreateMasterNodeManager(IServerInternal server, ApplicationConfiguration configuration) { return new MasterNodeManager(server, configuration, null); } /// /// Creates the event manager for the server. /// /// The server. /// The configuration. /// Returns an object that manages all events raised within the server, the return type is . protected virtual EventManager CreateEventManager(IServerInternal server, ApplicationConfiguration configuration) { return new EventManager(server, (uint)configuration.ServerConfiguration.MaxEventQueueSize); } /// /// Creates the session manager for the server. /// /// The server. /// The configuration. /// Returns a generic session manager object for a server, the return type is . protected virtual SessionManager CreateSessionManager(IServerInternal server, ApplicationConfiguration configuration) { return new SessionManager(server, configuration); } /// /// Creates the session manager for the server. /// /// The server. /// The configuration. /// Returns a generic session manager object for a server, the return type is . protected virtual SubscriptionManager CreateSubscriptionManager(IServerInternal server, ApplicationConfiguration configuration) { return new SubscriptionManager(server, configuration); } /// /// Called after the node managers have been started. /// /// The server. protected virtual void OnNodeManagerStarted(IServerInternal server) { // may be overridden by the subclass. } /// /// Called after the server has been started. /// /// The server. protected virtual void OnServerStarted(IServerInternal server) { // may be overridden by the subclass. } #endregion #region Private Fields private readonly object m_lock = new object(); private readonly object m_registrationLock = new object(); private ServerInternalData m_serverInternal; private ConfigurationWatcher m_configurationWatcher; private ConfiguredEndpointCollection m_registrationEndpoints; private RegisteredServer m_registrationInfo; private Timer m_registrationTimer; private int m_minRegistrationInterval; private int m_maxRegistrationInterval; private int m_lastRegistrationInterval; private int m_minNonceLength; private bool m_useRegisterServer2; #endregion } }