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.rest; 014 015import static javax.servlet.http.HttpServletResponse.*; 016import static org.apache.juneau.BeanContext.*; 017import static org.apache.juneau.internal.ClassUtils.*; 018import static org.apache.juneau.internal.CollectionUtils.*; 019import static org.apache.juneau.internal.StringUtils.*; 020import static org.apache.juneau.internal.Utils.*; 021import static org.apache.juneau.rest.RestContext.*; 022import static org.apache.juneau.rest.util.RestUtils.*; 023import static org.apache.juneau.httppart.HttpPartType.*; 024 025import java.lang.annotation.*; 026import java.lang.reflect.*; 027import java.util.*; 028import java.util.concurrent.*; 029 030import javax.servlet.http.*; 031 032import org.apache.juneau.*; 033import org.apache.juneau.encoders.*; 034import org.apache.juneau.http.*; 035import org.apache.juneau.http.annotation.*; 036import org.apache.juneau.http.annotation.FormData; 037import org.apache.juneau.http.annotation.Header; 038import org.apache.juneau.http.annotation.Query; 039import org.apache.juneau.http.annotation.Response; 040import org.apache.juneau.httppart.*; 041import org.apache.juneau.httppart.bean.*; 042import org.apache.juneau.internal.*; 043import org.apache.juneau.internal.HttpUtils; 044import org.apache.juneau.parser.*; 045import org.apache.juneau.rest.annotation.*; 046import org.apache.juneau.rest.exception.*; 047import org.apache.juneau.rest.util.RestUtils; 048import org.apache.juneau.rest.util.UrlPathPattern; 049import org.apache.juneau.rest.widget.*; 050import org.apache.juneau.serializer.*; 051import org.apache.juneau.svl.*; 052 053/** 054 * Represents a single Java servlet/resource method annotated with {@link RestMethod @RestMethod}. 055 */ 056public class RestJavaMethod implements Comparable<RestJavaMethod> { 057 private final String httpMethod; 058 private final UrlPathPattern pathPattern; 059 final RestMethodParam[] methodParams; 060 private final RestGuard[] guards; 061 private final RestMatcher[] optionalMatchers; 062 private final RestMatcher[] requiredMatchers; 063 private final RestConverter[] converters; 064 private final RestMethodProperties properties; 065 private final Integer priority; 066 private final RestContext context; 067 final java.lang.reflect.Method method; 068 final PropertyStore propertyStore; 069 final SerializerGroup serializers; 070 final ParserGroup parsers; 071 final EncoderGroup encoders; 072 final HttpPartSerializer partSerializer; 073 final HttpPartParser partParser; 074 final Map<String,Object> 075 defaultRequestHeaders, 076 defaultQuery, 077 defaultFormData; 078 final String defaultCharset; 079 final long maxInput; 080 final BeanContext beanContext; 081 final Map<String,Widget> widgets; 082 final List<MediaType> 083 supportedAcceptTypes, 084 supportedContentTypes; 085 086 final Map<Class<?>,ResponseBeanMeta> responseBeanMetas = new ConcurrentHashMap<>(); 087 final Map<Class<?>,ResponsePartMeta> headerPartMetas = new ConcurrentHashMap<>(); 088 final Map<Class<?>,ResponsePartMeta> bodyPartMetas = new ConcurrentHashMap<>(); 089 final ResponseBeanMeta responseMeta; 090 091 RestJavaMethod(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException { 092 Builder b = new Builder(servlet, method, context); 093 this.context = context; 094 this.method = method; 095 this.httpMethod = b.httpMethod; 096 this.pathPattern = b.pathPattern; 097 this.methodParams = b.methodParams; 098 this.guards = b.guards; 099 this.optionalMatchers = b.optionalMatchers; 100 this.requiredMatchers = b.requiredMatchers; 101 this.converters = b.converters; 102 this.serializers = b.serializers; 103 this.parsers = b.parsers; 104 this.encoders = b.encoders; 105 this.partParser = b.partParser; 106 this.partSerializer = b.partSerializer; 107 this.beanContext = b.beanContext; 108 this.properties = b.properties; 109 this.propertyStore = b.propertyStore; 110 this.defaultRequestHeaders = b.defaultRequestHeaders; 111 this.defaultQuery = b.defaultQuery; 112 this.defaultFormData = b.defaultFormData; 113 this.defaultCharset = b.defaultCharset; 114 this.maxInput = b.maxInput; 115 this.priority = b.priority; 116 this.supportedAcceptTypes = b.supportedAcceptTypes; 117 this.supportedContentTypes = b.supportedContentTypes; 118 this.responseMeta = b.responseMeta; 119 this.widgets = unmodifiableMap(b.widgets); 120 } 121 122 private static final class Builder { 123 String httpMethod, defaultCharset; 124 UrlPathPattern pathPattern; 125 RestMethodParam[] methodParams; 126 RestGuard[] guards; 127 RestMatcher[] optionalMatchers, requiredMatchers; 128 RestConverter[] converters; 129 SerializerGroup serializers; 130 ParserGroup parsers; 131 EncoderGroup encoders; 132 HttpPartParser partParser; 133 HttpPartSerializer partSerializer; 134 BeanContext beanContext; 135 RestMethodProperties properties; 136 PropertyStore propertyStore; 137 Map<String,Object> defaultRequestHeaders, defaultQuery, defaultFormData; 138 long maxInput; 139 Integer priority; 140 Map<String,Widget> widgets; 141 List<MediaType> supportedAcceptTypes, supportedContentTypes; 142 ResponseBeanMeta responseMeta; 143 144 Builder(Object servlet, java.lang.reflect.Method method, RestContext context) throws RestServletException { 145 String sig = method.getDeclaringClass().getName() + '.' + method.getName(); 146 147 try { 148 149 RestMethod m = getAnnotation(RestMethod.class, method); 150 if (m == null) 151 throw new RestServletException("@RestMethod annotation not found on method ''{0}''", sig); 152 153 VarResolver vr = context.getVarResolver(); 154 155 serializers = context.getSerializers(); 156 parsers = context.getParsers(); 157 partSerializer = context.getPartSerializer(); 158 partParser = context.getPartParser(); 159 beanContext = context.getBeanContext(); 160 encoders = context.getEncoders(); 161 properties = new RestMethodProperties(context.getProperties()); 162 defaultCharset = context.getDefaultCharset(); 163 maxInput = context.getMaxInput(); 164 165 if (! m.defaultCharset().isEmpty()) 166 defaultCharset = vr.resolve(m.defaultCharset()); 167 if (! m.maxInput().isEmpty()) 168 maxInput = StringUtils.parseLongWithSuffix(vr.resolve(m.maxInput())); 169 170 HtmlDocBuilder hdb = new HtmlDocBuilder(properties); 171 172 HtmlDoc hd = m.htmldoc(); 173 hdb.process(hd); 174 175 widgets = new HashMap<>(context.getWidgets()); 176 for (Class<? extends Widget> wc : hd.widgets()) { 177 Widget w = beanContext.newInstance(Widget.class, wc); 178 widgets.put(w.getName(), w); 179 hdb.script("INHERIT", "$W{"+w.getName()+".script}"); 180 hdb.style("INHERIT", "$W{"+w.getName()+".style}"); 181 } 182 183 SerializerGroupBuilder sgb = null; 184 ParserGroupBuilder pgb = null; 185 ParserBuilder uepb = null; 186 BeanContextBuilder bcb = null; 187 PropertyStore cps = context.getPropertyStore(); 188 189 Object[] mSerializers = merge(cps.getArrayProperty(REST_serializers, Object.class), m.serializers()); 190 Object[] mParsers = merge(cps.getArrayProperty(REST_parsers, Object.class), m.parsers()); 191 Object[] mPojoSwaps = merge(cps.getArrayProperty(BEAN_pojoSwaps, Object.class), m.pojoSwaps()); 192 Object[] mBeanFilters = merge(cps.getArrayProperty(BEAN_beanFilters, Object.class), m.beanFilters()); 193 194 if (m.serializers().length > 0 || m.parsers().length > 0 || m.properties().length > 0 || m.flags().length > 0 195 || m.beanFilters().length > 0 || m.pojoSwaps().length > 0 || m.bpi().length > 0 196 || m.bpx().length > 0) { 197 sgb = SerializerGroup.create(); 198 pgb = ParserGroup.create(); 199 uepb = Parser.create(); 200 bcb = beanContext.builder(); 201 sgb.append(mSerializers); 202 pgb.append(mParsers); 203 } 204 205 String p = m.path(); 206 if (isEmpty(p)) 207 p = HttpUtils.detectHttpPath(method, true); 208 209 httpMethod = emptyIfNull(firstNonEmpty(m.name(), m.method())).toUpperCase(Locale.ENGLISH); 210 if (httpMethod.isEmpty()) 211 httpMethod = HttpUtils.detectHttpMethod(method, true, "GET"); 212 if ("METHOD".equals(httpMethod)) 213 httpMethod = "*"; 214 215 priority = m.priority(); 216 217 converters = new RestConverter[m.converters().length]; 218 for (int i = 0; i < converters.length; i++) 219 converters[i] = beanContext.newInstance(RestConverter.class, m.converters()[i]); 220 221 guards = new RestGuard[m.guards().length]; 222 for (int i = 0; i < guards.length; i++) 223 guards[i] = beanContext.newInstance(RestGuard.class, m.guards()[i]); 224 225 List<RestMatcher> optionalMatchers = new LinkedList<>(), requiredMatchers = new LinkedList<>(); 226 for (int i = 0; i < m.matchers().length; i++) { 227 Class<? extends RestMatcher> c = m.matchers()[i]; 228 RestMatcher matcher = beanContext.newInstance(RestMatcher.class, c, true, servlet, method); 229 if (matcher.mustMatch()) 230 requiredMatchers.add(matcher); 231 else 232 optionalMatchers.add(matcher); 233 } 234 if (! m.clientVersion().isEmpty()) 235 requiredMatchers.add(new ClientVersionMatcher(context.getClientVersionHeader(), method)); 236 237 this.requiredMatchers = requiredMatchers.toArray(new RestMatcher[requiredMatchers.size()]); 238 this.optionalMatchers = optionalMatchers.toArray(new RestMatcher[optionalMatchers.size()]); 239 240 PropertyStoreBuilder psb = PropertyStore.create().add(properties).set(BEAN_beanFilters, mBeanFilters).set(BEAN_pojoSwaps, mPojoSwaps); 241 for (Property p1 : m.properties()) 242 psb.set(p1.name(), p1.value()); 243 for (String p1 : m.flags()) 244 psb.set(p1, true); 245 this.propertyStore = psb.build(); 246 247 if (sgb != null) { 248 sgb.apply(propertyStore); 249 if (m.bpi().length > 0) { 250 Map<String,String> bpiMap = new LinkedHashMap<>(); 251 for (String s : m.bpi()) { 252 for (String s2 : split(s, ';')) { 253 int i = s2.indexOf(':'); 254 if (i == -1) 255 throw new RestServletException( 256 "Invalid format for @RestMethod(bpi) on method ''{0}''. Must be in the format \"ClassName: comma-delimited-tokens\". \nValue: {1}", sig, s); 257 bpiMap.put(s2.substring(0, i).trim(), s2.substring(i+1).trim()); 258 } 259 } 260 sgb.includeProperties(bpiMap); 261 } 262 if (m.bpx().length > 0) { 263 Map<String,String> bpxMap = new LinkedHashMap<>(); 264 for (String s : m.bpx()) { 265 for (String s2 : split(s, ';')) { 266 int i = s2.indexOf(':'); 267 if (i == -1) 268 throw new RestServletException( 269 "Invalid format for @RestMethod(bpx) on method ''{0}''. Must be in the format \"ClassName: comma-delimited-tokens\". \nValue: {1}", sig, s); 270 bpxMap.put(s2.substring(0, i).trim(), s2.substring(i+1).trim()); 271 } 272 } 273 sgb.excludeProperties(bpxMap); 274 } 275 sgb.beanFilters(mBeanFilters); 276 sgb.pojoSwaps(mPojoSwaps); 277 } 278 279 if (pgb != null) { 280 pgb.apply(propertyStore); 281 pgb.beanFilters(mBeanFilters); 282 pgb.pojoSwaps(mPojoSwaps); 283 } 284 285 if (uepb != null) { 286 uepb.apply(propertyStore); 287 uepb.beanFilters(mBeanFilters); 288 uepb.pojoSwaps(mPojoSwaps); 289 } 290 291 if (bcb != null) { 292 bcb.apply(propertyStore); 293 bcb.pojoSwaps(mPojoSwaps); 294 } 295 296 if (m.properties().length > 0 || m.flags().length > 0) { 297 properties = new RestMethodProperties(properties); 298 for (Property p1 : m.properties()) 299 properties.put(p1.name(), p1.value()); 300 for (String p1 : m.flags()) 301 properties.put(p1, true); 302 } 303 304 if (m.encoders().length > 0) { 305 EncoderGroupBuilder g = EncoderGroup.create().append(IdentityEncoder.INSTANCE); 306 for (Class<?> c : m.encoders()) { 307 try { 308 g.append(c); 309 } catch (Exception e) { 310 throw new RestServletException( 311 "Exception occurred while trying to instantiate ConfigEncoder on method ''{0}'': ''{1}''", sig, c.getSimpleName()).initCause(e); 312 } 313 } 314 encoders = g.build(); 315 } 316 317 defaultRequestHeaders = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 318 for (String s : m.defaultRequestHeaders()) { 319 String[] h = RestUtils.parseKeyValuePair(vr.resolve(s)); 320 if (h == null) 321 throw new RestServletException( 322 "Invalid default request header specified on method ''{0}'': ''{1}''. Must be in the format: ''name[:=]value''", sig, s); 323 defaultRequestHeaders.put(h[0], h[1]); 324 } 325 326 String defaultAccept = vr.resolve(m.defaultAccept()); 327 if (isNotEmpty(defaultAccept)) 328 defaultRequestHeaders.put("Accept", defaultAccept); 329 330 String defaultContentType = vr.resolve(m.defaultContentType()); 331 if (isNotEmpty(defaultContentType)) 332 defaultRequestHeaders.put("Content-Type", defaultAccept); 333 334 defaultQuery = new LinkedHashMap<>(); 335 for (String s : m.defaultQuery()) { 336 String[] h = RestUtils.parseKeyValuePair(vr.resolve(s)); 337 if (h == null) 338 throw new RestServletException( 339 "Invalid default query parameter specified on method ''{0}'': ''{1}''. Must be in the format: ''name[:=]value''", sig, s); 340 defaultQuery.put(h[0], h[1]); 341 } 342 343 defaultFormData = new LinkedHashMap<>(); 344 for (String s : m.defaultFormData()) { 345 String[] h = RestUtils.parseKeyValuePair(vr.resolve(s)); 346 if (h == null) 347 throw new RestServletException( 348 "Invalid default form data parameter specified on method ''{0}'': ''{1}''. Must be in the format: ''name[:=]value''", sig, s); 349 defaultFormData.put(h[0], h[1]); 350 } 351 352 Type[] pt = method.getGenericParameterTypes(); 353 Annotation[][] pa = method.getParameterAnnotations(); 354 for (int i = 0; i < pt.length; i++) { 355 for (Annotation a : pa[i]) { 356 if (a instanceof Header) { 357 Header h = (Header)a; 358 if (h._default().length > 0) 359 defaultRequestHeaders.put(firstNonEmpty(h.name(), h.value()), parseAnything(joinnl(h._default()))); 360 } else if (a instanceof Query) { 361 Query q = (Query)a; 362 if (q._default().length > 0) 363 defaultQuery.put(firstNonEmpty(q.name(), q.value()), parseAnything(joinnl(q._default()))); 364 } else if (a instanceof FormData) { 365 FormData f = (FormData)a; 366 if (f._default().length > 0) 367 defaultFormData.put(firstNonEmpty(f.name(), f.value()), parseAnything(joinnl(f._default()))); 368 } 369 } 370 } 371 372 pathPattern = new UrlPathPattern(p); 373 374 if (sgb != null) 375 serializers = sgb.build(); 376 if (pgb != null) 377 parsers = pgb.build(); 378 if (uepb != null && partParser instanceof Parser) { 379 Parser pp = (Parser)partParser; 380 partParser = (HttpPartParser)pp.builder().apply(uepb.getPropertyStore()).build(); 381 } 382 if (bcb != null) 383 beanContext = bcb.build(); 384 385 supportedAcceptTypes = 386 m.produces().length > 0 387 ? immutableList(MediaType.forStrings(resolveVars(vr, m.produces()))) 388 : serializers.getSupportedMediaTypes(); 389 supportedContentTypes = 390 m.consumes().length > 0 391 ? immutableList(MediaType.forStrings(resolveVars(vr, m.consumes()))) 392 : parsers.getSupportedMediaTypes(); 393 394 methodParams = context.findParams(method, false, pathPattern); 395 396 if (hasAnnotation(Response.class, method)) 397 responseMeta = ResponseBeanMeta.create(method, serializers.getPropertyStore()); 398 399 // Need this to access methods in anonymous inner classes. 400 setAccessible(method, true); 401 } catch (RestServletException e) { 402 throw e; 403 } catch (Exception e) { 404 throw new RestServletException("Exception occurred while initializing method ''{0}''", sig).initCause(e); 405 } 406 } 407 } 408 409 ResponseBeanMeta getResponseBeanMeta(Object o) { 410 if (o == null) 411 return null; 412 Class<?> c = o.getClass(); 413 ResponseBeanMeta rbm = responseBeanMetas.get(c); 414 if (rbm == null) { 415 rbm = ResponseBeanMeta.create(c, serializers.getPropertyStore()); 416 if (rbm == null) 417 rbm = ResponseBeanMeta.NULL; 418 responseBeanMetas.put(c, rbm); 419 } 420 if (rbm == ResponseBeanMeta.NULL) 421 return null; 422 return rbm; 423 } 424 425 ResponsePartMeta getResponseHeaderMeta(Object o) { 426 if (o == null) 427 return null; 428 Class<?> c = o.getClass(); 429 ResponsePartMeta pm = headerPartMetas.get(c); 430 if (pm == null) { 431 ResponseHeader a = c.getAnnotation(ResponseHeader.class); 432 if (a != null) { 433 HttpPartSchema schema = HttpPartSchema.create(a); 434 HttpPartSerializer serializer = createPartSerializer(schema.getSerializer(), serializers.getPropertyStore(), partSerializer); 435 pm = new ResponsePartMeta(HEADER, schema, serializer); 436 } 437 if (pm == null) 438 pm = ResponsePartMeta.NULL; 439 headerPartMetas.put(c, pm); 440 } 441 if (pm == ResponsePartMeta.NULL) 442 return null; 443 return pm; 444 } 445 446 ResponsePartMeta getResponseBodyMeta(Object o) { 447 if (o == null) 448 return null; 449 Class<?> c = o.getClass(); 450 ResponsePartMeta pm = bodyPartMetas.get(c); 451 if (pm == null) { 452 ResponseBody a = c.getAnnotation(ResponseBody.class); 453 if (a != null) { 454 HttpPartSchema schema = HttpPartSchema.create(a); 455 HttpPartSerializer serializer = createPartSerializer(schema.getSerializer(), serializers.getPropertyStore(), partSerializer); 456 pm = new ResponsePartMeta(BODY, schema, serializer); 457 } 458 if (pm == null) 459 pm = ResponsePartMeta.NULL; 460 bodyPartMetas.put(c, pm); 461 } 462 if (pm == ResponsePartMeta.NULL) 463 return null; 464 return pm; 465 } 466 467 /** 468 * Returns <jk>true</jk> if this Java method has any guards or matchers. 469 */ 470 boolean hasGuardsOrMatchers() { 471 return (guards.length != 0 || requiredMatchers.length != 0 || optionalMatchers.length != 0); 472 } 473 474 /** 475 * Returns the HTTP method name (e.g. <js>"GET"</js>). 476 */ 477 String getHttpMethod() { 478 return httpMethod; 479 } 480 481 /** 482 * Returns the path pattern for this method. 483 */ 484 String getPathPattern() { 485 return pathPattern.toString(); 486 } 487 488 /** 489 * Returns <jk>true</jk> if the specified request object can call this method. 490 */ 491 boolean isRequestAllowed(RestRequest req) { 492 for (RestGuard guard : guards) { 493 req.setJavaMethod(method); 494 if (! guard.isRequestAllowed(req)) 495 return false; 496 } 497 return true; 498 } 499 500 /** 501 * Workhorse method. 502 * 503 * @param pathInfo The value of {@link HttpServletRequest#getPathInfo()} (sorta) 504 * @return The HTTP response code. 505 */ 506 int invoke(String pathInfo, RestRequest req, RestResponse res) throws Throwable { 507 508 String[] patternVals = pathPattern.match(pathInfo); 509 if (patternVals == null) 510 return SC_NOT_FOUND; 511 512 String remainder = null; 513 if (patternVals.length > pathPattern.getVars().length) 514 remainder = patternVals[pathPattern.getVars().length]; 515 for (int i = 0; i < pathPattern.getVars().length; i++) 516 req.getPathMatch().put(pathPattern.getVars()[i], patternVals[i]); 517 req.getPathMatch().pattern(pathPattern.getPatternString()).remainder(remainder); 518 519 RequestProperties requestProperties = new RequestProperties(req.getVarResolverSession(), properties); 520 521 req.init(this, requestProperties); 522 res.init(this, requestProperties); 523 524 // Class-level guards 525 for (RestGuard guard : context.getGuards()) 526 if (! guard.guard(req, res)) 527 return SC_UNAUTHORIZED; 528 529 // If the method implements matchers, test them. 530 for (RestMatcher m : requiredMatchers) 531 if (! m.matches(req)) 532 return SC_PRECONDITION_FAILED; 533 if (optionalMatchers.length > 0) { 534 boolean matches = false; 535 for (RestMatcher m : optionalMatchers) 536 matches |= m.matches(req); 537 if (! matches) 538 return SC_PRECONDITION_FAILED; 539 } 540 541 context.preCall(req, res); 542 543 Object[] args = new Object[methodParams.length]; 544 for (int i = 0; i < methodParams.length; i++) { 545 try { 546 args[i] = methodParams[i].resolve(req, res); 547 } catch (RestException e) { 548 throw e; 549 } catch (Exception e) { 550 throw new BadRequest(e, 551 "Invalid data conversion. Could not convert {0} ''{1}'' to type ''{2}'' on method ''{3}.{4}''.", 552 methodParams[i].getParamType().name(), methodParams[i].getName(), methodParams[i].getType(), method.getDeclaringClass().getName(), method.getName() 553 ); 554 } 555 } 556 557 try { 558 559 for (RestGuard guard : guards) 560 if (! guard.guard(req, res)) 561 return SC_OK; 562 563 Object output; 564 try { 565 output = method.invoke(context.getResource(), args); 566 if (res.getStatus() == 0) 567 res.setStatus(200); 568 if (! method.getReturnType().equals(Void.TYPE)) { 569 if (output != null || ! res.getOutputStreamCalled()) 570 res.setOutput(output); 571 } 572 } catch (InvocationTargetException e) { 573 Throwable e2 = e.getTargetException(); // Get the throwable thrown from the doX() method. 574 res.setStatus(500); 575 if (getResponseBodyMeta(e2) != null || getResponseBeanMeta(e2) != null) { 576 res.setOutput(e2); 577 } else { 578 throw e; 579 } 580 } 581 582 context.postCall(req, res); 583 584 if (res.hasOutput()) 585 for (RestConverter converter : converters) 586 res.setOutput(converter.convert(req, res.getOutput())); 587 588 } catch (IllegalArgumentException e) { 589 throw new BadRequest(e, 590 "Invalid argument type passed to the following method: ''{0}''.\n\tArgument types: {1}", 591 method.toString(), getReadableClassNames(args) 592 ); 593 } catch (InvocationTargetException e) { 594 Throwable e2 = e.getTargetException(); // Get the throwable thrown from the doX() method. 595 if (e2 instanceof RestException) 596 throw (RestException)e2; 597 if (e2 instanceof ParseException) 598 throw new BadRequest(e2); 599 if (e2 instanceof InvalidDataConversionException) 600 throw new BadRequest(e2); 601 throw e2; 602 } 603 return SC_OK; 604 } 605 606 @Override /* Object */ 607 public String toString() { 608 return "SimpleMethod: name=" + httpMethod + ", path=" + pathPattern.getPatternString(); 609 } 610 611 /* 612 * compareTo() method is used to keep SimpleMethods ordered in the RestCallRouter list. 613 * It maintains the order in which matches are made during requests. 614 */ 615 @Override /* Comparable */ 616 public int compareTo(RestJavaMethod o) { 617 int c; 618 619 c = priority.compareTo(o.priority); 620 if (c != 0) 621 return c; 622 623 c = pathPattern.compareTo(o.pathPattern); 624 if (c != 0) 625 return c; 626 627 c = compare(o.requiredMatchers.length, requiredMatchers.length); 628 if (c != 0) 629 return c; 630 631 c = compare(o.optionalMatchers.length, optionalMatchers.length); 632 if (c != 0) 633 return c; 634 635 c = compare(o.guards.length, guards.length); 636 if (c != 0) 637 return c; 638 639 return 0; 640 } 641 642 /** 643 * Bean property getter: <property>serializers</property>. 644 * 645 * @return The value of the <property>serializers</property> property on this bean, or <jk>null</jk> if it is not set. 646 */ 647 public SerializerGroup getSerializers() { 648 return serializers; 649 } 650 651 /** 652 * Bean property getter: <property>parsers</property>. 653 * 654 * @return The value of the <property>parsers</property> property on this bean, or <jk>null</jk> if it is not set. 655 */ 656 public ParserGroup getParsers() { 657 return parsers; 658 } 659 660 /** 661 * Bean property getter: <property>partSerializer</property>. 662 * 663 * @return The value of the <property>partSerializer</property> property on this bean, or <jk>null</jk> if it is not set. 664 */ 665 public HttpPartSerializer getPartSerializer() { 666 return partSerializer; 667 } 668 669 /** 670 * Bean property getter: <property>partParser</property>. 671 * 672 * @return The value of the <property>partParser</property> property on this bean, or <jk>null</jk> if it is not set. 673 */ 674 public HttpPartParser getPartParser() { 675 return partParser; 676 } 677 678 @Override /* Object */ 679 public boolean equals(Object o) { 680 if (! (o instanceof RestJavaMethod)) 681 return false; 682 return (compareTo((RestJavaMethod)o) == 0); 683 } 684 685 @Override /* Object */ 686 public int hashCode() { 687 return method.hashCode(); 688 } 689 690 //----------------------------------------------------------------------------------------------------------------- 691 // Utility methods. 692 //----------------------------------------------------------------------------------------------------------------- 693 static String[] resolveVars(VarResolver vr, String[] in) { 694 String[] out = new String[in.length]; 695 for (int i = 0; i < in.length; i++) 696 out[i] = vr.resolve(in[i]); 697 return out; 698 } 699 700 static HttpPartSerializer createPartSerializer(Class<? extends HttpPartSerializer> c, PropertyStore ps, HttpPartSerializer _default) { 701 HttpPartSerializer hps = ClassUtils.newInstance(HttpPartSerializer.class, c, true, ps); 702 return hps == null ? _default : hps; 703 } 704}