Skip to content

Commit 350e90c

Browse files
author
Michael Bridgen
committed
Merge bug 24453 (connection URLs)
2 parents 8f30ec2 + 19ff425 commit 350e90c

25 files changed

+323
-170
lines changed

build.xml

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,7 @@
227227
<java fork="true" classname="${test.main}"
228228
failonerror="${haltOnFailureJava}" errorproperty="test.failure">
229229
<jvmarg value="-Xdebug"/>
230-
<arg value="${broker.hostname}"/>
231-
<arg value="${broker.port}"/>
230+
<arg value="amqp://${broker.hostname}:${broker.port}"/>
232231
<classpath>
233232
<path refid="test.javac.classpath"/>
234233
<pathelement path="${javac.out}"/>
@@ -242,8 +241,7 @@
242241
failonerror="${haltOnFailureJava}" errorproperty="test.failure">
243242
<jvmarg value="-Xdebug"/>
244243
<jvmarg value="-Dsilent=true"/>
245-
<arg value="${broker.hostname}"/>
246-
<arg value="${broker.port}"/>
244+
<arg value="amqp://${broker.hostname}:${broker.port}"/>
247245
<classpath>
248246
<path refid="test.javac.classpath"/>
249247
<pathelement path="${javac.out}"/>
@@ -257,8 +255,7 @@
257255
failonerror="${haltOnFailureJava}" errorproperty="test.failure">
258256
<jvmarg value="-Xdebug"/>
259257
<jvmarg value="-Dsilent=true"/>
260-
<arg value="${broker.hostname}"/>
261-
<arg value="${broker.port}"/>
258+
<arg value="amqp://${broker.hostname}:${broker.port}"/>
262259
<classpath>
263260
<path refid="test.javac.classpath"/>
264261
<pathelement path="${javac.out}"/>
@@ -270,8 +267,7 @@
270267
<target name="producer" depends="test-build">
271268
<java fork="true" classname="com.rabbitmq.examples.ProducerMain">
272269
<jvmarg value="-Xdebug"/>
273-
<arg value="${broker.hostname}"/>
274-
<arg value="${broker.port}"/>
270+
<arg value="amqp://${broker.hostname}:${broker.port}"/>
275271
<arg value="${test.producer.rate-limit}"/>
276272
<arg value="${test.producer.message-count}"/>
277273
<arg value="${test.producer.send-completion}"/>
@@ -288,8 +284,7 @@
288284
<target name="consumer" depends="test-build">
289285
<java fork="true" classname="com.rabbitmq.examples.ConsumerMain">
290286
<jvmarg value="-Xdebug"/>
291-
<arg value="${broker.hostname}"/>
292-
<arg value="${broker.port}"/>
287+
<arg value="amqp://${broker.hostname}:${broker.port}"/>
293288
<classpath>
294289
<path refid="test.javac.classpath"/>
295290
<pathelement path="${javac.out}"/>

src/com/rabbitmq/client/Connection.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@
3838
*
3939
* Channel channel = conn.createChannel();
4040
* </pre>
41+
* Or, more compactly:
42+
*
43+
* <pre>
44+
* ConnectionFactory factory = new ConnectionFactory();
45+
* factory.setUri("amqp://username:password@hostName:portNumber/virtualHost");
46+
* Connection conn = factory.newConnection();
47+
* Channel channel = conn.createChannel()
48+
* </pre>
4149
*
4250
* Current implementations are thread-safe for code at the client API level,
4351
* and in fact thread-safe internally except for code within RPC calls.

src/com/rabbitmq/client/ConnectionFactory.java

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@
2323
import java.util.concurrent.ExecutorService;
2424
import java.util.concurrent.Executors;
2525

26-
import java.net.Socket;
2726
import java.net.InetSocketAddress;
27+
import java.net.Socket;
28+
import java.net.URI;
29+
import java.net.URISyntaxException;
30+
import java.net.URLDecoder;
2831

2932
import javax.net.SocketFactory;
3033
import javax.net.ssl.SSLSocketFactory;
@@ -173,6 +176,89 @@ public void setVirtualHost(String virtualHost) {
173176
this.virtualHost = virtualHost;
174177
}
175178

