Skip to content

Commit 826a536

Browse files
davidcochrumskafandri
authored andcommitted
Dynamic Connection Parameters (#1) (#398)
Sometimes your connection information may need to be dynamic. Dynamic connection parameters allow you to supply or override parameters programmatically through a service. e.g. In a scenario when the `vhost` parameter of the connection depends on the current tenant of your white-labeled application and you do not want (or can't) change it's configuration every time. Define a service under `connection_parameters_provider` that implements the `ConnectionParametersProviderInterface`, and add it to the appropriate `connections` configuration. ```yaml connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: 'foo' # to be dynamically overridden by `connection_parameters_provider` connection_parameters_provider: connection_parameters_provider_service ``` Example Implementation: ```php class ConnectionParametersProviderService implements ConnectionParametersProvider { ... public function getConnectionParameters() { return array('vhost' => $this->getVhost()); } ... } ``` In this case, the `vhost` parameter will be overridden by the output of `getVhost()`.
1 parent 42e7c46 commit 826a536

File tree

6 files changed

+156
-7
lines changed

6 files changed

+156
-7
lines changed

DependencyInjection/Configuration.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ protected function addConnections(ArrayNodeDefinition $node)
6767
->end()
6868
->booleanNode('keepalive')->defaultFalse()->info('requires php-amqplib v2.4.1+ and PHP5.4+')->end()
6969
->scalarNode('heartbeat')->defaultValue(0)->info('requires php-amqplib v2.4.1+')->end()
70+
->scalarNode('connection_parameters_provider')->end()
7071
->end()
7172
->end()
7273
->end()

DependencyInjection/OldSoundRabbitMqExtension.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
66
use Symfony\Component\DependencyInjection\ContainerInterface;
7-
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
87
use Symfony\Component\DependencyInjection\Extension\Extension;
98
use Symfony\Component\DependencyInjection\ContainerBuilder;
109
use Symfony\Component\DependencyInjection\Definition;
@@ -81,6 +80,10 @@ protected function loadConnections()
8180
$definition = new Definition('%old_sound_rabbit_mq.connection_factory.class%', array(
8281
$classParam, $connection,
8382
));
83+
if (isset($connection['connection_parameters_provider'])) {
84+
$definition->addArgument(new Reference($connection['connection_parameters_provider']));
85+
unset($connection['connection_parameters_provider']);
86+
}
8487
$definition->setPublic(false);
8588
$factoryName = sprintf('old_sound_rabbit_mq.connection_factory.%s', $key);
8689
$this->container->setDefinition($factoryName, $definition);
@@ -538,7 +541,7 @@ private function injectLogger(Definition $definition)
538541
));
539542
$definition->addMethodCall('setLogger', array(new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)));
540543
}
541-
544+
542545
/**
543546
* Get default AMQP exchange options
544547
*
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace OldSound\RabbitMqBundle\Provider;
4+
5+
/**
6+
* Interface to provide and/or override connection parameters.
7+
*
8+
* @author David Cochrum <[email protected]>
9+
*/
10+
interface ConnectionParametersProviderInterface
11+
{
12+
/**
13+
* Return connection parameters.
14+
*
15+
* Example:
16+
* array(
17+
* 'host' => 'localhost',
18+
* 'port' => 5672,
19+
* 'user' => 'guest',
20+
* 'password' => 'guest',
21+
* 'vhost' => '/',
22+
* 'lazy' => false,
23+
* 'connection_timeout' => 3,
24+
* 'read_write_timeout' => 3,
25+
* 'keepalive' => false,
26+
* 'heartbeat' => 0,
27+
* 'use_socket' => true,
28+
* )
29+
*
30+
* @return array
31+
*/
32+
public function getConnectionParameters();
33+
}

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,42 @@ by default to avoid possible breaks in applications already using this bundle.
184184

185185
It's a good idea to set the ```read_write_timeout``` to 2x the heartbeat so your socket will be open. If you don't do this, or use a different multiplier, there's a risk the __consumer__ socket will timeout.
186186

