001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * 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, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.util;
018
019 import java.io.UnsupportedEncodingException;
020 import java.net.URI;
021 import java.net.URISyntaxException;
022 import java.net.URLDecoder;
023 import java.net.URLEncoder;
024 import java.util.ArrayList;
025 import java.util.Collections;
026 import java.util.LinkedHashMap;
027 import java.util.List;
028 import java.util.Map;
029
030 import org.apache.camel.ResolveEndpointFailedException;
031
032 /**
033 * URI utilities.
034 *
035 * @version $Revision: 896014 $
036 */
037 public final class URISupport {
038
039 private static final String CHARSET = "UTF-8";
040
041 private URISupport() {
042 // Helper class
043 }
044
045 @SuppressWarnings("unchecked")
046 public static Map<String, Object> parseQuery(String uri) throws URISyntaxException {
047 // must check for trailing & as the uri.split("&") will ignore those
048 if (uri != null && uri.endsWith("&")) {
049 throw new URISyntaxException(uri, "Invalid uri syntax: Trailing & marker found. "
050 + "Check the uri and remove the trailing & marker.");
051 }
052
053 try {
054 // use a linked map so the parameters is in the same order
055 Map<String, Object> rc = new LinkedHashMap<String, Object>();
056 if (uri != null) {
057 String[] parameters = uri.split("&");
058 for (String parameter : parameters) {
059 int p = parameter.indexOf("=");
060 if (p >= 0) {
061 String name = URLDecoder.decode(parameter.substring(0, p), CHARSET);
062 String value = URLDecoder.decode(parameter.substring(p + 1), CHARSET);
063 rc.put(name, value);
064 } else {
065 rc.put(parameter, null);
066 }
067 }
068 }
069 return rc;
070 } catch (UnsupportedEncodingException e) {
071 URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding");
072 se.initCause(e);
073 throw se;
074 }
075 }
076
077 public static Map<String, Object> parseParameters(URI uri) throws URISyntaxException {
078 String query = uri.getQuery();
079 if (query == null) {
080 String schemeSpecificPart = uri.getSchemeSpecificPart();
081 int idx = schemeSpecificPart.lastIndexOf('?');
082 if (idx < 0) {
083 // return an empty map
084 return new LinkedHashMap<String, Object>(0);
085 } else {
086 query = schemeSpecificPart.substring(idx + 1);
087 }
088 } else {
089 query = stripPrefix(query, "?");
090 }
091 return parseQuery(query);
092 }
093
094 /**
095 * Creates a URI with the given query
096 */
097 public static URI createURIWithQuery(URI uri, String query) throws URISyntaxException {
098 return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(),
099 query, uri.getFragment());
100 }
101
102 public static String stripPrefix(String value, String prefix) {
103 if (value.startsWith(prefix)) {
104 return value.substring(prefix.length());
105 }
106 return value;
107 }
108
109 public static String createQueryString(Map<Object, Object> options) throws URISyntaxException {
110 try {
111 if (options.size() > 0) {
112 StringBuffer rc = new StringBuffer();
113 boolean first = true;
114 for (Object o : options.keySet()) {
115 if (first) {
116 first = false;
117 } else {
118 rc.append("&");
119 }
120
121 String key = (String) o;
122 String value = (String) options.get(key);
123 rc.append(URLEncoder.encode(key, CHARSET));
124 // only append if value is not null
125 if (value != null) {
126 rc.append("=");
127 rc.append(URLEncoder.encode(value, CHARSET));
128 }
129 }
130 return rc.toString();
131 } else {
132 return "";
133 }
134 } catch (UnsupportedEncodingException e) {
135 URISyntaxException se = new URISyntaxException(e.toString(), "Invalid encoding");
136 se.initCause(e);
137 throw se;
138 }
139 }
140
141 /**
142 * Creates a URI from the original URI and the remaining parameters
143 * <p/>
144 * Used by various Camel components
145 */
146 public static URI createRemainingURI(URI originalURI, Map<Object, Object> params) throws URISyntaxException {
147 String s = createQueryString(params);
148 if (s.length() == 0) {
149 s = null;
150 }
151 return createURIWithQuery(originalURI, s);
152 }
153
154 /**
155 * Normalizes the uri by reordering the parameters so they are sorted and thus
156 * we can use the uris for endpoint matching.
157 *
158 * @param uri the uri
159 * @return the normalized uri
160 * @throws URISyntaxException in thrown if the uri syntax is invalid
161 */
162 @SuppressWarnings("unchecked")
163 public static String normalizeUri(String uri) throws URISyntaxException {
164
165 URI u = new URI(UnsafeUriCharactersEncoder.encode(uri));
166 String path = u.getSchemeSpecificPart();
167 String scheme = u.getScheme();
168
169 // not possible to normalize
170 if (scheme == null || path == null) {
171 return uri;
172 }
173
174 // lets trim off any query arguments
175 if (path.startsWith("//")) {
176 path = path.substring(2);
177 }
178 int idx = path.indexOf('?');
179 if (idx > 0) {
180 path = path.substring(0, idx);
181 }
182
183 // in case there are parameters we should reorder them
184 Map parameters = URISupport.parseParameters(u);
185 if (parameters.isEmpty()) {
186 // no parameters then just return
187 return buildUri(scheme, path, null);
188 } else {
189 // reorder parameters a..z
190 List<String> keys = new ArrayList<String>(parameters.keySet());
191 Collections.sort(keys);
192
193 Map<Object, Object> sorted = new LinkedHashMap<Object, Object>(parameters.size());
194 for (String key : keys) {
195 sorted.put(key, parameters.get(key));
196 }
197
198 // build uri object with sorted parameters
199 String query = URISupport.createQueryString(sorted);
200 return buildUri(scheme, path, query);
201 }
202 }
203
204 private static String buildUri(String scheme, String path, String query) {
205 // must include :// to do a correct URI all components can work with
206 return scheme + "://" + path + (query != null ? "?" + query : "");
207 }
208 }