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