Coverage Report - org.mule.endpoint.URIBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
URIBuilder
0%
0/146
0%
0/76
0
URIBuilder$1
N/A
N/A
0
URIBuilder$OrderedQueryParameters
0%
0/31
0%
0/12
0
 
 1  
 /*
 2  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 3  
  * The software in this package is published under the terms of the CPAL v1.0
 4  
  * license, a copy of which has been included with this distribution in the
 5  
  * LICENSE.txt file.
 6  
  */
 7  
 package org.mule.endpoint;
 8  
 
 9  
 import org.mule.api.MuleContext;
 10  
 import org.mule.api.endpoint.EndpointException;
 11  
 import org.mule.api.endpoint.EndpointURI;
 12  
 import org.mule.util.ClassUtils;
 13  
 
 14  
 import java.io.File;
 15  
 import java.net.URI;
 16  
 import java.net.URLDecoder;
 17  
 import java.util.ArrayList;
 18  
 import java.util.Iterator;
 19  
 import java.util.List;
 20  
 import java.util.Map;
 21  
 import java.util.StringTokenizer;
 22  
 import java.util.TreeMap;
 23  
 
 24  
 import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicReference;
 25  
 
 26  
 /**
 27  
  * This has the following logic:
 28  
  * - if an address is specified, it is used verbatim (except for parameters); this is consistent with the generic case
 29  
  * - otherwise, we construct from components, omitting things that aren't specified as much as possible
 30  
  * (use required attributes to guarantee entries)
 31  
  *
 32  
  * In addition, parameters are handled as follows:
 33  
  * - parameters can be given in the uri, the queryMap, or both
 34  
  * - queryMap values override uri values
 35  
  * - the order of parameters in the uri remains the same (even if values change)
 36  
  * - queryMap parameters are appended after uri parameters
 37  
  *
 38  
  * TODO - check that we have sufficient control via XML (what about empty strings?)
 39  
  *
 40  
  * Not called EndpointURIBuilder because of {@link org.mule.api.endpoint.EndpointURIBuilder}
 41  
  * 
 42  
  */
 43  
 public class URIBuilder
 44  
 {
 45  
 
 46  
     private static final String DOTS = ":";
 47  
     private static final String DOTS_SLASHES = DOTS + "//";
 48  
     private static final String QUERY = "?";
 49  
     private static final String AND = "&";
 50  
     private static final String EQUALS = "=";
 51  
     private static final String BACKSLASH = "\\";
 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  0
     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  0
     public static final String[] PATH_ATTRIBUTES = new String[]{PATH};
 66  0
     public static final String[] HOST_ATTRIBUTES = new String[]{HOST};
 67  0
     public static final String[] SOCKET_ATTRIBUTES = new String[]{HOST, PORT};
 68  0
     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  0
     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  
     private MuleContext muleContext;
 82  
 
 83  0
     private AtomicReference cache = new AtomicReference();
 84  
 
 85  
     public URIBuilder()
 86  0
     {
 87  
         //default for spring. Must call setMulecontext().
 88  0
     }
 89  
 
 90  
     public URIBuilder(MuleContext muleContext)
 91  0
     {
 92  0
         this.muleContext = muleContext;
 93  0
     }
 94  
 
 95  
     public URIBuilder(EndpointURI endpointURI)
 96  
     {
 97  0
         this(endpointURI.getMuleContext());
 98  0
         cache.set(endpointURI);
 99  0
     }
 100  
 
 101  
     public URIBuilder(String address, MuleContext muleContext)
 102  
     {
 103  0
         this(muleContext);
 104  
         // separate meta from address, if necessary
 105  0
         int dots = address.indexOf(DOTS);
 106  0
         int dotsSlashes = address.indexOf(DOTS_SLASHES);
 107  0
         if (dots > -1 && dots < dotsSlashes)
 108  
         {
 109  0
             this.meta = address.substring(0, dots);
 110  0
             address = address.substring(dots+1);
 111  
         }
 112  0
         this.address = address;
 113  0
     }
 114  
 
 115  
     public MuleContext getMuleContext()
 116  
     {
 117  0
         return muleContext;
 118  
     }
 119  
 
 120  
     public void setMuleContext(MuleContext muleContext)
 121  
     {
 122  0
         this.muleContext = muleContext;
 123  0
     }
 124  
 
 125  
     public void setUser(String user)
 126  
     {
 127  0
         assertNotUsed();
 128  0
         this.user = user;
 129  0
     }
 130  
 
 131  
     public void setPassword(String password)
 132  
     {
 133  0
         assertNotUsed();
 134  0
         this.password = password;
 135  0
     }
 136  
 
 137  
     public void setHost(String host)
 138  
     {
 139  0
         assertNotUsed();
 140  0
         this.host = host;
 141  0
     }
 142  
 
 143  
     public void setAddress(String address)
 144  
     {
 145  0
         assertNotUsed();
 146  0
         this.address = address;
 147  0
         assertAddressConsistent();
 148  0
     }
 149  
 
 150  
     public void setPort(int port)
 151  
     {
 152  0
         assertNotUsed();
 153  0
         this.port = new Integer(port);
 154  0
     }
 155  
 
 156  
     public void setProtocol(String protocol)
 157  
     {
 158  0
         assertNotUsed();
 159  0
         this.protocol = protocol;
 160  0
         assertAddressConsistent();
 161  0
     }
 162  
 
 163  
     public void setMeta(String meta)
 164  
     {
 165  0
         assertNotUsed();
 166  0
         this.meta = meta;
 167  0
     }
 168  
 
 169  
     public void setPath(String path)
 170  
     {
 171  0
         assertNotUsed();
 172  0
         if (null != path)
 173  
         {
 174  0
             if (path.indexOf(DOTS_SLASHES) > -1)
 175  
             {
 176  0
                 throw new IllegalArgumentException("Unusual syntax in path: '" + path + "' contains " + DOTS_SLASHES);
 177  
             }
 178  0
             else if (path.contains(BACKSLASH))
 179  
             {
 180  
                 // Windows syntax.  convert it to URI syntax
 181  
                 try
 182  
                 {
 183  0
                     URI pathUri = new File(path).toURI();
 184  0
                     path = pathUri.getPath();
 185  
                 }
 186  0
                 catch (Exception ex)
 187  
                 {
 188  0
                     throw new IllegalArgumentException("Illegal syntax in path: " + path, ex);
 189  0
                 }
 190  
             }
 191  
         }
 192  0
         this.path = path;
 193  0
     }
 194  
 
 195  
     public void setQueryMap(Map queryMap)
 196  
     {
 197  0
         assertNotUsed();
 198  0
         this.queryMap = queryMap;
 199  0
     }
 200  
 
 201  
     public EndpointURI getEndpoint()
 202  
     {
 203  0
         if (null == cache.get())
 204  
         {
 205  
             try
 206  
             {
 207  0
                 EndpointURI endpointUri = new MuleEndpointURI(getConstructor(), getEncodedConstructor(), muleContext);
 208  0
                 cache.compareAndSet(null, endpointUri);
 209  
             }
 210  0
             catch (EndpointException e)
 211  
             {
 212  0
                 throw (IllegalStateException)new IllegalStateException("Bad endpoint configuration").initCause(e);
 213  0
             }
 214  
         }
 215  0
         return (EndpointURI)cache.get();
 216  
     }
 217  
 
 218  
     /**
 219  
      * @return The String supplied to the delegate constructor
 220  
      */
 221  
     protected String getConstructor()
 222  
     {
 223  0
         return URLDecoder.decode(getEncodedConstructor());
 224  
     }
 225  
 
 226  
     protected String getEncodedConstructor()
 227  
     {
 228  0
         StringBuffer buffer = new StringBuffer();
 229  0
         appendMeta(buffer);
 230  0
         OrderedQueryParameters uriQueries = appendAddress(buffer);
 231  0
         uriQueries.override(queryMap);
 232  0
         buffer.append(uriQueries.toString());
 233  0
         return buffer.toString();
 234  
     }
 235  
 
 236  
     private void appendMeta(StringBuffer buffer)
 237  
     {
 238  0
         if (null != meta)
 239  
         {
 240  0
             buffer.append(meta);
 241  0
             buffer.append(DOTS);
 242  
         }
 243  0
     }
 244  
 
 245  
     private OrderedQueryParameters appendAddress(StringBuffer buffer)
 246  
     {
 247  0
         if (null != address)
 248  
         {
 249  0
             int index = address.indexOf(QUERY);
 250  0
             if (index > -1)
 251  
             {
 252  0
                 buffer.append(address.substring(0, index));
 253  0
                 return parseQueries(address.substring(index + 1));
 254  
             }
 255  
             else
 256  
             {
 257  0
                 buffer.append(address);
 258  0
                 return new OrderedQueryParameters();
 259  
             }
 260  
         }
 261  
         else
 262  
         {
 263  0
             constructAddress(buffer);
 264  0
             return new OrderedQueryParameters();
 265  
         }
 266  
     }
 267  
 
 268  
     private OrderedQueryParameters parseQueries(String queries)
 269  
     {
 270  0
         OrderedQueryParameters map = new OrderedQueryParameters();
 271  0
         StringTokenizer query = new StringTokenizer(queries, AND);
 272  0
         while (query.hasMoreTokens())
 273  
         {
 274  0
             StringTokenizer nameValue = new StringTokenizer(query.nextToken(), EQUALS);
 275  0
             String name = nameValue.nextToken();
 276  0
             String value = null;
 277  0
             if (nameValue.hasMoreTokens())
 278  
             {
 279  0
                 value = nameValue.nextToken();
 280  
             }
 281  0
             map.put(name, value);
 282  0
         }
 283  0
         return map;
 284  
     }
 285  
 
 286  
     private void constructAddress(StringBuffer buffer)
 287  
     {
 288  0
         buffer.append(protocol);
 289  0
         buffer.append(DOTS_SLASHES);
 290  0
         boolean atStart = true;
 291  0
         if (null != user)
 292  
         {
 293  0
             buffer.append(user);
 294  0
             if (null != password)
 295  
             {
 296  0
                 buffer.append(":");
 297  0
                 buffer.append(password);
 298  
             }
 299  0
             buffer.append("@");
 300  0
             atStart = false;
 301  
         }
 302  0
         if (null != host)
 303  
         {
 304  0
             buffer.append(host);
 305  0
             if (null != port)
 306  
             {
 307  0
                 buffer.append(":");
 308  0
                 buffer.append(port);
 309  
             }
 310  0
             atStart = false;
 311  
         }
 312  0
         if (null != path)
 313  
         {
 314  0
             if (! atStart)
 315  
             {
 316  0
                 buffer.append("/");
 317  
             }
 318  0
             buffer.append(path);
 319  
         }
 320  0
     }
 321  
 
 322  
     protected void assertNotUsed()
 323  
     {
 324  0
         if (null != cache.get())
 325  
         {
 326  0
             throw new IllegalStateException("Too late to set values - builder already used");
 327  
         }
 328  0
     }
 329  
 
 330  
     protected void assertAddressConsistent()
 331  
     {
 332  0
         if (null != meta)
 333  
         {
 334  0
             if (null != address)
 335  
             {
 336  0
                 if (address.startsWith(meta + DOTS))
 337  
                 {
 338  0
                     throw new IllegalArgumentException("Meta-protocol '" + meta +
 339  
                             "' should not be specified in the address '" + address +
 340  
                             "' - it is implicit in the element namespace.");
 341  
                 }
 342  0
                 if (null != protocol)
 343  
                 {
 344  0
                     assertProtocolConsistent();
 345  
                 }
 346  
                 else
 347  
                 {
 348  0
                     if (address.indexOf(DOTS_SLASHES) == -1)
 349  
                     {
 350  0
                         throw new IllegalArgumentException("Address '" + address +
 351  
                                 "' does not have a transport protocol prefix " +
 352  
                                 "(omit the meta protocol prefix, '" + meta + DOTS +
 353  
                                 "' - it is implicit in the element namespace).");
 354  
                     }
 355  
                 }
 356  
             }
 357  
         }
 358  
         else
 359  
         {
 360  0
             assertProtocolConsistent();
 361  
         }
 362  0
     }
 363  
 
 364  
     protected void assertProtocolConsistent()
 365  
     {
 366  0
         if (null != protocol && null != address && !address.startsWith(protocol + DOTS_SLASHES))
 367  
         {
 368  0
             throw new IllegalArgumentException("Address '" + address + "' for protocol '" + protocol +
 369  
                     "' should start with " + protocol + DOTS_SLASHES);
 370  
         }
 371  0
     }
 372  
 
 373  
     public String toString()
 374  
     {
 375  0
         return getConstructor();
 376  
     }
 377  
 
 378  
     public boolean equals(Object other)
 379  
     {
 380  0
         if (null == other || !getClass().equals(other.getClass())) return false;
 381  0
         if (this == other) return true;
 382  
 
 383  0
         URIBuilder builder = (URIBuilder) other;
 384  0
         return equal(address, builder.address)
 385  
                 && equal(meta, builder.meta)
 386  
                 && equal(protocol, builder.protocol)
 387  
                 && equal(user, builder.user)
 388  
                 && equal(password, builder.password)
 389  
                 && equal(host, builder.host)
 390  
                 && equal(port, builder.port)
 391  
                 && equal(path, builder.path)
 392  
                 && equal(queryMap, builder.queryMap);
 393  
     }
 394  
 
 395  
     protected static boolean equal(Object a, Object b)
 396  
     {
 397  0
         return ClassUtils.equal(a, b);
 398  
     }
 399  
 
 400  
     public int hashCode()
 401  
     {
 402  0
         return ClassUtils.hash(new Object[]{address, meta, protocol, user, password, host, port, path, queryMap});
 403  
     }
 404  
 
 405  0
     private static class OrderedQueryParameters
 406  
     {
 407  0
         private List<String> names = new ArrayList<String>();
 408  0
         private List<String> values = new ArrayList<String>();
 409  
 
 410  
         public void put(String name, String value)
 411  
         {
 412  0
             names.add(name);
 413  0
             values.add(value);
 414  0
         }
 415  
 
 416  
         /**
 417  
          * Replace the first instance of the given parameter. This method does not make sense under the assumption that
 418  
          * a given parameter name can have multiple values, so here we simply preserve the existing semantics.
 419  
          * @param map A map off the name/value pairs to add/replace in the query string 
 420  
          */
 421  
         public void override(Map map)
 422  
         {
 423  0
             if (null != map)
 424  
             {
 425  
                 // order additional parameters
 426  0
                 Iterator mapNames = new TreeMap(map).keySet().iterator();
 427  0
                 while (mapNames.hasNext())
 428  
                 {
 429  0
                     String name = (String) mapNames.next();
 430  0
                     String value = (String) map.get(name);
 431  
                     
 432  0
                     int pos = names.indexOf(name);
 433  0
                     if (pos >= 0)
 434  
                     {
 435  
                         // Found, so replace
 436  0
                         values.set(pos, value);
 437  
                     }
 438  
                     else 
 439  
                     {       
 440  
                         // Append new value
 441  0
                         names.add(name);
 442  0
                         values.add(value);
 443  
                     }
 444  0
                  }
 445  
             }
 446  0
         }
 447  
 
 448  
         public String toString()
 449  
         {
 450  0
             StringBuffer buffer = new StringBuffer();
 451  
 
 452  0
             boolean first = true;
 453  
 
 454  0
             for (int i = 0; i < names.size(); i++)
 455  
             {
 456  0
                 if (first)
 457  
                 {
 458  0
                     buffer.append(QUERY);
 459  0
                     first = false;
 460  
                 }
 461  
                 else
 462  
                 {
 463  0
                     buffer.append(AND);
 464  
                 }
 465  
                 
 466  0
                 buffer.append(names.get(i));
 467  0
                 String value = values.get(i);
 468  
  
 469  0
                 if (null != value)
 470  
                 {
 471  0
                     buffer.append(EQUALS);
 472  0
                     buffer.append(value);
 473  
                 }
 474  
             }
 475  0
             return buffer.toString();
 476  
         }
 477  
     }
 478  
 
 479  
 }