001 /* Licensed to the Apache Software Foundation (ASF) under one
002 * or more contributor license agreements. See the NOTICE file
003 * distributed with this work for additional information
004 * regarding copyright ownership. The ASF licenses this file
005 * to you under the Apache License, Version 2.0 (the
006 * "License"); you may not use this file except in compliance
007 * with the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing,
012 * software distributed under the License is distributed on an
013 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
014 * KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations
016 * under the License.
017 */
018 package org.apache.qpid.client.url;
019
020 import java.net.MalformedURLException;
021 import java.util.ArrayList;
022 import java.util.List;
023
024 import org.apache.qpid.client.AMQBrokerDetails;
025 import org.apache.qpid.jms.BrokerDetails;
026
027 /**
028 * The format Qpid URL is based on the AMQP one.
029 * The grammar is as follows:
030 * <p> qpid_url = "qpid:" [client_props "@"] port_addr_list ["/" future-parameters]
031 * <p> port_addr_list = [port_addr ","]* port_addr
032 * <p> port_addr = tcp_port_addr | tls_prot_addr | future_prot_addr
033 * <p> tcp_port_addr = tcp_id tcp_addr
034 * <p> tcp_id = "tcp:" | ""
035 * <p> tcp_addr = host [":" port]
036 * <p> host = <as per http://www.apps.ietf.org/>
037 * <p> port = number
038 * <p> tls_prot_addr = tls_id tls_addr
039 * <p> tls_id = "tls:" | ""
040 * <p> tls_addr = host [":" port]
041 * <p> future_prot_addr = future_prot_id future_prot_addr
042 * <p> future_prot_id = <placeholder, must end in ":". Example "sctp:">
043 * <p> future_prot_addr = <placeholder, protocl-specific address>
044 * <p> future_parameters = <placeholder, not used in failover addresses>
045 * <p> client_props = [client_prop ";"]* client_prop
046 * <p> client_prop = prop "=" val
047 * <p> prop = chars as per <as per http://www.apps.ietf.org/>
048 * <p> val = valid as per <as per http://www.apps.ietf.org/>
049 * <p/>
050 * Ex: qpid:virtualhost=tcp:host-foo,test,client_id=foo@tcp:myhost.com:5672,virtualhost=prod;
051 * keystore=/opt/keystore@client_id2@tls:mysecurehost.com:5672
052 */
053 public class URLParser_0_10
054 {
055 private static final char[] URL_START_SEQ = new char[]{'q', 'p', 'i', 'd', ':'};
056 private static final char PROPERTY_EQUALS_CHAR = '=';
057 private static final char PROPERTY_SEPARATOR_CHAR = ';';
058 private static final char ADDRESS_SEPERATOR_CHAR = ',';
059
060 //private static final char CLIENT_ID_TRANSPORT_SEPARATOR_CHAR = ':';
061 private static final char TRANSPORT_HOST_SEPARATOR_CHAR = ':';
062 private static final char HOST_PORT_SEPARATOR_CHAR = ':';
063 private static final char AT_CHAR = '@';
064 private static final char END_OF_URL_MARKER = '^';
065
066 enum URLParserState
067 {
068 QPID_URL_START,
069 ADDRESS_START,
070 PROPERTY_NAME,
071 PROPERTY_EQUALS,
072 PROPERTY_VALUE,
073 PROPERTY_SEPARATOR,
074 AT_CHAR,
075 TRANSPORT,
076 TRANSPORT_HOST_SEPARATOR,
077 HOST,
078 HOST_PORT_SEPARATOR,
079 PORT,
080 ADDRESS_END,
081 ADDRESS_SEPERATOR,
082 QPID_URL_END,
083 ERROR
084 }
085
086 //-- Constructors
087
088 private char[] _url;
089 private List<BrokerDetails> _brokerDetailList = new ArrayList<BrokerDetails>();
090 private String _error;
091 private int _index = 0;
092 private BrokerDetails _currentBroker;
093 private String _currentPropName;
094 private boolean _endOfURL = false;
095 private URLParserState _currentParserState;
096
097 public URLParser_0_10(String url) throws MalformedURLException
098 {
099 _url = (url + END_OF_URL_MARKER).toCharArray();
100 _endOfURL = false;
101 _currentParserState = URLParserState.QPID_URL_START;
102 URLParserState prevState = _currentParserState; // for error handling
103 try
104 {
105 while (_currentParserState != URLParserState.ERROR && _currentParserState != URLParserState.QPID_URL_END)
106 {
107 prevState = _currentParserState;
108 _currentParserState = next();
109 }
110
111 if (_currentParserState == URLParserState.ERROR)
112 {
113 _error =
114 "Invalid URL format [current_state = " + prevState + ", broker details parsed so far " + _currentBroker + " ] error at (" + _index + ") due to " + _error;
115 MalformedURLException ex;
116 ex = new MalformedURLException(_error);
117 throw ex;
118 }
119 }
120 catch (ArrayIndexOutOfBoundsException e)
121 {
122 _error = "Invalid URL format [current_state = " + prevState + ", broker details parsed so far " + _currentBroker + " ] error at (" + _index + ")";
123 MalformedURLException ex = new MalformedURLException(_error);
124 throw ex;
125 }
126 }
127
128 //-- interface QpidURL
129 public List<BrokerDetails> getAllBrokerDetails()
130 {
131 return _brokerDetailList;
132 }
133
134 public String getURL()
135 {
136 return new String(_url);
137 }
138
139 private URLParserState next()
140 {
141 switch (_currentParserState)
142 {
143 case QPID_URL_START:
144 return checkSequence(URL_START_SEQ, URLParserState.ADDRESS_START);
145 case ADDRESS_START:
146 return startAddress();
147 case PROPERTY_NAME:
148 return extractPropertyName();
149 case PROPERTY_EQUALS:
150 _index++; // skip the equal sign
151 return URLParserState.PROPERTY_VALUE;
152 case PROPERTY_VALUE:
153 return extractPropertyValue();
154 case PROPERTY_SEPARATOR:
155 _index++; // skip ","
156 return URLParserState.PROPERTY_NAME;
157 case AT_CHAR:
158 _index++; // skip the @ sign
159 return URLParserState.TRANSPORT;
160 case TRANSPORT:
161 return extractTransport();
162 case TRANSPORT_HOST_SEPARATOR:
163 _index++; // skip ":"
164 return URLParserState.HOST;
165 case HOST:
166 return extractHost();
167 case HOST_PORT_SEPARATOR:
168 _index++; // skip ":"
169 return URLParserState.PORT;
170 case PORT:
171 return extractPort();
172 case ADDRESS_END:
173 return endAddress();
174 case ADDRESS_SEPERATOR:
175 _index++; // skip ","
176 return URLParserState.ADDRESS_START;
177 default:
178 return URLParserState.ERROR;
179 }
180 }
181
182 private URLParserState checkSequence(char[] expected, URLParserState nextPart)
183 {
184 for (char expectedChar : expected)
185 {
186 if (expectedChar != _url[_index])
187 {
188 _error = "Excepted (" + expectedChar + ") at position " + _index + ", got (" + _url[_index] + ")";
189 return URLParserState.ERROR;
190 }
191 _index++;
192 }
193 return nextPart;
194 }
195
196 private URLParserState startAddress()
197 {
198 _currentBroker = new AMQBrokerDetails();
199
200 for (int j = _index; j < _url.length; j++)
201 {
202 if (_url[j] == PROPERTY_EQUALS_CHAR)
203 {
204 return URLParserState.PROPERTY_NAME;
205 }
206 else if (_url[j] == ADDRESS_SEPERATOR_CHAR)
207 {
208 return URLParserState.TRANSPORT;
209 }
210 }
211 return URLParserState.TRANSPORT;
212 }
213
214 private URLParserState endAddress()
215 {
216 _brokerDetailList.add(_currentBroker);
217 if (_endOfURL)
218 {
219 return URLParserState.QPID_URL_END;
220 }
221 else
222 {
223 return URLParserState.ADDRESS_SEPERATOR;
224 }
225 }
226
227 private URLParserState extractPropertyName()
228 {
229 StringBuilder b = new StringBuilder();
230 char next = _url[_index];
231 while (next != PROPERTY_EQUALS_CHAR && next != AT_CHAR)
232 {
233 b.append(next);
234 next = _url[++_index];
235 }
236 _currentPropName = b.toString();
237 if (_currentPropName.trim().equals(""))
238 {
239 _error = "Property name cannot be empty";
240 return URLParserState.ERROR;
241 }
242 else if (next == PROPERTY_EQUALS_CHAR)
243 {
244 return URLParserState.PROPERTY_EQUALS;
245 }
246 else
247 {
248 return URLParserState.AT_CHAR;
249 }
250 }
251
252 private URLParserState extractPropertyValue()
253 {
254 StringBuilder b = new StringBuilder();
255 char next = _url[_index];
256 while (next != PROPERTY_SEPARATOR_CHAR && next != AT_CHAR)
257 {
258 b.append(next);
259 next = _url[++_index];
260 }
261 String propValue = b.toString();
262 if (propValue.trim().equals(""))
263 {
264 _error = "Property values cannot be empty";
265 return URLParserState.ERROR;
266 }
267 else
268 {
269 _currentBroker.setProperty(_currentPropName, propValue);
270 if (next == PROPERTY_SEPARATOR_CHAR)
271 {
272 return URLParserState.PROPERTY_SEPARATOR;
273 }
274 else
275 {
276 return URLParserState.AT_CHAR;
277 }
278 }
279 }
280
281 private URLParserState extractTransport()
282 {
283 String transport = buildUntil(TRANSPORT_HOST_SEPARATOR_CHAR);
284 if (transport.trim().equals(""))
285 {
286 _error = "Transport cannot be empty";
287 return URLParserState.ERROR;
288 }
289 else if (!(transport.trim().equals(BrokerDetails.PROTOCOL_TCP) || transport.trim()
290 .equals(BrokerDetails.PROTOCOL_TLS)))
291 {
292 _error = "Transport cannot be " + transport + " value must be tcp or tls";
293 return URLParserState.ERROR;
294 }
295 else
296 {
297 _currentBroker.setTransport(transport);
298 return URLParserState.TRANSPORT_HOST_SEPARATOR;
299 }
300 }
301
302 private URLParserState extractHost()
303 {
304 char nextSep = 'c';
305 String host;
306 URLParserState nextState;
307
308 for (int i = _index; i < _url.length; i++)
309 {
310 if (_url[i] == HOST_PORT_SEPARATOR_CHAR)
311 {
312 nextSep = HOST_PORT_SEPARATOR_CHAR;
313 break;
314 }
315 else if (_url[i] == ADDRESS_SEPERATOR_CHAR)
316 {
317 nextSep = ADDRESS_SEPERATOR_CHAR;
318 break;
319 }
320 }
321
322 if (nextSep == HOST_PORT_SEPARATOR_CHAR)
323 {
324 host = buildUntil(HOST_PORT_SEPARATOR_CHAR);
325 nextState = URLParserState.HOST_PORT_SEPARATOR;
326 }
327 else if (nextSep == ADDRESS_SEPERATOR_CHAR)
328 {
329 host = buildUntil(ADDRESS_SEPERATOR_CHAR);
330 nextState = URLParserState.ADDRESS_END;
331 }
332 else
333 {
334 host = buildUntil(END_OF_URL_MARKER);
335 nextState = URLParserState.ADDRESS_END;
336 _endOfURL = true;
337 }
338
339 if (host.trim().equals(""))
340 {
341 _error = "Host cannot be empty";
342 return URLParserState.ERROR;
343 }
344 else
345 {
346 _currentBroker.setHost(host);
347 return nextState;
348 }
349 }
350
351
352 private URLParserState extractPort()
353 {
354
355 StringBuilder b = new StringBuilder();
356 try
357 {
358 char next = _url[_index];
359 while (next != ADDRESS_SEPERATOR_CHAR && next != END_OF_URL_MARKER )
360 {
361 b.append(next);
362 next = _url[++_index];
363 }
364 }
365 catch (ArrayIndexOutOfBoundsException e)
366 {
367 _endOfURL = true;
368 }
369 String portStr = b.toString();
370 if (portStr.trim().equals(""))
371 {
372 _error = "Host cannot be empty";
373 return URLParserState.ERROR;
374 }
375 else
376 {
377 try
378 {
379 int port = Integer.parseInt(portStr);
380 _currentBroker.setPort(port);
381 if( _url[_index] == END_OF_URL_MARKER )
382 {
383 _endOfURL = true;
384 }
385 return URLParserState.ADDRESS_END;
386 }
387 catch (NumberFormatException e)
388 {
389 _error = "Illegal number for port";
390 return URLParserState.ERROR;
391 }
392 }
393 }
394
395 private String buildUntil(char c)
396 {
397 StringBuilder b = new StringBuilder();
398 char next = _url[_index];
399 while (next != c)
400 {
401 b.append(next);
402 next = _url[++_index];
403 }
404 return b.toString();
405 }
406
407 public static void main(String[] args)
408 {
409 String testurl = "qpid:password=pass;username=name@tcp:test1";
410 try
411 {
412 URLParser_0_10 impl = new URLParser_0_10(testurl);
413 for (BrokerDetails d : impl.getAllBrokerDetails())
414 {
415 System.out.println(d);
416 }
417 }
418 catch (Exception e)
419 {
420 e.printStackTrace();
421 }
422 }
423 }
|