001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau; 014 015import static org.apache.juneau.common.internal.StringUtils.*; 016import static org.apache.juneau.common.internal.ThrowableUtils.*; 017 018import org.apache.juneau.annotation.*; 019import org.apache.juneau.collections.*; 020import org.apache.juneau.json.*; 021import org.apache.juneau.parser.*; 022 023/** 024 * Represents a URL broken into authority/context-root/servlet-path/path-info parts. 025 * 026 * <p> 027 * A typical request against a URL takes the following form: 028 * <p class='bcode'> 029 * http://host:port/context-root/servlet-path/path-info 030 * | authority | context | resource | path | 031 * +--------------------------------------------------+ 032 * </p> 033 * 034 * <p> 035 * This class allows you to convert URL strings to absolute (e.g. <js>"http://host:port/foo/bar"</js>) or root-relative 036 * (e.g. <js>"/foo/bar"</js>) URLs. 037 * 038 * <h5 class='section'>See Also:</h5><ul> 039 * </ul> 040 */ 041@Bean 042public class UriContext { 043 044 /** 045 * Default URI context. 046 * 047 * <p> 048 * No information about authority, servlet-root, context-root, or path-info is known. 049 */ 050 public static final UriContext DEFAULT = new UriContext(); 051 052 @SuppressWarnings("javadoc") 053 public final String authority, contextRoot, servletPath, pathInfo, parentPath; 054 055 // Lazy-initialized fields. 056 private String aContextRoot, rContextRoot, aServletPath, rResource, aPathInfo, rPath; 057 058 /** 059 * Static creator. 060 * 061 * @param authority 062 * The authority portion of URL (e.g. <js>"http://hostname:port"</js>) 063 * @param contextRoot 064 * The context root of the application (e.g. <js>"/context-root"</js>, or <js>"context-root"</js>) 065 * @param servletPath 066 * The servlet path (e.g. <js>"/servlet-path"</js>, or <js>"servlet-path"</js>) 067 * @param pathInfo 068 * The path info (e.g. <js>"/path-info"</js>, or <js>"path-info"</js>) 069 * @return A new {@link UriContext} object. 070 */ 071 public static UriContext of(String authority, String contextRoot, String servletPath, String pathInfo) { 072 return new UriContext(authority, contextRoot, servletPath, pathInfo); 073 } 074 075 /** 076 * Static creator. 077 * 078 * @param s 079 * The input string. 080 * <br>Example: <js>{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}</js> 081 * @return A new {@link UriContext} object. 082 */ 083 public static UriContext of(String s) { 084 try { 085 return new UriContext(s); 086 } catch (ParseException e) { 087 throw asRuntimeException(e); 088 } 089 } 090 091 /** 092 * Constructor. 093 * 094 * <p> 095 * Leading and trailing slashes are trimmed of all parameters. 096 * 097 * <p> 098 * Any parameter can be <jk>null</jk>. Blanks and nulls are equivalent. 099 * 100 * @param authority 101 * The authority portion of URL (e.g. <js>"http://hostname:port"</js>) 102 * @param contextRoot 103 * The context root of the application (e.g. <js>"/context-root"</js>, or <js>"context-root"</js>) 104 * @param servletPath 105 * The servlet path (e.g. <js>"/servlet-path"</js>, or <js>"servlet-path"</js>) 106 * @param pathInfo 107 * The path info (e.g. <js>"/path-info"</js>, or <js>"path-info"</js>) 108 */ 109 @Beanc 110 public UriContext(@Name("authority") String authority, @Name("contextRoot") String contextRoot, @Name("servletPath") String servletPath, @Name("pathInfo") String pathInfo) { 111 this.authority = nullIfEmpty(trimSlashes(authority)); 112 this.contextRoot = nullIfEmpty(trimSlashes(contextRoot)); 113 this.servletPath = nullIfEmpty(trimSlashes(servletPath)); 114 this.pathInfo = nullIfEmpty(trimSlashes(pathInfo)); 115 this.parentPath = this.pathInfo == null || this.pathInfo.indexOf('/') == -1 ? null 116 : this.pathInfo.substring(0, this.pathInfo.lastIndexOf('/')); 117 } 118 119 /** 120 * Default constructor. 121 * 122 * <p> 123 * All <jk>null</jk> values. 124 */ 125 public UriContext() { 126 this(null, null, null, null); 127 } 128 129 /** 130 * String constructor. 131 * 132 * <p> 133 * Input string is a JSON object with the following format: 134 * <js>{authority:'xxx',contextRoot:'xxx',servletPath:'xxx',pathInfo:'xxx'}</js> 135 * 136 * @param s 137 * The input string. 138 * <br>Example: <js>{authority:'http://localhost:10000',contextRoot:'/myContext',servletPath:'/myServlet',pathInfo:'/foo'}</js> 139 * @throws ParseException 140 * If input string is not a valid JSON object. 141 */ 142 public UriContext(String s) throws ParseException { 143 JsonMap m = JsonMap.ofJson(s); 144 this.authority = nullIfEmpty(trimSlashes(m.getString("authority"))); 145 this.contextRoot = nullIfEmpty(trimSlashes(m.getString("contextRoot"))); 146 this.servletPath = nullIfEmpty(trimSlashes(m.getString("servletPath"))); 147 this.pathInfo = nullIfEmpty(trimSlashes(m.getString("pathInfo"))); 148 this.parentPath = this.pathInfo == null || this.pathInfo.indexOf('/') == -1 ? null 149 : this.pathInfo.substring(0, this.pathInfo.lastIndexOf('/')); 150 } 151 152 /** 153 * Returns the absolute URI of just the authority portion of this URI context. 154 * 155 * <p> 156 * Example: <js>"http://hostname:port"</js> 157 * 158 * <p> 159 * If the authority is null/empty, returns <js>"/"</js>. 160 * 161 * @return 162 * The absolute URI of just the authority portion of this URI context. 163 * Never <jk>null</jk>. 164 */ 165 public String getAbsoluteAuthority() { 166 return authority == null ? "/" : authority; 167 } 168 169 /** 170 * Returns the absolute URI of the context-root portion of this URI context. 171 * 172 * <p> 173 * Example: <js>"http://hostname:port/context-root"</js> 174 * 175 * @return 176 * The absolute URI of the context-root portion of this URI context. 177 * Never <jk>null</jk>. 178 */ 179 public String getAbsoluteContextRoot() { 180 if (aContextRoot == null) { 181 if (authority == null) 182 aContextRoot = getRootRelativeContextRoot(); 183 else 184 aContextRoot = ( 185 contextRoot == null 186 ? authority 187 : (authority + '/' + contextRoot) 188 ); 189 } 190 return aContextRoot; 191 } 192 193 /** 194 * Returns the root-relative URI of the context portion of this URI context. 195 * 196 * <p> 197 * Example: <js>"/context-root"</js> 198 * 199 * @return 200 * The root-relative URI of the context portion of this URI context. 201 * Never <jk>null</jk>. 202 */ 203 public String getRootRelativeContextRoot() { 204 if (rContextRoot == null) 205 rContextRoot = contextRoot == null ? "/" : ('/' + contextRoot); 206 return rContextRoot; 207 } 208 209 /** 210 * Returns the absolute URI of the resource portion of this URI context. 211 * 212 * <p> 213 * Example: <js>"http://hostname:port/context-root/servlet-path"</js> 214 * 215 * @return 216 * The absolute URI of the resource portion of this URI context. 217 * Never <jk>null</jk>. 218 */ 219 public String getAbsoluteServletPath() { 220 if (aServletPath == null) { 221 if (authority == null) 222 aServletPath = getRootRelativeServletPath(); 223 else { 224 if (contextRoot == null) 225 aServletPath = ( 226 servletPath == null 227 ? authority 228 : authority + '/' + servletPath 229 ); 230 else 231 aServletPath = ( 232 servletPath == null 233 ? (authority + '/' + contextRoot) 234 : (authority + '/' + contextRoot + '/' + servletPath) 235 ); 236 } 237 } 238 return aServletPath; 239 } 240 241 /** 242 * Returns the root-relative URI of the resource portion of this URI context. 243 * 244 * <p> 245 * Example: <js>"/context-root/servlet-path"</js> 246 * 247 * @return 248 * The root-relative URI of the resource portion of this URI context. 249 * Never <jk>null</jk>. 250 */ 251 public String getRootRelativeServletPath() { 252 if (rResource == null) { 253 if (contextRoot == null) 254 rResource = ( 255 servletPath == null 256 ? "/" 257 : ('/' + servletPath) 258 ); 259 else 260 rResource = ( 261 servletPath == null 262 ? ('/' + contextRoot) 263 : ('/' + contextRoot + '/' + servletPath) 264 ); 265 } 266 return rResource; 267 } 268 269 /** 270 * Returns the parent of the URL returned by {@link #getAbsoluteServletPath()}. 271 * 272 * @return The parent of the URL returned by {@link #getAbsoluteServletPath()}. 273 */ 274 public String getAbsoluteServletPathParent() { 275 return getParent(getAbsoluteServletPath()); 276 } 277 278 /** 279 * Returns the parent of the URL returned by {@link #getRootRelativeServletPath()}. 280 * 281 * @return The parent of the URL returned by {@link #getRootRelativeServletPath()}. 282 */ 283 public String getRootRelativeServletPathParent() { 284 return getParent(getRootRelativeServletPath()); 285 } 286 287 /** 288 * Returns the absolute URI of the path portion of this URI context. 289 * 290 * <p> 291 * Example: <js>"http://hostname:port/context-root/servlet-path/path-info"</js> 292 * 293 * @return 294 * The absolute URI of the path portion of this URI context. 295 * Never <jk>null</jk>. 296 */ 297 public String getAbsolutePathInfo() { 298 if (aPathInfo == null) { 299 if (authority == null) 300 aPathInfo = getRootRelativePathInfo(); 301 else { 302 if (contextRoot == null) { 303 if (servletPath == null) 304 aPathInfo = ( 305 pathInfo == null 306 ? authority : (authority + '/' + pathInfo) 307 ); 308 else 309 aPathInfo = ( 310 pathInfo == null 311 ? (authority + '/' + servletPath) 312 : (authority + '/' + servletPath + '/' + pathInfo) 313 ); 314 } else { 315 if (servletPath == null) 316 aPathInfo = ( 317 pathInfo == null 318 ? authority + '/' + contextRoot 319 : (authority + '/' + contextRoot + '/' + pathInfo) 320 ); 321 else 322 aPathInfo = ( 323 pathInfo == null 324 ? (authority + '/' + contextRoot + '/' + servletPath) 325 : (authority + '/' + contextRoot + '/' + servletPath + '/' + pathInfo) 326 ); 327 } 328 } 329 } 330 return aPathInfo; 331 } 332 333 /** 334 * Returns the root-relative URI of the path portion of this URI context. 335 * 336 * <p> 337 * Example: <js>"/context-root/servlet-path/path-info"</js> 338 * 339 * @return 340 * The root-relative URI of the path portion of this URI context. 341 * Never <jk>null</jk>. 342 */ 343 public String getRootRelativePathInfo() { 344 if (rPath == null) { 345 if (contextRoot == null) { 346 if (servletPath == null) 347 rPath = ( 348 pathInfo == null 349 ? "/" 350 : ('/' + pathInfo) 351 ); 352 else 353 rPath = ( 354 pathInfo == null 355 ? ('/' + servletPath) 356 : ('/' + servletPath + '/' + pathInfo) 357 ); 358 } else { 359 if (servletPath == null) 360 rPath = ( 361 pathInfo == null 362 ? ('/' + contextRoot) 363 : ('/' + contextRoot + '/' + pathInfo) 364 ); 365 else 366 rPath = ( 367 pathInfo == null 368 ? ('/' + contextRoot + '/' + servletPath) 369 : ('/' + contextRoot + '/' + servletPath + '/' + pathInfo) 370 ); 371 } 372 } 373 return rPath; 374 } 375 376 /** 377 * Returns the parent of the URL returned by {@link #getAbsolutePathInfo()}. 378 * 379 * @return The parent of the URL returned by {@link #getAbsolutePathInfo()}. 380 */ 381 public String getAbsolutePathInfoParent() { 382 return getParent(getAbsolutePathInfo()); 383 } 384 385 /** 386 * Returns the parent of the URL returned by {@link #getRootRelativePathInfo()}. 387 * 388 * @return The parent of the URL returned by {@link #getRootRelativePathInfo()}. 389 */ 390 public String getRootRelativePathInfoParent() { 391 return getParent(getRootRelativePathInfo()); 392 } 393 394 private static String getParent(String uri) { 395 int i = uri.lastIndexOf('/'); 396 if (i <= 1) 397 return "/"; 398 return uri.substring(0, i); 399 } 400 401 @Override /* Object */ 402 public String toString() { 403 return Json5Serializer.DEFAULT.toString(this); 404 } 405}