1
2
3
4
5
6
7
8
9
10
11 package org.mule.endpoint;
12
13 import org.mule.api.AnnotatedObject;
14 import org.mule.api.MuleContext;
15 import org.mule.api.endpoint.EndpointException;
16 import org.mule.api.endpoint.EndpointURI;
17 import org.mule.util.ClassUtils;
18
19 import java.io.File;
20 import java.net.URI;
21 import java.net.URLDecoder;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.StringTokenizer;
28 import java.util.TreeMap;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.atomic.AtomicReference;
31
32 import javax.xml.namespace.QName;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 public class URIBuilder implements AnnotatedObject
52 {
53
54 private static final String DOTS = ":";
55 private static final String DOTS_SLASHES = DOTS + "//";
56 private static final String QUERY = "?";
57 private static final String AND = "&";
58 private static final String EQUALS = "=";
59 private static final String BACKSLASH = "\\";
60
61 public static final String META = "meta";
62 public static final String PROTOCOL = "protocol";
63 public static final String USER = "user";
64 public static final String PASSWORD = "password";
65 public static final String HOST = "host";
66 public static final String ADDRESS = "address";
67 public static final String PORT = "port";
68 public static final String PATH = "path";
69
70 public static final String[] ALL_ATTRIBUTES =
71 new String[]{META, PROTOCOL, USER, PASSWORD, HOST, ADDRESS, PORT, PATH};
72
73 public static final String[] PATH_ATTRIBUTES = new String[]{PATH};
74 public static final String[] HOST_ATTRIBUTES = new String[]{HOST};
75 public static final String[] SOCKET_ATTRIBUTES = new String[]{HOST, PORT};
76 public static final String[] USERHOST_ATTRIBUTES = new String[]{USER, HOST};
77
78 public static final String[] ALL_TRANSPORT_ATTRIBUTES = new String[]{USER, PASSWORD, HOST, PORT, PATH};
79
80 private String address;
81 private String meta;
82 private String protocol;
83 private String user;
84 private String password;
85 private String host;
86 private Integer port;
87 private String path;
88 private Map queryMap;
89 private MuleContext muleContext;
90 private final Map<QName, Object> annotations = new ConcurrentHashMap<QName, Object>();
91
92 private AtomicReference<EndpointURI> cache = new AtomicReference<EndpointURI>();
93
94 public URIBuilder()
95 {
96
97 }
98
99 public URIBuilder(MuleContext muleContext)
100 {
101 this.muleContext = muleContext;
102 }
103
104 public URIBuilder(EndpointURI endpointURI)
105 {
106 this(endpointURI.getMuleContext());
107 cache.set(endpointURI);
108 }
109
110 public URIBuilder(String address, MuleContext muleContext)
111 {
112 this(muleContext);
113
114 int dots = address.indexOf(DOTS);
115 int dotsSlashes = address.indexOf(DOTS_SLASHES);
116 if (dots > -1 && dots < dotsSlashes)
117 {
118 this.meta = address.substring(0, dots);
119 address = address.substring(dots+1);
120 }
121 this.address = address;
122 }
123
124 public MuleContext getMuleContext()
125 {
126 return muleContext;
127 }
128
129 public void setMuleContext(MuleContext muleContext)
130 {
131 this.muleContext = muleContext;
132 }
133
134 public void setUser(String user)
135 {
136 assertNotUsed();
137 this.user = user;
138 }
139
140 public void setPassword(String password)
141 {
142 assertNotUsed();
143 this.password = password;
144 }
145
146 public void setHost(String host)
147 {
148 assertNotUsed();
149 this.host = host;
150 }
151
152 public void setAddress(String address)
153 {
154 assertNotUsed();
155 this.address = address;
156 assertAddressConsistent();
157 }
158
159 public void setPort(int port)
160 {
161 assertNotUsed();
162 this.port = new Integer(port);
163 }
164
165 public void setProtocol(String protocol)
166 {
167 assertNotUsed();
168 this.protocol = protocol;
169 assertAddressConsistent();
170 }
171
172 public void setMeta(String meta)
173 {
174 assertNotUsed();
175 this.meta = meta;
176 }
177
178 public void setPath(String path)
179 {
180 assertNotUsed();
181 if (null != path)
182 {
183 if (path.indexOf(DOTS_SLASHES) > -1)
184 {
185 throw new IllegalArgumentException("Unusual syntax in path: '" + path + "' contains " + DOTS_SLASHES);
186 }
187 else if (path.contains(BACKSLASH))
188 {
189
190 try
191 {
192 URI pathUri = new File(path).toURI();
193 path = pathUri.getPath();
194 }
195 catch (Exception ex)
196 {
197 throw new IllegalArgumentException("Illegal syntax in path: " + path, ex);
198 }
199 }
200 }
201 this.path = path;
202 }
203
204 public void setQueryMap(Map queryMap)
205 {
206 assertNotUsed();
207 this.queryMap = queryMap;
208 }
209
210 public EndpointURI getEndpoint()
211 {
212 if (null == cache.get())
213 {
214 try
215 {
216 EndpointURI endpointUri = new MuleEndpointURI(getConstructor(), getEncodedConstructor(), muleContext);
217 cache.compareAndSet(null, endpointUri);
218 }
219 catch (EndpointException e)
220 {
221 throw (IllegalStateException)new IllegalStateException("Bad endpoint configuration").initCause(e);
222 }
223 }
224 return cache.get();
225 }
226
227
228
229
230 protected String getConstructor()
231 {
232 return URLDecoder.decode(getEncodedConstructor());
233 }
234
235 protected String getEncodedConstructor()
236 {
237 StringBuffer buffer = new StringBuffer();
238 appendMeta(buffer);
239 OrderedQueryParameters uriQueries = appendAddress(buffer);
240 uriQueries.override(queryMap);
241 buffer.append(uriQueries.toString());
242 return buffer.toString();
243 }
244
245 private void appendMeta(StringBuffer buffer)
246 {
247 if (null != meta)
248 {
249 buffer.append(meta);
250 buffer.append(DOTS);
251 }
252 }
253
254 private OrderedQueryParameters appendAddress(StringBuffer buffer)
255 {
256 if (null != address)
257 {
258 int index = address.indexOf(QUERY);
259 if (index > -1)
260 {
261 buffer.append(address.substring(0, index));
262 return parseQueries(address.substring(index + 1));
263 }
264 else
265 {
266 buffer.append(address);
267 return new OrderedQueryParameters();
268 }
269 }
270 else
271 {
272 constructAddress(buffer);
273 return new OrderedQueryParameters();
274 }
275 }
276
277 private OrderedQueryParameters parseQueries(String queries)
278 {
279 OrderedQueryParameters map = new OrderedQueryParameters();
280 StringTokenizer query = new StringTokenizer(queries, AND);
281 while (query.hasMoreTokens())
282 {
283 StringTokenizer nameValue = new StringTokenizer(query.nextToken(), EQUALS);
284 String name = nameValue.nextToken();
285 String value = null;
286 if (nameValue.hasMoreTokens())
287 {
288 value = nameValue.nextToken();
289 }
290 map.put(name, value);
291 }
292 return map;
293 }
294
295 private void constructAddress(StringBuffer buffer)
296 {
297 buffer.append(protocol);
298 buffer.append(DOTS_SLASHES);
299 boolean atStart = true;
300 if (null != user)
301 {
302 buffer.append(user);
303 if (null != password)
304 {
305 buffer.append(":");
306 buffer.append(password);
307 }
308 buffer.append("@");
309 atStart = false;
310 }
311 if (null != host)
312 {
313 buffer.append(host);
314 if (null != port)
315 {
316 buffer.append(":");
317 buffer.append(port);
318 }
319 atStart = false;
320 }
321 if (null != path)
322 {
323 if (! atStart)
324 {
325 buffer.append("/");
326 }
327 buffer.append(path);
328 }
329 }
330
331 protected void assertNotUsed()
332 {
333 if (null != cache.get())
334 {
335 throw new IllegalStateException("Too late to set values - builder already used");
336 }
337 }
338
339 protected void assertAddressConsistent()
340 {
341 if (null != meta)
342 {
343 if (null != address)
344 {
345 if (address.startsWith(meta + DOTS))
346 {
347 throw new IllegalArgumentException("Meta-protocol '" + meta +
348 "' should not be specified in the address '" + address +
349 "' - it is implicit in the element namespace.");
350 }
351 if (null != protocol)
352 {
353 assertProtocolConsistent();
354 }
355 else
356 {
357 if (address.indexOf(DOTS_SLASHES) == -1)
358 {
359 throw new IllegalArgumentException("Address '" + address +
360 "' does not have a transport protocol prefix " +
361 "(omit the meta protocol prefix, '" + meta + DOTS +
362 "' - it is implicit in the element namespace).");
363 }
364 }
365 }
366 }
367 else
368 {
369 assertProtocolConsistent();
370 }
371 }
372
373 protected void assertProtocolConsistent()
374 {
375 if (null != protocol && null != address && !address.startsWith(protocol + DOTS_SLASHES))
376 {
377 throw new IllegalArgumentException("Address '" + address + "' for protocol '" + protocol +
378 "' should start with " + protocol + DOTS_SLASHES);
379 }
380 }
381
382 @Override
383 public String toString()
384 {
385 return getConstructor();
386 }
387
388 @Override
389 public boolean equals(Object other)
390 {
391 if (null == other || !getClass().equals(other.getClass())) return false;
392 if (this == other) return true;
393
394 URIBuilder builder = (URIBuilder) other;
395 return equal(address, builder.address)
396 && equal(meta, builder.meta)
397 && equal(protocol, builder.protocol)
398 && equal(user, builder.user)
399 && equal(password, builder.password)
400 && equal(host, builder.host)
401 && equal(port, builder.port)
402 && equal(path, builder.path)
403 && equal(queryMap, builder.queryMap);
404 }
405
406 protected static boolean equal(Object a, Object b)
407 {
408 return ClassUtils.equal(a, b);
409 }
410
411 @Override
412 public int hashCode()
413 {
414 return ClassUtils.hash(new Object[]{address, meta, protocol, user, password, host, port, path, queryMap});
415 }
416
417 private static class OrderedQueryParameters
418 {
419 private List<String> names = new ArrayList<String>();
420 private List<String> values = new ArrayList<String>();
421
422 public void put(String name, String value)
423 {
424 names.add(name);
425 values.add(value);
426 }
427
428
429
430
431
432
433 public void override(Map map)
434 {
435 if (null != map)
436 {
437
438 Iterator mapNames = new TreeMap(map).keySet().iterator();
439 while (mapNames.hasNext())
440 {
441 String name = (String) mapNames.next();
442 String value = (String) map.get(name);
443
444 int pos = names.indexOf(name);
445 if (pos >= 0)
446 {
447
448 values.set(pos, value);
449 }
450 else
451 {
452
453 names.add(name);
454 values.add(value);
455 }
456 }
457 }
458 }
459
460 @Override
461 public String toString()
462 {
463 StringBuffer buffer = new StringBuffer();
464
465 boolean first = true;
466
467 for (int i = 0; i < names.size(); i++)
468 {
469 if (first)
470 {
471 buffer.append(QUERY);
472 first = false;
473 }
474 else
475 {
476 buffer.append(AND);
477 }
478
479 buffer.append(names.get(i));
480 String value = values.get(i);
481
482 if (null != value)
483 {
484 buffer.append(EQUALS);
485 buffer.append(value);
486 }
487 }
488 return buffer.toString();
489 }
490 }
491
492 public final Object getAnnotation(QName name)
493 {
494 return annotations.get(name);
495 }
496
497 public final Map<QName, Object> getAnnotations()
498 {
499 return Collections.unmodifiableMap(annotations);
500 }
501
502 public synchronized final void setAnnotations(Map<QName, Object> newAnnotations)
503 {
504 annotations.clear();
505 annotations.putAll(newAnnotations);
506 }
507 }