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