Skip to content

Commit dc85d52

Browse files
Copilotromanett
andauthored
Add support for private encodeable factory in server initialization (#3366)
* Initial plan * Add support for private encodeable factory in server initialization - Add new ServiceMessageContext constructor that accepts a custom IEncodeableFactory - Add CreateMessageContext(IEncodeableFactory) overload to ApplicationConfiguration - Mark CreateMessageContext(bool) as obsolete since the clonedFactory parameter was never used - Add PrivateEncodeableFactory property to ServerBase for specifying a private factory - Update all internal calls to use the new parameterless CreateMessageContext() Co-authored-by: romanett <[email protected]> * Add unit tests for ServiceMessageContext private factory support Co-authored-by: romanett <[email protected]> * Improve documentation for ServiceMessageContext constructors Co-authored-by: romanett <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: romanett <[email protected]>
1 parent 0f196ba commit dc85d52

File tree

8 files changed

+203
-11
lines changed

8 files changed

+203
-11
lines changed

Libraries/Opc.Ua.Client/Session/DefaultSessionFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public virtual async Task<ITransportChannel> CreateChannelAsync(
232232
}
233233

234234
// create message context.
235-
ServiceMessageContext messageContext = configuration.CreateMessageContext(true);
235+
ServiceMessageContext messageContext = configuration.CreateMessageContext();
236236

237237
// update endpoint description using the discovery endpoint.
238238
if (endpoint.UpdateBeforeConnect && connection == null)

Libraries/Opc.Ua.Client/Session/Session.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public Session(
110110
channel,
111111
configuration,
112112
endpoint,
113-
channel.MessageContext ?? configuration.CreateMessageContext(true))
113+
channel.MessageContext ?? configuration.CreateMessageContext())
114114
{
115115
m_instanceCertificate = clientCertificate;
116116
m_instanceCertificateChain = clientCertificateChain;
@@ -129,7 +129,7 @@ public Session(ITransportChannel channel, Session template, bool copyEventHandle
129129
channel,
130130
template.m_configuration,
131131
template.ConfiguredEndpoint,
132-
channel.MessageContext ?? template.m_configuration.CreateMessageContext(true))
132+
channel.MessageContext ?? template.m_configuration.CreateMessageContext())
133133
{
134134
m_instanceCertificate = template.m_instanceCertificate;
135135
m_instanceCertificateChain = template.m_instanceCertificateChain;

Libraries/Opc.Ua.Gds.Client.Common/GlobalDiscoveryServerClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public GlobalDiscoveryServerClient(
5757
DiagnosticsMasks diagnosticsMasks = DiagnosticsMasks.None)
5858
{
5959
Configuration = configuration;
60-
MessageContext = configuration.CreateMessageContext(true);
60+
MessageContext = configuration.CreateMessageContext();
6161
m_logger = MessageContext.Telemetry.CreateLogger<GlobalDiscoveryServerClient>();
6262
m_sessionFactory = sessionFactory ??
6363
new DefaultSessionFactory(MessageContext.Telemetry)

Libraries/Opc.Ua.Gds.Client.Common/ServerPushConfigurationClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public ServerPushConfigurationClient(
5555
DiagnosticsMasks diagnosticsMasks = DiagnosticsMasks.None)
5656
{
5757
Configuration = configuration;
58-
MessageContext = configuration.CreateMessageContext(true);
58+
MessageContext = configuration.CreateMessageContext();
5959
m_logger = MessageContext.Telemetry.CreateLogger<ServerPushConfigurationClient>();
6060
m_sessionFactory = sessionFactory ??
6161
new DefaultSessionFactory(MessageContext.Telemetry)

Stack/Opc.Ua.Core/Stack/Configuration/ApplicationConfiguration.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,31 @@ public IList<string> GetServerDomainNames()
172172
/// <summary>
173173
/// Creates the message context from the configuration.
174174
/// </summary>
175+
/// <param name="clonedFactory">This parameter is obsolete and ignored. A new factory instance is always created.</param>
175176
/// <returns>A new instance of a ServiceMessageContext object.</returns>
176-
public ServiceMessageContext CreateMessageContext(bool clonedFactory = false)
177+
[Obsolete("Use CreateMessageContext() without parameters or CreateMessageContext(IEncodeableFactory) instead.")]
178+
public ServiceMessageContext CreateMessageContext(bool clonedFactory)
177179
{
178-
var messageContext = new ServiceMessageContext(m_telemetry);
180+
return CreateMessageContext((IEncodeableFactory)null);
181+
}
182+
183+
/// <summary>
184+
/// Creates the message context from the configuration.
185+
/// </summary>
186+
/// <returns>A new instance of a ServiceMessageContext object with a new encodeable factory.</returns>
187+
public ServiceMessageContext CreateMessageContext()
188+
{
189+
return CreateMessageContext((IEncodeableFactory)null);
190+
}
191+
192+
/// <summary>
193+
/// Creates the message context from the configuration with a private encodeable factory.
194+
/// </summary>
195+
/// <param name="factory">The private encodeable factory to use. If null, a new factory will be created.</param>
196+
/// <returns>A new instance of a ServiceMessageContext object.</returns>
197+
public ServiceMessageContext CreateMessageContext(IEncodeableFactory factory)
198+
{
199+
var messageContext = new ServiceMessageContext(m_telemetry, factory);
179200

180201
if (TransportQuotas != null)
181202
{

Stack/Opc.Ua.Core/Stack/Server/ServerBase.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,22 @@ protected class BaseAddress
701701
/// <value>The provider for the X.509 certificates.</value>
702702
public CertificateTypesProvider InstanceCertificateTypesProvider { get; private set; }
703703

704+
/// <summary>
705+
/// Gets or sets the private encodeable factory to use for this server instance.
706+
/// </summary>
707+
/// <remarks>
708+
/// <para>
709+
/// Set this property before calling <see cref="StartAsync(ApplicationConfiguration, CancellationToken, Uri[])"/>
710+
/// or <see cref="StartAsync(ApplicationConfiguration, CancellationToken)"/> to use a private factory
711+
/// that is isolated from other servers and clients in the same process.
712+
/// </para>
713+
/// <para>
714+
/// If this property is null (the default), a new factory will be created
715+
/// during server startup via <see cref="EncodeableFactory.Create()"/>.
716+
/// </para>
717+
/// </remarks>
718+
public IEncodeableFactory PrivateEncodeableFactory { get; set; }
719+
704720
/// <summary>
705721
/// The non-configurable properties for the server.
706722
/// </summary>
@@ -1363,8 +1379,8 @@ protected virtual void OnServerStarting(ApplicationConfiguration configuration)
13631379
{
13641380
// use the message context from the configuration to ensure the channels are
13651381
// using the same one. This also sets the telemetry context for the server
1366-
// from configuration.
1367-
ServiceMessageContext messageContext = configuration.CreateMessageContext(true);
1382+
// from configuration. If a private factory was specified, use it.
1383+
ServiceMessageContext messageContext = configuration.CreateMessageContext(PrivateEncodeableFactory);
13681384
messageContext.NamespaceUris = new NamespaceTable();
13691385
MessageContext = messageContext;
13701386

Stack/Opc.Ua.Types/Utils/ServiceMessageContext.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,25 @@ public ServiceMessageContext()
4646
}
4747

4848
/// <summary>
49-
/// Initializes the object with default values.
49+
/// Initializes the object with default values and a new encodeable factory.
5050
/// </summary>
51+
/// <param name="telemetry">The telemetry context to use to create observability instruments.</param>
52+
/// <remarks>
53+
/// A new <see cref="IEncodeableFactory"/> instance is created via <see cref="EncodeableFactory.Create()"/>.
54+
/// To use a private factory that is isolated from other instances in the same process,
55+
/// use <see cref="ServiceMessageContext(ITelemetryContext, IEncodeableFactory)"/> instead.
56+
/// </remarks>
5157
public ServiceMessageContext(ITelemetryContext telemetry)
58+
: this(telemetry, null)
59+
{
60+
}
61+
62+
/// <summary>
63+
/// Initializes the object with default values and an optional private encodeable factory.
64+
/// </summary>
65+
/// <param name="telemetry">The telemetry context to use to create observability instruments.</param>
66+
/// <param name="factory">The private encodeable factory to use. If null, a new factory will be created.</param>
67+
public ServiceMessageContext(ITelemetryContext telemetry, IEncodeableFactory factory)
5268
{
5369
Telemetry = telemetry;
5470
MaxStringLength = DefaultEncodingLimits.MaxStringLength;
@@ -59,7 +75,7 @@ public ServiceMessageContext(ITelemetryContext telemetry)
5975
MaxDecoderRecoveries = DefaultEncodingLimits.MaxDecoderRecoveries;
6076
m_namespaceUris = new NamespaceTable();
6177
m_serverUris = new StringTable();
62-
m_factory = EncodeableFactory.Create();
78+
m_factory = factory ?? EncodeableFactory.Create();
6379
}
6480

6581
/// <summary>
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* ========================================================================
2+
* Copyright (c) 2005-2024 The OPC Foundation, Inc. All rights reserved.
3+
*
4+
* OPC Foundation MIT License 1.00
5+
*
6+
* Permission is hereby granted, free of charge, to any person
7+
* obtaining a copy of this software and associated documentation
8+
* files (the "Software"), to deal in the Software without
9+
* restriction, including without limitation the rights to use,
10+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the
12+
* Software is furnished to do so, subject to the following
13+
* conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be
16+
* included in all copies or substantial portions of the Software.
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24+
* OTHER DEALINGS IN THE SOFTWARE.
25+
*
26+
* The complete license agreement can be found here:
27+
* http://opcfoundation.org/License/MIT/1.00
28+
* ======================================================================*/
29+
30+
using NUnit.Framework;
31+
using Assert = NUnit.Framework.Legacy.ClassicAssert;
32+
33+
namespace Opc.Ua.Core.Tests.Types.ServiceMessageContextTests
34+
{
35+
/// <summary>
36+
/// Tests for <see cref="ServiceMessageContext"/>.
37+
/// </summary>
38+
[TestFixture]
39+
[Category("ServiceMessageContext")]
40+
[Parallelizable]
41+
public class ServiceMessageContextTests
42+
{
43+
/// <summary>
44+
/// Test that creating a ServiceMessageContext with a null factory creates a new factory.
45+
/// </summary>
46+
[Test]
47+
public void ConstructorWithNullFactoryCreatesNewFactory()
48+
{
49+
// Arrange & Act
50+
var context = new ServiceMessageContext((ITelemetryContext)null, (IEncodeableFactory)null);
51+
52+
// Assert
53+
Assert.IsNotNull(context.Factory);
54+
}
55+
56+
/// <summary>
57+
/// Test that creating a ServiceMessageContext with a custom factory uses that factory.
58+
/// </summary>
59+
[Test]
60+
public void ConstructorWithCustomFactoryUsesProvidedFactory()
61+
{
62+
// Arrange
63+
IEncodeableFactory customFactory = EncodeableFactory.Create();
64+
65+
// Act
66+
var context = new ServiceMessageContext(null, customFactory);
67+
68+
// Assert
69+
Assert.AreSame(customFactory, context.Factory);
70+
}
71+
72+
/// <summary>
73+
/// Test that two ServiceMessageContexts with different factories have separate factories.
74+
/// </summary>
75+
[Test]
76+
public void TwoContextsWithDifferentFactoriesAreSeparate()
77+
{
78+
// Arrange
79+
IEncodeableFactory factory1 = EncodeableFactory.Create();
80+
IEncodeableFactory factory2 = EncodeableFactory.Create();
81+
82+
var context1 = new ServiceMessageContext(null, factory1);
83+
var context2 = new ServiceMessageContext(null, factory2);
84+
85+
// Assert - the contexts should reference different factory instances
86+
Assert.AreSame(factory1, context1.Factory);
87+
Assert.AreSame(factory2, context2.Factory);
88+
Assert.AreNotSame(context1.Factory, context2.Factory);
89+
}
90+
91+
/// <summary>
92+
/// Test that the default constructor creates a new factory.
93+
/// </summary>
94+
[Test]
95+
public void DefaultConstructorCreatesNewFactory()
96+
{
97+
// Arrange & Act
98+
var context = new ServiceMessageContext(null);
99+
100+
// Assert
101+
Assert.IsNotNull(context.Factory);
102+
}
103+
104+
/// <summary>
105+
/// Test that setting the Factory property works correctly.
106+
/// </summary>
107+
[Test]
108+
public void SettingFactoryPropertyWorks()
109+
{
110+
// Arrange
111+
var context = new ServiceMessageContext(null);
112+
IEncodeableFactory customFactory = EncodeableFactory.Create();
113+
114+
// Act
115+
context.Factory = customFactory;
116+
117+
// Assert
118+
Assert.AreSame(customFactory, context.Factory);
119+
}
120+
121+
/// <summary>
122+
/// Test that setting the Factory property to null creates a new factory.
123+
/// </summary>
124+
[Test]
125+
public void SettingFactoryPropertyToNullCreatesNewFactory()
126+
{
127+
// Arrange
128+
var context = new ServiceMessageContext(null);
129+
var originalFactory = context.Factory;
130+
131+
// Act
132+
context.Factory = null;
133+
134+
// Assert
135+
Assert.IsNotNull(context.Factory);
136+
Assert.AreNotSame(originalFactory, context.Factory);
137+
}
138+
}
139+
}

0 commit comments

Comments
 (0)