Merge pull request #188 from cezarypiatekGC/feature/add_credential_connection_factory
Add UserCredentialsConnectionFactoryAdapter
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Apache.NMS;
|
||||
|
||||
namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
/// <summary>
|
||||
/// An adapter for a target JMS {@link javax.jms.ConnectionFactory}, applying the
|
||||
/// given user credentials to every standard <code>CreateConnection()</code> call,
|
||||
/// that is, implicitly invoking <code>CreateConnection(username, password)</code>
|
||||
/// on the target.All other methods simply delegate to the corresponding methods
|
||||
/// of the target ConnectionFactory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be used to proxy a target NMS ConnectionFactory that does not have user
|
||||
/// credentials configured. Client code can work with the ConnectionFactory without
|
||||
/// passing in username and password on every <code>CreateConnection()</code> call.
|
||||
/// If the "Username" is empty, this proxy will simply delegate to the standard
|
||||
/// <code>CreateConnection()</code> method of the target ConnectionFactory.
|
||||
/// This can be used to keep a UserCredentialsConnectionFactoryAdapter
|
||||
/// definition just for the<i> option</i> of implicitly passing in user credentials
|
||||
/// if the particular target ConnectionFactory requires it.
|
||||
/// </remarks>
|
||||
public class UserCredentialsConnectionFactoryAdapter:IConnectionFactory
|
||||
{
|
||||
private readonly IConnectionFactory _wrappedConnectionFactory;
|
||||
|
||||
private readonly ThreadLocal<NmsUserCredentials> threadLocalCredentials = new ThreadLocal<NmsUserCredentials>();
|
||||
|
||||
public UserCredentialsConnectionFactoryAdapter(IConnectionFactory wrappedConnectionFactory)
|
||||
{
|
||||
this._wrappedConnectionFactory = wrappedConnectionFactory;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set user credentials for this proxy and the current thread.
|
||||
/// The given username and password will be applied to all subsequent
|
||||
/// <code>CreateConnection()</code> calls on this ConnectionFactory proxy.
|
||||
/// This will override any statically specified user credentials,
|
||||
/// that is, values of the "username" and "password" properties.
|
||||
/// </summary>
|
||||
public void SetCredentialsForCurrentThread(string userName, string password)
|
||||
{
|
||||
this.threadLocalCredentials.Value = new NmsUserCredentials(userName, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any user credentials for this proxy from the current thread.
|
||||
/// Statically specified user credentials apply again afterwards.
|
||||
/// </summary>
|
||||
public void RemoveCredentialsFromCurrentThread()
|
||||
{
|
||||
this.threadLocalCredentials.Value = null;
|
||||
}
|
||||
|
||||
|
||||
private string _userName;
|
||||
|
||||
/// <summary>
|
||||
/// Set the username that this adapter should use for retrieving Connections.
|
||||
/// </summary>
|
||||
public string UserName
|
||||
{
|
||||
get => _userName;
|
||||
set => _userName = string.IsNullOrWhiteSpace(value) ? null : value;
|
||||
}
|
||||
|
||||
private string _password;
|
||||
|
||||
/// <summary>
|
||||
/// Set the password that this adapter should use for retrieving Connections.
|
||||
/// </summary>
|
||||
public string Password
|
||||
{
|
||||
get => _password;
|
||||
set => _password = string.IsNullOrEmpty(value) ? null : value;
|
||||
}
|
||||
|
||||
public IConnection CreateConnection()
|
||||
{
|
||||
var credentialsForCurrentThread = this.threadLocalCredentials.Value;
|
||||
if (credentialsForCurrentThread != null)
|
||||
{
|
||||
return CreateConnectionForSpecificCredentials(credentialsForCurrentThread.UserName, credentialsForCurrentThread.Password);
|
||||
}
|
||||
|
||||
return CreateConnectionForSpecificCredentials(UserName, Password);
|
||||
}
|
||||
|
||||
private IConnection CreateConnectionForSpecificCredentials(string userName, string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userName) == false)
|
||||
{
|
||||
return CreateConnection(userName, password);
|
||||
}
|
||||
|
||||
return _wrappedConnectionFactory.CreateConnection();
|
||||
}
|
||||
|
||||
public IConnection CreateConnection(string userName, string password)
|
||||
{
|
||||
return _wrappedConnectionFactory.CreateConnection(userName, password);
|
||||
}
|
||||
|
||||
public Uri BrokerUri
|
||||
{
|
||||
get => _wrappedConnectionFactory.BrokerUri;
|
||||
set => _wrappedConnectionFactory.BrokerUri = value;
|
||||
}
|
||||
|
||||
public IRedeliveryPolicy RedeliveryPolicy
|
||||
{
|
||||
get => _wrappedConnectionFactory.RedeliveryPolicy;
|
||||
set => _wrappedConnectionFactory.RedeliveryPolicy = value;
|
||||
}
|
||||
|
||||
public ConsumerTransformerDelegate ConsumerTransformer
|
||||
{
|
||||
get => _wrappedConnectionFactory.ConsumerTransformer;
|
||||
set => _wrappedConnectionFactory.ConsumerTransformer = value;
|
||||
}
|
||||
|
||||
public ProducerTransformerDelegate ProducerTransformer
|
||||
{
|
||||
get => _wrappedConnectionFactory.ProducerTransformer;
|
||||
set => _wrappedConnectionFactory.ProducerTransformer = value;
|
||||
}
|
||||
|
||||
private class NmsUserCredentials
|
||||
{
|
||||
public string UserName { get; }
|
||||
public string Password { get; }
|
||||
|
||||
public NmsUserCredentials(string userName, string password)
|
||||
{
|
||||
UserName = userName;
|
||||
Password = password;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
public class MultithreadingTestHelper
|
||||
{
|
||||
[DebuggerStepThrough]
|
||||
public static TestThreadHandler RunOnSeparateThread(Action action)
|
||||
{
|
||||
Exception exception = null;
|
||||
var thread1 = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
});
|
||||
thread1.Start();
|
||||
|
||||
return new TestThreadHandler(() =>
|
||||
{
|
||||
thread1.Join();
|
||||
if (exception != null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class TestThreadHandler
|
||||
{
|
||||
private readonly Action _waitAction;
|
||||
|
||||
public TestThreadHandler(Action waitAction)
|
||||
{
|
||||
_waitAction = waitAction;
|
||||
}
|
||||
|
||||
[DebuggerStepThrough]
|
||||
public void Wait()
|
||||
{
|
||||
_waitAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Apache.NMS;
|
||||
using FakeItEasy;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Spring.Messaging.Nms.Connections
|
||||
{
|
||||
[TestFixture]
|
||||
public class UserCredentialsConnectionFactoryAdapterTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void CreateConnectionWithoutCredentialsWhenTheyAreNotSet()
|
||||
{
|
||||
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
|
||||
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
|
||||
|
||||
connectionFactory.CreateConnection();
|
||||
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappenedOnceExactly();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappened(0, Times.Exactly);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void CreateConnectionWithoutCredentialsWhenLoginIsNotSet()
|
||||
{
|
||||
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
|
||||
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
|
||||
connectionFactory.Password = "Secret";
|
||||
connectionFactory.CreateConnection();
|
||||
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappenedOnceExactly();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappened(0, Times.Exactly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateConnectionWithCredentialsWhenTheyAreSet()
|
||||
{
|
||||
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
|
||||
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
|
||||
connectionFactory.UserName = "SampleUser";
|
||||
connectionFactory.Password = "Secret";
|
||||
connectionFactory.CreateConnection();
|
||||
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappenedOnceExactly();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void SetConnectionCredentialsOnlyForCurrentThread()
|
||||
{
|
||||
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
|
||||
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
|
||||
|
||||
// Call CreateConnection on thread that also called SetCredentialsForCurrentThread
|
||||
MultithreadingTestHelper.RunOnSeparateThread(() =>
|
||||
{
|
||||
connectionFactory.SetCredentialsForCurrentThread("SampleUser", "Password");
|
||||
connectionFactory.CreateConnection();
|
||||
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Password")).MustHaveHappenedOnceExactly();
|
||||
}).Wait();
|
||||
|
||||
// Call CreateConnection on thread that didn't callSetCredentialsForCurrentThread
|
||||
MultithreadingTestHelper.RunOnSeparateThread(() =>
|
||||
{
|
||||
connectionFactory.CreateConnection();
|
||||
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappenedOnceExactly();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappenedOnceExactly();
|
||||
}).Wait();
|
||||
|
||||
|
||||
// Call CreateConnection on the main thread
|
||||
connectionFactory.CreateConnection();
|
||||
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(2, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection(A<string>._, A<string>._)).MustHaveHappenedOnceExactly();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InheritCredentialsIfTheyAreNotSetForSpecificThread()
|
||||
{
|
||||
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
|
||||
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
|
||||
|
||||
|
||||
connectionFactory.UserName = "SampleUser";
|
||||
connectionFactory.Password = "Secret";
|
||||
|
||||
// Call CreateConnection on thread that also called SetCredentialsForCurrentThread
|
||||
MultithreadingTestHelper.RunOnSeparateThread(() =>
|
||||
{
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappenedOnceExactly();
|
||||
|
||||
|
||||
connectionFactory.SetCredentialsForCurrentThread("ThreadSampleUser", "ThreadPassword");
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("ThreadSampleUser", "ThreadPassword")).MustHaveHappenedOnceExactly();
|
||||
|
||||
connectionFactory.RemoveCredentialsFromCurrentThread();
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappened(2, Times.Exactly);
|
||||
}).Wait();
|
||||
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappened(3, Times.Exactly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetDifferentCredentialsOnDifferentThreads()
|
||||
{
|
||||
var underlyingConnectionFactory = A.Fake<IConnectionFactory>();
|
||||
var connectionFactory = new UserCredentialsConnectionFactoryAdapter(underlyingConnectionFactory);
|
||||
|
||||
connectionFactory.UserName = "SampleUser";
|
||||
connectionFactory.Password = "Secret";
|
||||
|
||||
var barrier = new Barrier(2);
|
||||
|
||||
// Call CreateConnection on thread that also called SetCredentialsForCurrentThread
|
||||
var thread1 = MultithreadingTestHelper.RunOnSeparateThread(() =>
|
||||
{
|
||||
connectionFactory.SetCredentialsForCurrentThread("Thread1SampleUser", "Password");
|
||||
barrier.SignalAndWait();
|
||||
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("Thread1SampleUser", "Password")).MustHaveHappenedOnceExactly();
|
||||
});
|
||||
|
||||
// Call CreateConnection on thread that didn't callSetCredentialsForCurrentThread
|
||||
var thread2 = MultithreadingTestHelper.RunOnSeparateThread(() =>
|
||||
{
|
||||
connectionFactory.SetCredentialsForCurrentThread("Thread2SampleUser", "Password");
|
||||
barrier.SignalAndWait();
|
||||
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("Thread2SampleUser", "Password")).MustHaveHappenedOnceExactly();
|
||||
});
|
||||
|
||||
|
||||
connectionFactory.CreateConnection();
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection()).MustHaveHappened(0, Times.Exactly);
|
||||
A.CallTo(() => underlyingConnectionFactory.CreateConnection("SampleUser", "Secret")).MustHaveHappenedOnceExactly();
|
||||
|
||||
thread1.Wait();
|
||||
thread2.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user