URLParser_0_10.java
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 urlthrows 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 }