Skip to content

Commit 1bb9edf

Browse files
Add UserCredentialsConnectionFactoryAdapter
1 parent ec3e168 commit 1bb9edf

File tree

3 files changed

+377
-0
lines changed

3 files changed

+377
-0
lines changed
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#region License
2+
3+
/*
4+
* Copyright 2002-2010 the original author or authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
#endregion
19+
20+
using System;
21+
using System.Threading;
22+
using Apache.NMS;
23+
24+
namespace Spring.Messaging.Nms.Connections
25+
{
26+
/// <summary>
27+
/// An adapter for a target JMS {@link javax.jms.ConnectionFactory}, applying the
28+
/// given user credentials to every standard <code>CreateConnection()</code> call,
29+
/// that is, implicitly invoking <code>CreateConnection(username, password)</code>
30+
/// on the target.All other methods simply delegate to the corresponding methods
31+
/// of the target ConnectionFactory.
32+
/// </summary>
33+
/// <remarks>
34+
/// Can be used to proxy a target NMS ConnectionFactory that does not have user
35+
/// credentials configured. Client code can work with the ConnectionFactory without
36+
/// passing in username and password on every <code>CreateConnection()</code> call.
37+
/// If the "Username" is empty, this proxy will simply delegate to the standard
38+
/// <code>CreateConnection()</code> method of the target ConnectionFactory.
39+
/// This can be used to keep a UserCredentialsConnectionFactoryAdapter
40+
/// definition just for the<i> option</i> of implicitly passing in user credentials
41+
/// if the particular target ConnectionFactory requires it.
42+
/// </remarks>
43+
public class UserCredentialsConnectionFactoryAdapter:IConnectionFactory
44+
{
45+
private readonly IConnectionFactory _wrappedConnectionFactory;
46+
47+
private readonly ThreadLocal<NmsUserCredentials> threadLocalCredentials = new ThreadLocal<NmsUserCredentials>();
48+
49+
public UserCredentialsConnectionFactoryAdapter(IConnectionFactory wrappedConnectionFactory)
50+
{
51+
this._wrappedConnectionFactory = wrappedConnectionFactory;
52+
}
53+
54+
55+
/// <summary>
56+
/// Set user credentials for this proxy and the current thread.
57+
/// The given username and password will be applied to all subsequent
58+
/// <code>CreateConnection()</code> calls on this ConnectionFactory proxy.
59+
/// This will override any statically specified user credentials,
60+
/// that is, values of the "username" and "password" properties.
61+
/// </summary>
62+
public void SetCredentialsForCurrentThread(string userName, string password)
63+
{
64+
this.threadLocalCredentials.Value = new NmsUserCredentials(userName, password);
65+
}
66+
67+
/// <summary>
68+
/// Remove any user credentials for this proxy from the current thread.
69+
/// Statically specified user credentials apply again afterwards.
70+
/// </summary>
71+
public void RemoveCredentialsFromCurrentThread()
72+
{
73+
this.threadLocalCredentials.Value = null;
74+
}
75+
76+
77+
private string _userName;
78+
79+
/// <summary>
80+
/// Set the username that this adapter should use for retrieving Connections.
81+
/// </summary>
82+
public string UserName
83+
{
84+
get => _userName;
85+
set => _userName = string.IsNullOrWhiteSpace(value) ? null : value;
86+
}
87+
88+
private string _password;
89+
90+
/// <summary>
91+
/// Set the password that this adapter should use for retrieving Connections.
92+
/// </summary>
93+
public string Password
94+
{
95+
get => _password;
96+
set => _password = string.IsNullOrEmpty(value) ? null : value;
97+
}
98+
99+
public IConnection CreateConnection()
100+
{
101+
var credentialsForCurrentThread = this.threadLocalCredentials.Value;
102+
if (credentialsForCurrentThread != null)
103+
{
104+
return CreateConnectionForSpecificCredentials(credentialsForCurrentThread.UserName, credentialsForCurrentThread.Password);
105+
}
106+
107+
return CreateConnectionForSpecificCredentials(UserName, Password);
108+
}
109+
110+
private IConnection CreateConnectionForSpecificCredentials(string userName, string password)
111+
{
112+
if (string.IsNullOrWhiteSpace(userName) == false)
113+
{
114+
return CreateConnection(userName, password);
115+
}
116+
117+
return _wrappedConnectionFactory.CreateConnection();
118+
}
119+
120+
public IConnection CreateConnection(string userName, string password)
121+
{
122+
return _wrappedConnectionFactory.CreateConnection(userName, password);
123+
}
124+
125+
public Uri BrokerUri
126+
{
127+
get => _wrappedConnectionFactory.BrokerUri;
128+
set => _wrappedConnectionFactory.BrokerUri = value;
129+
}
130+
131+
public IRedeliveryPolicy RedeliveryPolicy
132+
{
133+
get => _wrappedConnectionFactory.RedeliveryPolicy;
134+
set => _wrappedConnectionFactory.RedeliveryPolicy = value;
135+
}
136+
137+
public ConsumerTransformerDelegate ConsumerTransformer
138+
{
139+
get => _wrappedConnectionFactory.ConsumerTransformer;
140+
set => _wrappedConnectionFactory.ConsumerTransformer = value;
141+
}
142+
143+
public ProducerTransformerDelegate ProducerTransformer
144+
{
145+
get => _wrappedConnectionFactory.ProducerTransformer;
146+
set => _wrappedConnectionFactory.ProducerTransformer = value;
147+
}
148+
149+
private class NmsUserCredentials
150+
{
151+
public string UserName { get; }
152+
public string Password { get; }
153+
154+
public NmsUserCredentials(string userName, string password)
155+
{
156+
UserName = userName;
157+
Password = password;
158+
}
159+
}
160+
}
161+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Threading;
4+
5+
namespace Spring.Messaging.Nms.Connections
6+
{
7+
public class MultithreadingTestHelper
8+
{
9+
[DebuggerStepThrough]
10+
public static TestThreadHandler RunOnSeparateThread(Action action)
11+
{
12+
Exception exception = null;
13+
var thread1 = new Thread(() =>
14+
{
15+
try
16+
{
17+
action();
18+
}
19+
catch (Exception e)
20+
{
21+
exception = e;
22+
}
23+
});
24+
thread1.Start();
25+
26+
return new TestThreadHandler(() =>
27+
{
28+
thread1.Join();
29+
if (exception != null)
30+
{
31+
throw exception;
32+
}
33+
});
34+
}
35+
36+
public class TestThreadHandler
37+
{
38+
private readonly Action _waitAction;
39+
40+
public TestThreadHandler(Action waitAction)
41+
{
42+
_waitAction = waitAction;
43+
}
44+
45+
[DebuggerStepThrough]
46+
public void Wait()
47+
{
48+
_waitAction();
49+
}
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Text;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Apache.NMS;
7+
using FakeItEasy;
8+
using NUnit.Framework;
9+
10+
namespace Spring.Messaging.Nms.Connections
11+
{
12+
[TestFixture]
13+
public class UserCredentialsConnectionFactoryAdapterTests
14+
{
15+
16+
[Test]
17+
public void CreateConnectionWithoutCredentialsWhenTheyAreNotSet()
18+
{
19+
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
20+
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
21+
22+
connectionFactory.CreateConnection();
23+
24+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappenedOnceExactly();
25+
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappened(0, Times.Exactly);
26+
}
27+
28+
29+
[Test]
30+
public void CreateConnectionWithoutCredentialsWhenLoginIsNotSet()
31+
{
32+
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
33+
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
34+
connectionFactory.Password = "Secret";
35+
connectionFactory.CreateConnection();
36+
37+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappenedOnceExactly();
38+
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappened(0, Times.Exactly);
39+
}
40+
41+
[Test]
42+
public void CreateConnectionWithCredentialsWhenTheyAreSet()
43+
{
44+
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
45+
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
46+
connectionFactory.UserName = "SampleUser";
47+
connectionFactory.Password = "Secret";
48+
connectionFactory.CreateConnection();
49+
50+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
51+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappenedOnceExactly();
52+
}
53+
54+
55+
[Test]
56+
public void SetConnectionCredentialsOnlyForCurrentThread()
57+
{
58+
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
59+
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
60+
61+
// Call CreateConnection on thread that also called SetCredentialsForCurrentThread
62+
MultithreadingTestHelper.RunOnSeparateThread(() =>
63+
{
64+
connectionFactory.SetCredentialsForCurrentThread("SampleUser", "Password");
65+
connectionFactory.CreateConnection();
66+
67+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
68+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Password")).MustHaveHappenedOnceExactly();
69+
}).Wait();
70+
71+
// Call CreateConnection on thread that didn't callSetCredentialsForCurrentThread
72+
MultithreadingTestHelper.RunOnSeparateThread(() =>
73+
{
74+
connectionFactory.CreateConnection();
75+
76+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappenedOnceExactly();
77+
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappenedOnceExactly();
78+
}).Wait();
79+
80+
81+
// Call CreateConnection on the main thread
82+
connectionFactory.CreateConnection();
83+
84+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(2, Times.Exactly);
85+
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappenedOnceExactly();
86+
}
87+
88+
[Test]
89+
public void InheritCredentialsIfTheyAreNotSetForSpecificThread()
90+
{
91+
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
92+
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
93+
94+
95+
connectionFactory.UserName = "SampleUser";
96+
connectionFactory.Password = "Secret";
97+
98+
// Call CreateConnection on thread that also called SetCredentialsForCurrentThread
99+
MultithreadingTestHelper.RunOnSeparateThread(() =>
100+
{
101+
connectionFactory.CreateConnection();
102+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
103+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappenedOnceExactly();
104+
105+
106+
connectionFactory.SetCredentialsForCurrentThread("ThreadSampleUser", "ThreadPassword");
107+
connectionFactory.CreateConnection();
108+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
109+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("ThreadSampleUser", "ThreadPassword")).MustHaveHappenedOnceExactly();
110+
111+
connectionFactory.RemoveCredentialsFromCurrentThread();
112+
connectionFactory.CreateConnection();
113+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
114+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappened(2, Times.Exactly);
115+
}).Wait();
116+
117+
connectionFactory.CreateConnection();
118+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
119+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappened(3, Times.Exactly);
120+
}
121+
122+
[Test]
123+
public void SetDifferentCredentialsOnDifferentThreads()
124+
{
125+
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
126+
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
127+
128+
connectionFactory.UserName = "SampleUser";
129+
connectionFactory.Password = "Secret";
130+
131+
var barrier = new Barrier(2);
132+
133+
// Call CreateConnection on thread that also called SetCredentialsForCurrentThread
134+
var thread1 = MultithreadingTestHelper.RunOnSeparateThread(() =>
135+
{
136+
connectionFactory.SetCredentialsForCurrentThread("Thread1SampleUser", "Password");
137+
barrier.SignalAndWait();
138+
139+
connectionFactory.CreateConnection();
140+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
141+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("Thread1SampleUser", "Password")).MustHaveHappenedOnceExactly();
142+
});
143+
144+
// Call CreateConnection on thread that didn't callSetCredentialsForCurrentThread
145+
var thread2 = MultithreadingTestHelper.RunOnSeparateThread(() =>
146+
{
147+
connectionFactory.SetCredentialsForCurrentThread("Thread2SampleUser", "Password");
148+
barrier.SignalAndWait();
149+
150+
connectionFactory.CreateConnection();
151+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
152+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("Thread2SampleUser", "Password")).MustHaveHappenedOnceExactly();
153+
});
154+
155+
156+
connectionFactory.CreateConnection();
157+
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
158+
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappenedOnceExactly();
159+
160+
thread1.Wait();
161+
thread2.Wait();
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)