179+
/**
180+
* Convenience method for setting the fields in an AMQP URI: host,
181+
* port, username, password and virtual host. If any part of the
182+
* URI is ommited, the ConnectionFactory's corresponding variable
183+
* is left unchanged.
184+
* @param uri is the AMQP URI containing the data
185+
*/
186+
public void setUri(URI uri)
187+
throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException
188+
{
189+
if ("amqp".equals(uri.getScheme().toLowerCase())) {
190+
// nothing special to do
191+
} else if ("amqps".equals(uri.getScheme().toLowerCase())) {
192+
setPort(DEFAULT_AMQP_OVER_SSL_PORT);
193+
useSslProtocol();
194+
} else {
195+
throw new IllegalArgumentException("Wrong scheme in AMQP URI: " +
196+
uri.getScheme());
197+
}
198+
199+
String host = uri.getHost();
200+
if (host != null) {
201+
setHost(host);
202+
}
203+
204+
int port = uri.getPort();
205+
if (port != -1) {
206+
setPort(port);
207+
}
208+
209+
String userInfo = uri.getRawUserInfo();
210+
if (userInfo != null) {
211+
String userPass[] = userInfo.split(":");
212+
if (userPass.length > 2) {
213+
throw new IllegalArgumentException("Bad user info in AMQP " +
214+
"URI: " + userInfo);
215+
}
216+
217+
setUsername(uriDecode(userPass[0]));
218+
if (userPass.length == 2) {
219+
setPassword(uriDecode(userPass[1]));
220+
}
221+
}
222+
223+
String path = uri.getRawPath();
224+
if (path != null && path.length() > 0) {
225+
if (path.indexOf('/', 1) != -1) {
226+
throw new IllegalArgumentException("Multiple segments in " +
227+
"path of AMQP URI: " +
228+
path);
229+
}
230+
231+
setVirtualHost(uriDecode(uri.getPath().substring(1)));
232+
}
233+
}
234+
235+
/**
236+
* Convenience method for setting the fields in an AMQP URI: host,
237+
* port, username, password and virtual host. If any part of the
238+
* URI is ommited, the ConnectionFactory's corresponding variable
239+
* is left unchanged. Note that not all valid AMQP URIs are
240+
* accepted; in particular, the hostname must be given if the
241+
* port, username or password are given, and escapes in the
242+
* hostname are not permitted.
243+
* @param uriString is the AMQP URI containing the data
244+
*/
245+
public void setUri(String uriString)
246+
throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException
247+
{
248+
setUri(new URI(uriString));
249+
}
250+
251+
private String uriDecode(String s) {
252+
try {
253+
// URLDecode decodes '+' to a space, as for
254+
// form encoding. So protect plus signs.
255+
return URLDecoder.decode(s.replace("+", "%2B"), "US-ASCII");
256+
}
257+
catch (java.io.UnsupportedEncodingException e) {
258+
throw new RuntimeException(e);
259+
}
260+
}
261+
176262
/**
177263
* Retrieve the requested maximum channel number
178264
* @return the initially requested maximum channel number; zero for unlimited
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// The contents of this file are subject to the Mozilla Public License
2+
// Version 1.1 (the "License"); you may not use this file except in
3+
// compliance with the License. You may obtain a copy of the License
4+
// at http://www.mozilla.org/MPL/
5+
//
6+
// Software distributed under the License is distributed on an "AS IS"
7+
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
8+
// the License for the specific language governing rights and
9+
// limitations under the License.
10+
//
11+
// The Original Code is RabbitMQ.
12+
//
13+
// The Initial Developer of the Original Code is VMware, Inc.
14+
// Copyright (c) 2011 VMware, Inc. All rights reserved.
15+
//
16+
//
17+
package com.rabbitmq.client.test;
18+
19+
import com.rabbitmq.client.ConnectionFactory;
20+
21+
import java.security.KeyManagementException;
22+
import java.security.NoSuchAlgorithmException;
23+
import java.net.URISyntaxException;
24+
25+
public class AmqpUriTest extends BrokerTestCase
26+
{
27+
public void testUriParsing()
28+
throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException
29+
{
30+
/* From the spec (subset of the tests) */
31+
parseSuccess("amqp://user:pass@host:10000/vhost",
32+
"user", "pass", "host", 10000, "vhost");
33+
parseSuccess("aMQps://user%61:%61pass@host:10000/v%2fhost",
34+
"usera", "apass", "host", 10000, "v/host");
35+
parseSuccess("amqp://host", "guest", "guest", "host", 5672, "/");
36+
parseSuccess("amqp:///vhost",
37+
"guest", "guest", "localhost", 5672, "vhost");
38+
parseSuccess("amqp://host/", "guest", "guest", "host", 5672, "");
39+
parseSuccess("amqp://host/%2f", "guest", "guest", "host", 5672, "/");
40+
parseSuccess("amqp://[::1]", "guest", "guest", "[::1]", 5672, "/");
41+
42+
/* Various other success cases */
43+
parseSuccess("amqp://host:100", "guest", "guest", "host", 100, "/");
44+
parseSuccess("amqp://[::1]:100", "guest", "guest", "[::1]", 100, "/");
45+
46+
parseSuccess("amqp://host/blah",
47+
"guest", "guest", "host", 5672, "blah");
48+
parseSuccess("amqp://host:100/blah",
49+
"guest", "guest", "host", 100, "blah");
50+
parseSuccess("amqp://[::1]/blah",
51+
"guest", "guest", "[::1]", 5672, "blah");
52+
parseSuccess("amqp://[::1]:100/blah",
53+
"guest", "guest", "[::1]", 100, "blah");
54+
55+
parseSuccess("amqp://user:pass@host",
56+
"user", "pass", "host", 5672, "/");
57+
parseSuccess("amqp://user:pass@[::1]",
58+
"user", "pass", "[::1]", 5672, "/");
59+
parseSuccess("amqp://user:pass@[::1]:100",
60+
"user", "pass", "[::1]", 100, "/");
61+
62+
/* Various failure cases */
63+
parseFail("http://www.rabbitmq.com");
64+
parseFail("amqp://foo[::1]");
65+
parseFail("amqp://foo:[::1]");
66+
parseFail("amqp://[::1]foo");
67+
68+
parseFail("amqp://foo%1");
69+
parseFail("amqp://foo%1x");
70+
parseFail("amqp://foo%xy");
71+
}
72+
73+
private void parseSuccess(String uri, String user, String password,
74+
String host, int port, String vhost)
75+
throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException
76+
{
77+
ConnectionFactory cf = new ConnectionFactory();
78+
cf.setUri(uri);
79+
80+
assertEquals(user, cf.getUsername());
81+
assertEquals(password, cf.getPassword());
82+
assertEquals(host, cf.getHost());
83+
assertEquals(port, cf.getPort());
84+
assertEquals(vhost, cf.getVirtualHost());
85+
}
86+
87+
private void parseFail(String uri) {
88+
try {
89+
(new ConnectionFactory()).setUri(uri);
90+
fail("URI parse didn't fail: '" + uri + "'");
91+
} catch (Exception e) {
92+
// whoosh!
93+
}
94+
}
95+
}

