Coverage Report - org.mule.endpoint.URIBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
URIBuilder
61%
81/132
41%
30/74
2.429
URIBuilder$1
N/A
N/A
2.429
URIBuilder$OrderedQueryParameters
100%
32/32
100%
12/12
2.429
 
 1  
 /*
 2  
  * $Id: URIBuilder.java 11620 2008-04-22 02:37:40Z dfeist $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 
 11  
 package org.mule.endpoint;
 12  
 
 13  
 import org.mule.api.endpoint.EndpointException;
 14  
 import org.mule.api.endpoint.EndpointURI;
 15  
 import org.mule.util.ClassUtils;
 16  
 
 17  
 import java.util.HashMap;
 18  
 import java.util.Iterator;
 19  
 import java.util.LinkedList;
 20  
 import java.util.List;
 21  
 import java.util.Map;
 22  
 import java.util.StringTokenizer;
 23  
 import java.util.TreeMap;
 24  
 
 25  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicReference;
 26  
 
 27  
 /**
 28  
  * This has the following logic:
 29  
  * - if an address is specified, it is used verbatim (except for parameters); this is consistent with the generic case
 30  
  * - otherwise, we construct from components, omitting things that aren't specified as much as possible
 31  
  * (use required attributes to guarantee entries)
 32  
  *
 33  
  * In addition, parameters are handled as follows:
 34  
  * - parameters can be given in the uri, the queryMap, or both
 35  
  * - queryMap values override uri values
 36  
  * - the order of parameters in the uri remains the same (even if values change)
 37  
  * - queryMap parameters are appended after uri parameters
 38  
  * (I don't think ordering should matter, but XFire seems to require it) 
 39  
  *
 40  
  * TODO - check that we have sufficient control via XML (what about empty strings?)
 41  
  *
 42  
  * Not called EndpointURIBuilder because of {@link org.mule.api.endpoint.EndpointURIBuilder}
 43  
  */
 44  
 public class URIBuilder
 45  
 {
 46  
 
 47  
     private static final String DOTS = ":";
 48  
     private static final String DOTS_SLASHES = DOTS + "//";
 49  
     private static final String QUERY = "?";
 50  
     private static final String AND = "&";
 51  
     private static final String EQUALS = "=";
 52  
 
 53  
     public static final String META = "meta";
 54  
     public static final String PROTOCOL = "protocol";
 55  
     public static final String USER = "user";
 56  
     public static final String PASSWORD = "password";
 57  
     public static final String HOST = "host";
 58  
     public static final String ADDRESS = "address";
 59  
     public static final String PORT = "port";
 60  
     public static final String PATH = "path";
 61  
 
 62  2
     public static final String[] ALL_ATTRIBUTES =
 63  
             new String[]{META, PROTOCOL, USER, PASSWORD, HOST, ADDRESS, PORT, PATH};
 64  
     // combinations used in various endpoint parsers to validate required attributes
 65  2
     public static final String[] PATH_ATTRIBUTES = new String[]{PATH};
 66  2
     public static final String[] HOST_ATTRIBUTES = new String[]{HOST};
 67  2
     public static final String[] SOCKET_ATTRIBUTES = new String[]{HOST, PORT};
 68  2
     public static final String[] USERHOST_ATTRIBUTES = new String[]{USER, HOST};
 69  
     // this doesn't include address, since that is handled separately (and is exclusive with these)
 70  2
     public static final String[] ALL_TRANSPORT_ATTRIBUTES = new String[]{USER, PASSWORD, HOST, PORT, PATH};
 71  
 
 72  
     private String address;
 73  
     private String meta;
 74  
     private String protocol;
 75  
     private String user;
 76  
     private String password;
 77  
     private String host;
 78  
     private Integer port;
 79  
     private String path;
 80  
     private Map queryMap;
 81  
 
 82  502
     private AtomicReference cache = new AtomicReference();
 83  
 
 84  
     public URIBuilder()
 85  12
     {
 86  
         // default
 87  12
     }
 88  
 
 89  
     public URIBuilder(EndpointURI endpointURI)
 90  0
     {
 91  0
         cache.set(endpointURI);
 92  0
     }
 93  
 
 94  
     public URIBuilder(String address)
 95  490
     {
 96  
         // separate meta from address, if necessary
 97  490
         int dots = address.indexOf(DOTS);
 98  490
         int dotsSlashes = address.indexOf(DOTS_SLASHES);
 99  490
         if (dots > -1 && dots < dotsSlashes)
 100  
         {
 101  2
             this.meta = address.substring(0, dots);
 102  2
             address = address.substring(dots+1);
 103  
         }
 104  490
         this.address = address;
 105  490
     }
 106  
 
 107  
     public void setUser(String user)
 108  
     {
 109  0
         assertNotUsed();
 110  0
         this.user = user;
 111  0
     }
 112  
 
 113  
     public void setPassword(String password)
 114  
     {
 115  0
         assertNotUsed();
 116  0
         this.password = password;
 117  0
     }
 118  
 
 119  
     public void setHost(String host)
 120  
     {
 121  0
         assertNotUsed();
 122  0
         this.host = host;
 123  0
     }
 124  
 
 125  
     public void setAddress(String address)
 126  
     {
 127  12
         assertNotUsed();
 128  12
         this.address = address;
 129  12
         assertAddressConsistent();
 130  12
     }
 131  
 
 132  
     public void setPort(int port)
 133  
     {
 134  0
         assertNotUsed();
 135  0
         this.port = new Integer(port);
 136  0
     }
 137  
 
 138  
     public void setProtocol(String protocol)
 139  
     {
 140  2
         assertNotUsed();
 141  2
         this.protocol = protocol;
 142  2
         assertAddressConsistent();
 143  2
     }
 144  
 
 145  
     public void setMeta(String meta)
 146  
     {
 147  2
         assertNotUsed();
 148  2
         this.meta = meta;
 149  2
     }
 150  
 
 151  
     public void setPath(String path)
 152  
     {
 153  0
         assertNotUsed();
 154  0
         if (null != path && path.indexOf(DOTS_SLASHES) > -1)
 155  
         {
 156  0
             throw new IllegalArgumentException("Unusual syntax in path: '" + path + "' contains " + DOTS_SLASHES);
 157  
         }
 158  0
         this.path = path;
 159  0
     }
 160  
 
 161  
     public void setQueryMap(Map queryMap)
 162  
     {
 163  8
         assertNotUsed();
 164  8
         this.queryMap = queryMap;
 165  8
     }
 166  
 
 167  
     public EndpointURI getEndpoint()
 168  
     {
 169  2178
         if (null == cache.get())
 170  
         {
 171  
             try
 172  
             {
 173  488
                 EndpointURI endpointUri = new MuleEndpointURI(getConstructor());
 174  486
                 cache.compareAndSet(null, endpointUri);
 175  
             }
 176  2
             catch (EndpointException e)
 177  
             {
 178  2
                 throw (IllegalStateException)new IllegalStateException("Bad endpoint configuration").initCause(e);
 179  486
             }
 180  
         }
 181  2176
         return (EndpointURI)cache.get();
 182  
     }
 183  
 
 184  
     /**
 185  
      * @return The String supplied to the delegate constructor
 186  
      */
 187  
     protected String getConstructor()
 188  
     {
 189  500
         StringBuffer buffer = new StringBuffer();
 190  500
         appendMeta(buffer);
 191  500
         OrderedQueryParameters uriQueries = appendAddress(buffer);
 192  500
         uriQueries.override(queryMap);
 193  500
         buffer.append(uriQueries.toString());
 194  500
         return buffer.toString();
 195  
     }
 196  
 
 197  
     private void appendMeta(StringBuffer buffer)
 198  
     {
 199  500
         if (null != meta)
 200  
         {
 201  4
             buffer.append(meta);
 202  4
             buffer.append(DOTS);
 203  
         }
 204  500
     }
 205  
 
 206  
     private OrderedQueryParameters appendAddress(StringBuffer buffer)
 207  
     {
 208  500
         if (null != address)
 209  
         {
 210  500
             int index = address.indexOf(QUERY);
 211  500
             if (index > -1)
 212  
             {
 213  18
                 buffer.append(address.substring(0, index));
 214  18
                 return parseQueries(address.substring(index + 1));
 215  
             }
 216  
             else
 217  
             {
 218  482
                 buffer.append(address);
 219  482
                 return new OrderedQueryParameters();
 220  
             }
 221  
         }
 222  
         else
 223  
         {
 224  0
             constructAddress(buffer);
 225  0
             return new OrderedQueryParameters();
 226  
         }
 227  
     }
 228  
 
 229  
     private OrderedQueryParameters parseQueries(String queries)
 230  
     {
 231  18
         OrderedQueryParameters map = new OrderedQueryParameters();
 232  18
         StringTokenizer query = new StringTokenizer(queries, AND);
 233  42
         while (query.hasMoreTokens())
 234  
         {
 235  24
             StringTokenizer nameValue = new StringTokenizer(query.nextToken(), EQUALS);
 236  24
             String name = nameValue.nextToken();
 237  24
             String value = null;
 238  24
             if (nameValue.hasMoreTokens())
 239  
             {
 240  16
                 value = nameValue.nextToken();
 241  
             }
 242  24
             map.put(name, value);
 243  24
         }
 244  18
         return map;
 245  
     }
 246  
 
 247  
     private void constructAddress(StringBuffer buffer)
 248  
     {
 249  0
         buffer.append(protocol);
 250  0
         buffer.append(DOTS_SLASHES);
 251  0
         boolean atStart = true;
 252  0
         if (null != user)
 253  
         {
 254  0
             buffer.append(user);
 255  0
             if (null != password)
 256  
             {
 257  0
                 buffer.append(":");
 258  0
                 buffer.append(password);
 259  
             }
 260  0
             buffer.append("@");
 261  0
             atStart = false;
 262  
         }
 263  0
         if (null != host)
 264  
         {
 265  0
             buffer.append(host);
 266  0
             if (null != port)
 267  
             {
 268  0
                 buffer.append(":");
 269  0
                 buffer.append(port);
 270  
             }
 271  0
             atStart = false;
 272  
         }
 273  0
         if (null != path)
 274  
         {
 275  0
             if (! atStart)
 276  
             {
 277  0
                 buffer.append("/");
 278  
             }
 279  0
             buffer.append(path);
 280  
         }
 281  0
     }
 282  
 
 283  
     protected void assertNotUsed()
 284  
     {
 285  24
         if (null != cache.get())
 286  
         {
 287  0
             throw new IllegalStateException("Too late to set values - builder already used");
 288  
         }
 289  24
     }
 290  
 
 291  
     protected void assertAddressConsistent()
 292  
     {
 293  14
         if (null != meta)
 294  
         {
 295  2
             if (null != address)
 296  
             {
 297  2
                 if (address.startsWith(meta + DOTS))
 298  
                 {
 299  0
                     throw new IllegalArgumentException("Meta-protocol '" + meta +
 300  
                             "' should not be specified in the address '" + address +
 301  
                             "' - it is implicit in the element namespace.");
 302  
                 }
 303  2
                 if (null != protocol)
 304  
                 {
 305  0
                     assertProtocolConsistent();
 306  
                 }
 307  
                 else
 308  
                 {
 309  2
                     if (address.indexOf(DOTS_SLASHES) == -1)
 310  
                     {
 311  0
                         throw new IllegalArgumentException("Address '" + address +
 312  
                                 "' does not have a transport protocol prefix " +
 313  
                                 "(omit the meta protocol prefix, '" + meta + DOTS +
 314  
                                 "' - it is implicit in the element namespace).");
 315  
                     }
 316  
                 }
 317  
             }
 318  
         }
 319  
         else
 320  
         {
 321  12
             assertProtocolConsistent();
 322  
         }
 323  14
     }
 324  
 
 325  
     protected void assertProtocolConsistent()
 326  
     {
 327  12
         if (null != protocol && null != address && !address.startsWith(protocol + DOTS_SLASHES))
 328  
         {
 329  0
             throw new IllegalArgumentException("Address '" + address + "' for protocol '" + protocol +
 330  
                     "' should start with " + protocol + DOTS_SLASHES);
 331  
         }
 332  12
     }
 333  
 
 334  
     public String toString()
 335  
     {
 336  12
         return getConstructor();
 337  
     }
 338  
 
 339  
     public boolean equals(Object other)
 340  
     {
 341  6
         if (null == other || !getClass().equals(other.getClass())) return false;
 342  6
         if (this == other) return true;
 343  
 
 344  0
         URIBuilder builder = (URIBuilder) other;
 345  0
         return equal(address, builder.address)
 346  
                 && equal(meta, builder.meta)
 347  
                 && equal(protocol, builder.protocol)
 348  
                 && equal(user, builder.user)
 349  
                 && equal(password, builder.password)
 350  
                 && equal(host, builder.host)
 351  
                 && equal(port, builder.port)
 352  
                 && equal(path, builder.path)
 353  
                 && equal(queryMap, builder.queryMap);
 354  
     }
 355  
 
 356  
     protected static boolean equal(Object a, Object b)
 357  
     {
 358  0
         return ClassUtils.equal(a, b);
 359  
     }
 360  
 
 361  
     public int hashCode()
 362  
     {
 363  32
         return ClassUtils.hash(new Object[]{address, meta, protocol, user, password, host, port, path, queryMap});
 364  
     }
 365  
 
 366  1000
     private static class OrderedQueryParameters
 367  
     {
 368  
 
 369  500
         private Map values = new HashMap();
 370  500
         private List orderedKeys = new LinkedList();
 371  
 
 372  
         public void put(String name, String value)
 373  
         {
 374  24
             values.put(name, value);
 375  24
             orderedKeys.add(name);
 376  24
         }
 377  
 
 378  
         public void override(Map map)
 379  
         {
 380  500
             if (null != map)
 381  
             {
 382  
                 // order additional parameters
 383  8
                 Iterator names = new TreeMap(map).keySet().iterator();
 384  24
                 while (names.hasNext())
 385  
                 {
 386  16
                     String name = (String) names.next();
 387  16
                     String value  =(String) map.get(name);
 388  16
                     if (! values.keySet().contains(name))
 389  
                     {
 390  10
                         orderedKeys.add(name);
 391  
                     }
 392  16
                     values.put(name, value);
 393  16
                 }
 394  
             }
 395  500
         }
 396  
 
 397  
         public String toString()
 398  
         {
 399  500
             StringBuffer buffer = new StringBuffer();
 400  500
             Iterator keys = orderedKeys.iterator();
 401  500
             boolean first = true;
 402  534
             while (keys.hasNext())
 403  
             {
 404  34
                 if (first)
 405  
                 {
 406  20
                     buffer.append(QUERY);
 407  20
                     first = false;
 408  
                 }
 409  
                 else
 410  
                 {
 411  14
                     buffer.append(AND);
 412  
                 }
 413  34
                 String key = (String)keys.next();
 414  34
                 buffer.append(key);
 415  34
                 String value = (String)values.get(key);
 416  34
                 if (null != value)
 417  
                 {
 418  28
                     buffer.append(EQUALS);
 419  28
                     buffer.append(value);
 420  
                 }
 421  34
             }
 422  500
             return buffer.toString();
 423  
         }
 424  
 
 425  
     }
 426  
 
 427  
 }