187+
### Dynamic Connection Parameters ###
188+
189+
Sometimes your connection information may need to be dynamic. Dynamic connection parameters allow you to supply or
190+
override parameters programmatically through a service.
191+
192+
e.g. In a scenario when the `vhost` parameter of the connection depends on the current tenant of your white-labeled
193+
application and you do not want (or can't) change it's configuration every time.
194+
195+
Define a service under `connection_parameters_provider` that implements the `ConnectionParametersProviderInterface`,
196+
and add it to the appropriate `connections` configuration.
197+
198+
```yaml
199+
connections:
200+
default:
201+
host: 'localhost'
202+
port: 5672
203+
user: 'guest'
204+
password: 'guest'
205+
vhost: 'foo' # to be dynamically overridden by `connection_parameters_provider`
206+
connection_parameters_provider: connection_parameters_provider_service
207+
```
208+
209+
Example Implementation:
210+
211+
```php
212+
class ConnectionParametersProviderService implements ConnectionParametersProvider {
213+
...
214+
public function getConnectionParameters() {
215+
return array('vhost' => $this->getVhost());
216+
}
217+
...
218+
}
219+
```
220+
221+
In this case, the `vhost` parameter will be overridden by the output of `getVhost()`.
222+
187223
## Producers, Consumers, What? ##
188224

189225
In a messaging application, the process sending messages to the broker is called __producer__ while the process receiving those messages is called __consumer__. In your application you will have several of them that you can list under their respective entries in the configuration.

RabbitMq/AMQPConnectionFactory.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace OldSound\RabbitMqBundle\RabbitMq;
44

5+
use OldSound\RabbitMqBundle\Provider\ConnectionParametersProviderInterface;
6+
use PhpAmqpLib\Connection\AbstractConnection;
57
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
68

79
class AMQPConnectionFactory
@@ -27,11 +29,17 @@ class AMQPConnectionFactory
2729
/**
2830
* Constructor
2931
*
30-
* @param string $class FQCN of AMQPConnection class to instantiate.
31-
* @param array $parameters Map containing parameters resolved by Extension.
32+
* @param string $class FQCN of AMQPConnection class to instantiate.
33+
* @param array $parameters Map containing parameters resolved by
34+
* Extension.
35+
* @param ConnectionParametersProviderInterface $parametersProvider Optional service providing/overriding
36+
* connection parameters.
3237
*/
33-
public function __construct($class, array $parameters)
34-
{
38+
public function __construct(
39+
$class,
40+
array $parameters,
41+
ConnectionParametersProviderInterface $parametersProvider = null
42+
) {
3543
$this->class = $class;
3644
$this->parameters = array_merge($this->parameters, $parameters);
3745
$this->parameters = $this->parseUrl($this->parameters);
@@ -40,8 +48,16 @@ public function __construct($class, array $parameters)
4048
? stream_context_create(array('ssl' => $this->parameters['ssl_context']))
4149
: null;
4250
}
51+
if ($parametersProvider) {
52+
$this->parameters = array_merge($this->parameters, $parametersProvider->getConnectionParameters());
53+
}
4354
}
4455

56+
/**
57+
* Creates the appropriate connection using current parameters.
58+
*
59+
* @return AbstractConnection
60+
*/
4561
public function createConnection()
4662
{
4763
return new $this->class(
@@ -62,7 +78,14 @@ public function createConnection()
6278
);
6379
}
6480

65-
private function parseUrl($parameters)
81+
/**
82+
* Parses connection parameters from URL parameter.
83+
*
84+
* @param array $parameters
85+
*
86+
* @return array
87+
*/
88+
private function parseUrl(array $parameters)
6689
{
6790
if (!$parameters['url']) {
6891
return $parameters;

Tests/RabbitMq/AMQPConnectionFactoryTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace OldSound\RabbitMqBundle\Tests\RabbitMq;
44

5+
use OldSound\RabbitMqBundle\Provider\ConnectionParametersProviderInterface;
56
use OldSound\RabbitMqBundle\RabbitMq\AMQPConnectionFactory;
67
use OldSound\RabbitMqBundle\Tests\RabbitMq\Fixtures\AMQPConnection;
78

@@ -208,4 +209,56 @@ public function testSSLConnectionParameters()
208209
0, // heartbeat
209210
), $instance->constructParams);
210211
}
212+
213+
public function testConnectionsParametersProvider()
214+
{
215+
$connectionParametersProvider = $this->prepareConnectionParametersProvider();
216+
$connectionParametersProvider->expects($this->once())
217+
->method('getConnectionParameters')
218+
->will($this->returnValue(
219+
array(
220+
'host' => '1.2.3.4',
221+
'port' => 5678,
222+
'user' => 'admin',
223+
'password' => 'admin',
224+
'vhost' => 'foo',
225+
)
226+
));
227+
$factory = new AMQPConnectionFactory(
228+
'OldSound\RabbitMqBundle\Tests\RabbitMq\Fixtures\AMQPConnection',
229+
array(),
230+
$connectionParametersProvider
231+
);
232+
233+
/** @var AMQPConnection $instance */
234+
$instance = $factory->createConnection();
235+
$this->assertInstanceOf('OldSound\RabbitMqBundle\Tests\RabbitMq\Fixtures\AMQPConnection', $instance);
236+
$this->assertEquals(array(
237+
'1.2.3.4', // host
238+
5678, // port
239+
'admin', // user
240+
'admin', // password
241+
'foo', // vhost
242+
false, // insist
243+
"AMQPLAIN", // login method
244+
null, // login response
245+
"en_US", // locale
246+
3, // connection timeout
247+
3, // read write timeout
248+
null, // context
249+
false, // keepalive
250+
0, // heartbeat
251+
), $instance->constructParams);
252+
}
253+
254+
/**
255+
* Preparing ConnectionParametersProviderInterface instance
256+
*
257+
* @return \PHPUnit_Framework_MockObject_MockObject|ConnectionParametersProviderInterface
258+
*/
259+
private function prepareConnectionParametersProvider()
260+
{
261+
return $this->getMockBuilder('OldSound\RabbitMqBundle\Provider\ConnectionParametersProviderInterface')
262+
->getMock();
263+
}
211264
}

0 commit comments

Comments
 (0)