test/src/com/rabbitmq/client/test/ClientTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public static TestSuite suite() {
3737
suite.addTestSuite(MultiThreadedChannel.class);
3838
suite.addTestSuite(com.rabbitmq.utility.IntAllocatorTests.class);
3939
suite.addTestSuite(AMQBuilderApiTest.class);
40+
suite.addTestSuite(AmqpUriTest.class);
4041
return suite;
4142
}
4243
}

test/src/com/rabbitmq/examples/BufferPerformanceMetrics.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class BufferPerformanceMetrics {
4343
public static double NANOSECONDS_PER_SECOND = 1000 * 1000 * 1000;
4444

4545
public static void main(String[] args) throws Exception {
46-
final String hostName = args.length > 0 ? args[0] : "localhost";
46+
final String uri = args.length > 0 ? args[0] : "amqp://localhost";
4747

4848
Random rnd = new Random();
4949

@@ -64,8 +64,8 @@ public static void main(String[] args) throws Exception {
6464

6565
for(final boolean useNagle : new boolean[] { false, true }) {
6666
ConnectionFactory factory = new ConnectionFactory() {
67-
{ setHost(hostName); }
68-
67+
{ setUri(uri); }
68+
6969
public void configureSocket(Socket socket)
7070
throws IOException {
7171
socket.setTcpNoDelay(!useNagle);

test/src/com/rabbitmq/examples/ConsumerMain.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,10 @@ public static boolean optArg(String[] args, int index, boolean def) {
5151

5252
public static void main(String[] args) {
5353
try {
54-
final String hostName = optArg(args, 0, "localhost");
55-
final int portNumber = optArg(args, 1, AMQP.PROTOCOL.PORT);
56-
boolean writeStats = optArg(args, 2, true);
57-
boolean autoAck = optArg(args, 3, true);
58-
final Connection conn = new ConnectionFactory(){{setHost(hostName); setPort(portNumber);}}.newConnection();
54+
final String uri = optArg(args, 0, "amqp://localhost");
55+
boolean writeStats = optArg(args, 1, true);
56+
boolean autoAck = optArg(args, 2, true);
57+
final Connection conn = new ConnectionFactory(){{setUri(uri);}}.newConnection();
5958
System.out.println("Channel 0 fully open.");
6059
new ConsumerMain(conn, writeStats, autoAck).run();
6160
} catch (Exception e) {

test/src/com/rabbitmq/examples/FileConsumer.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
public class FileConsumer {
3838
public static void main(String[] args) {
3939
Options options = new Options();
40-
options.addOption(new Option("h", "host", true, "broker host"));
41-
options.addOption(new Option("p", "port", true, "broker port"));
40+
options.addOption(new Option("h", "uri", true, "AMQP URI"));
4241
options.addOption(new Option("q", "queue", true, "queue name"));
4342
options.addOption(new Option("t", "type", true, "exchange type"));
4443
options.addOption(new Option("e", "exchange", true, "exchange name"));
@@ -50,8 +49,7 @@ public static void main(String[] args) {
5049
try {
5150
CommandLine cmd = parser.parse(options, args);
5251

53-
String hostName = strArg(cmd, 'h', "localhost");
54-
int portNumber = intArg(cmd, 'p', AMQP.PROTOCOL.PORT);
52+
String uri = strArg(cmd, 'h', "amqp://localhost");
5553
String requestedQueueName = strArg(cmd, 'q', "");
5654
String exchangeType = strArg(cmd, 't', "direct");
5755
String exchange = strArg(cmd, 'e', null);
@@ -65,8 +63,7 @@ public static void main(String[] args) {
6563
}
6664

6765
ConnectionFactory connFactory = new ConnectionFactory();
68-
connFactory.setHost(hostName);
69-
connFactory.setPort(portNumber);
66+
connFactory.setUri(uri);
7067
Connection conn = connFactory.newConnection();
7168

7269
final Channel ch = conn.createChannel();

test/src/com/rabbitmq/examples/FileProducer.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
public class FileProducer {
3838
public static void main(String[] args) {
3939
Options options = new Options();
40-
options.addOption(new Option("h", "host", true, "broker host"));
40+
options.addOption(new Option("h", "uri", true, "AMQP URI"));
4141
options.addOption(new Option("p", "port", true, "broker port"));
4242
options.addOption(new Option("t", "type", true, "exchange type"));
4343
options.addOption(new Option("e", "exchange", true, "exchange name"));
@@ -48,15 +48,13 @@ public static void main(String[] args) {
4848
try {
4949
CommandLine cmd = parser.parse(options, args);
5050

51-
String hostName = strArg(cmd, 'h', "localhost");
52-
int portNumber = intArg(cmd, 'p', AMQP.PROTOCOL.PORT);
51+
String uri = strArg(cmd, 'h', "amqp://localhost");
5352
String exchangeType = strArg(cmd, 't', "direct");
5453
String exchange = strArg(cmd, 'e', null);
5554
String routingKey = strArg(cmd, 'k', null);
5655

5756
ConnectionFactory connFactory = new ConnectionFactory();
58-
connFactory.setHost(hostName);
59-
connFactory.setPort(portNumber);
57+
connFactory.setUri(uri);
6058
Connection conn = connFactory.newConnection();
6159

6260
final Channel ch = conn.createChannel();

test/src/com/rabbitmq/examples/HelloClient.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ public class HelloClient {
2727
public static void main(String[] args) {
2828
try {
2929
String request = (args.length > 0) ? args[0] : "Rabbit";
30-
String hostName = (args.length > 1) ? args[1] : "localhost";
31-
int portNumber = (args.length > 2) ? Integer.parseInt(args[2]) : AMQP.PROTOCOL.PORT;
30+
String uri = (args.length > 1) ? args[1] : "amqp://localhost";
3231

33-
ConnectionFactory cfconn = new ConnectionFactory();
34-
cfconn.setHost(hostName);
35-
cfconn.setPort(portNumber);
32+
ConnectionFactory cfconn = new ConnectionFactory();
33+
cfconn.setUri(uri);
3634
Connection conn = cfconn.newConnection();
3735
Channel ch = conn.createChannel();
3836
RpcClient service = new RpcClient(ch, "", "Hello");

test/src/com/rabbitmq/examples/HelloJsonClient.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,10 @@ public class HelloJsonClient {
2929
public static void main(String[] args) {
3030
try {
3131
String request = (args.length > 0) ? args[0] : "Rabbit";
32-
String hostName = (args.length > 1) ? args[1] : "localhost";
33-
int portNumber = (args.length > 2) ? Integer.parseInt(args[2]) : AMQP.PROTOCOL.PORT;
32+
String uri = (args.length > 1) ? args[1] : "amqp://localhost";
3433

35-
ConnectionFactory cfconn = new ConnectionFactory();
36-
cfconn.setHost(hostName);
37-
cfconn.setPort(portNumber);
34+
ConnectionFactory cfconn = new ConnectionFactory();
35+
cfconn.setUri(uri);
3836
Connection conn = cfconn.newConnection();
3937
Channel ch = conn.createChannel();
4038
JsonRpcClient client = new JsonRpcClient(ch, "", "Hello", RPC_TIMEOUT_ONE_SECOND);

0 commit comments

Comments
 (0)