juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject [juneau] branch master updated: Fix invalid field type for SchemaInfo.required.
Date Thu, 15 Mar 2018 22:41:25 GMT
This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new d09b3c1  Fix invalid field type for SchemaInfo.required.
d09b3c1 is described below

commit d09b3c16cb99bd485232593756251ef991ff7f47
Author: JamesBognar <jamesbognar@apache.org>
AuthorDate: Thu Mar 15 18:41:23 2018 -0400

    Fix invalid field type for SchemaInfo.required.
---
 .../apache/juneau/dto/swagger/SchemaInfoTest.java  |   38 +-
 .../org/apache/juneau/dto/swagger/SchemaInfo.java  |   65 +-
 .../org/apache/juneau/json/JsonParserSession.java  |   11 +-
 juneau-doc/src/main/javadoc/overview.html          |    7 +
 .../juneau/examples/rest/StaticFilesResource.java  |    9 +
 .../juneau/examples/rest/files/petstore.html       |  213 ----
 .../juneau/examples/rest/files/petstore.html       |  522 ++++++++++
 .../juneau/examples/rest/files/petstore.json       | 1055 ++++++++++++++++++++
 .../java/org/apache/juneau/rest/RestContext.java   |    2 +-
 9 files changed, 1674 insertions(+), 248 deletions(-)

diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/swagger/SchemaInfoTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/swagger/SchemaInfoTest.java
index 4aac32a..41b52ce 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/swagger/SchemaInfoTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/swagger/SchemaInfoTest.java
@@ -438,19 +438,19 @@ public class SchemaInfoTest {
 	public void testRequired() {
 		SchemaInfo t = new SchemaInfo();
 		
-		t.required(true);
-		assertEquals(true, t.getRequired());
-		assertType(Boolean.class, t.getRequired());
+		t.required("['x']");
+		assertEquals("[x]", t.getRequired().toString());
+		assertType(List.class, t.getRequired());
 		
-		t.required("true");
-		assertEquals(true, t.getRequired());
-		assertType(Boolean.class, t.getRequired());
+		t.required("['x']");
+		assertEquals("[x, x]", t.getRequired().toString());
+		assertType(List.class, t.getRequired());
 
-		t.required(new StringBuilder("true"));
-		assertEquals(true, t.getRequired());
-		assertType(Boolean.class, t.getRequired());
+		t.required(new StringBuilder("['x']"));
+		assertEquals("[x, x, x]", t.getRequired().toString());
+		assertType(List.class, t.getRequired());
 		
-		t.required(null);
+		t.setRequired(null);
 		assertNull(t.getRequired());
 	}
 
@@ -861,14 +861,14 @@ public class SchemaInfoTest {
 			.set("pattern", "k")
 			.set("properties", new AMap<String,Map<String,Object>>().append("l", new AMap<String,Object>().append("l1", 1)))
 			.set("readOnly", true)
-			.set("required", true)
+			.set("required", new ASet<String>().appendAll("x"))
 			.set("title", "m")
 			.set("type", "n")
 			.set("uniqueItems", true)
 			.set("xml", xml().name("o"))
 			.set("$ref", "ref");
 	
-		assertObjectEquals("{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:true,'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':'r [...]
+		assertObjectEquals("{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:['x'],'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':' [...]
 		
 		t
 			.set("default", "a")
@@ -895,14 +895,14 @@ public class SchemaInfoTest {
 			.set("pattern", "k")
 			.set("properties", "{l:{l1:1}}")
 			.set("readOnly", "true")
-			.set("required", "true")
+			.set("required", "['x']")
 			.set("title", "m")
 			.set("type", "n")
 			.set("uniqueItems", "true")
 			.set("xml", "{name:'o'}")
 			.set("$ref", "ref");
 	
-		assertObjectEquals("{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:true,'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':'r [...]
+		assertObjectEquals("{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:['x'],'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':' [...]
 		
 		t
 			.set("default", new StringBuilder("a"))
@@ -929,14 +929,14 @@ public class SchemaInfoTest {
 			.set("pattern", new StringBuilder("k"))
 			.set("properties", new StringBuilder("{l:{l1:1}}"))
 			.set("readOnly", new StringBuilder("true"))
-			.set("required", new StringBuilder("true"))
+			.set("required", new StringBuilder("['x']"))
 			.set("title", new StringBuilder("m"))
 			.set("type", new StringBuilder("n"))
 			.set("uniqueItems", new StringBuilder("true"))
 			.set("xml", new StringBuilder("{name:'o'}"))
 			.set("$ref", new StringBuilder("ref"));
 	
-		assertObjectEquals("{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:true,'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':'r [...]
+		assertObjectEquals("{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:['x'],'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':' [...]
 
 		assertEquals("a", t.get("default", String.class));
 		assertEquals("['b']", t.get("enum", String.class));
@@ -962,7 +962,7 @@ public class SchemaInfoTest {
 		assertEquals("k", t.get("pattern", String.class));
 		assertEquals("{l:{l1:1}}", t.get("properties", String.class));
 		assertEquals("true", t.get("readOnly", String.class));
-		assertEquals("true", t.get("required", String.class));
+		assertEquals("['x']", t.get("required", String.class));
 		assertEquals("m", t.get("title", String.class));
 		assertEquals("n", t.get("type", String.class));
 		assertEquals("true", t.get("uniqueItems", String.class));
@@ -993,7 +993,7 @@ public class SchemaInfoTest {
 		assertType(String.class, t.get("pattern", Object.class));
 		assertType(Map.class, t.get("properties", Object.class));
 		assertType(Boolean.class, t.get("readOnly", Object.class));
-		assertType(Boolean.class, t.get("required", Object.class));
+		assertType(List.class, t.get("required", Object.class));
 		assertType(String.class, t.get("title", Object.class));
 		assertType(String.class, t.get("type", Object.class));
 		assertType(Boolean.class, t.get("uniqueItems", Object.class));
@@ -1005,7 +1005,7 @@ public class SchemaInfoTest {
 		assertNull(t.get(null, Object.class));
 		assertNull(t.get("foo", Object.class));
 		
-		String s = "{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:true,'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':'ref'}";
+		String s = "{format:'i',title:'m',description:'e','default':'a',multipleOf:123.0,maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'k',maxItems:123,minItems:123,uniqueItems:true,maxProperties:123,minProperties:123,required:['x'],'enum':['b'],type:'n',items:{type:'j'},allOf:['d'],properties:{l:{l1:1}},additionalProperties:{c:['c1']},discriminator:'f',readOnly:true,xml:{name:'o'},externalDocs:{url:'h'},example:'g','$ref':'ref'}";
 		assertObjectEquals(s, JsonParser.DEFAULT.parse(s, SchemaInfo.class));
 	}
 }
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java
index 613a618..90438a0 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java
@@ -80,7 +80,6 @@ public class SchemaInfo extends SwaggerElement {
 		exclusiveMaximum,
 		exclusiveMinimum,
 		uniqueItems,
-		required,
 		readOnly;
 	private Object 
 		_default,
@@ -91,6 +90,8 @@ public class SchemaInfo extends SwaggerElement {
 	private List<Object> 
 		_enum,
 		allOf;
+	private List<String>
+		required;
 	private Map<String,Map<String,Object>> properties;
 	private Map<String,Object> additionalProperties;
 
@@ -705,36 +706,78 @@ public class SchemaInfo extends SwaggerElement {
 	/**
 	 * Bean property getter:  <property>required</property>.
 	 * 
+	 * <p>
+	 * The list of required properties.
+	 * 
 	 * @return The property value, or <jk>null</jk> if it is not set.
 	 */
-	public Boolean getRequired() {
+	public List<String> getRequired() {
 		return required;
 	}
 
 	/**
 	 * Bean property setter:  <property>required</property>.
 	 * 
+	 * <p>
+	 * The list of required properties.
+	 * 
 	 * @param value 
 	 * 	The new value for this property.
+	 * 	<br>Valid values:
+	 * 	<ul>
+	 * 		<li><js>"http"</js>
+	 * 		<li><js>"https"</js>
+	 * 		<li><js>"ws"</js>
+	 * 		<li><js>"wss"</js>
+	 * 	</ul>
 	 * 	<br>Can be <jk>null</jk> to unset the property.
 	 * @return This object (for method chaining).
 	 */
-	public SchemaInfo setRequired(Boolean value) {
-		required = value;
+	public SchemaInfo setRequired(Collection<String> value) {
+		required = newList(value);
 		return this;
 	}
 
 	/**
-	 * Same as {@link #setRequired(Boolean)}.
+	 * Adds one or more values to the <property>required</property> property.
 	 * 
-	 * @param value
+	 * <p>
+	 * The list of required properties.
+	 * 
+	 * @param value 
+	 * 	The values to add to this property.
+	 * 	<br>Ignored if <jk>null</jk>.
+	 * @return This object (for method chaining).
+	 */
+	public SchemaInfo addRequired(Collection<String> value) {
+		required = addToList(required, value);
+		return this;
+	}
+
+	/**
+	 * Same as {@link #addRequired(Collection)}.
+	 * 
+	 * @param values 
 	 * 	The new value for this property.
-	 * 	<br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>.
-	 * 	<br>Can be <jk>null</jk> to unset the property.
+	 * 	<br>Valid types:
+	 * 	<ul>
+	 * 		<li><code>Collection&lt;String&gt;</code>
+	 * 		<li><code>String</code> - JSON array representation of <code>Collection&lt;String&gt;</code>
+	 * 			<h5 class='figure'>Example:</h5>
+	 * 			<p class='bcode'>
+	 * 	schemes(<js>"['scheme1','scheme2']"</js>);
+	 * 			</p>
+	 * 		<li><code>String</code> - Individual values
+	 * 			<h5 class='figure'>Example:</h5>
+	 * 			<p class='bcode'>
+	 * 	schemes(<js>"scheme1</js>, <js>"scheme2"</js>);
+	 * 			</p>
+	 * 	</ul>
 	 * @return This object (for method chaining).
 	 */
-	public SchemaInfo required(Object value) {
-		return setRequired(toBoolean(value));
+	public SchemaInfo required(Object...values) {
+		required = addToList(required, values, String.class);
+		return this;
 	}
 
 	/**
@@ -1323,7 +1366,7 @@ public class SchemaInfo extends SwaggerElement {
 			case "uniqueItems": return uniqueItems(value);
 			case "maxProperties": return maxProperties(value);
 			case "minProperties": return minProperties(value);
-			case "required": return required(value);
+			case "required": return setRequired(null).required(value);
 			case "enum": return setEnum(null)._enum(value);
 			case "type": return type(value);
 			case "items": return items(value);
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
index e0d7a86..e244aa6 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -285,9 +285,12 @@ public final class JsonParserSession extends ReaderParserSession {
 		if (c == 't') {
 			parseKeyword("true", r);
 			return Boolean.TRUE;
+		} else if (c == 'f') {
+			parseKeyword("false", r);
+			return Boolean.FALSE;
+		} else {
+			throw new ParseException(loc(r), "Unrecognized syntax.  Expected boolean value, actual=''{0}''", r.read(100));
 		}
-		parseKeyword("false", r);
-		return Boolean.FALSE;
 	}
 
 
@@ -627,9 +630,9 @@ public final class JsonParserSession extends ReaderParserSession {
 			String s = r.read(keyword.length());
 			if (s.equals(keyword))
 				return;
-			throw new ParseException(loc(r), "Unrecognized syntax.");
+			throw new ParseException(loc(r), "Unrecognized syntax.  Expected=''{0}'', Actual=''{1}''", keyword, s);
 		} catch (IndexOutOfBoundsException e) {
-			throw new ParseException(loc(r), "Unrecognized syntax.");
+			throw new ParseException(loc(r), "Unrecognized syntax.  Expected=''{0}'', found end-of-file.", keyword);
 		}
 	}
 
diff --git a/juneau-doc/src/main/javadoc/overview.html b/juneau-doc/src/main/javadoc/overview.html
index dbd8f70..e10f7f1 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -21247,6 +21247,13 @@
 				</ul>
 		</ul>
 		
+		<h5 class='topic w800'>juneau-dto</h5>
+		<ul class='spaced-list'>
+			<li>
+				Fixed bug where Swagger {@link org.apache.juneau.dto.swagger.SchemaInfo#required(Object)} was defined as a boolean
+				instead of a list of strings.
+		</ul>
+		
 		<h5 class='topic w800'>juneau-rest-server</h5>
 		<ul class='spaced-list'>
 			<li>
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
index 0966ccf..d0b9597 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/StaticFilesResource.java
@@ -15,6 +15,8 @@ package org.apache.juneau.examples.rest;
 import static org.apache.juneau.http.HttpMethodName.*;
 
 import org.apache.juneau.dto.*;
+import org.apache.juneau.dto.swagger.*;
+import org.apache.juneau.http.*;
 import org.apache.juneau.microservice.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.widget.*;
@@ -56,4 +58,11 @@ public class StaticFilesResource extends BasicRestServletJena {
 			new LinkString("petstore.html","static/petstore.html")
 		};
 	}
+	
+	@RestMethod(name=GET, path="/swagger", summary="xxx")
+	public Swagger testSwagger() throws Exception {
+		Swagger s = getContext().getClasspathResource(Swagger.class, MediaType.JSON, "files/petstore.json", null);
+		return s;
+	}
+	
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/files/petstore.html b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/files/petstore.html
deleted file mode 100644
index ef4bcbe..0000000
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/files/petstore.html
+++ /dev/null
@@ -1,213 +0,0 @@
-<!--
- ***************************************************************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
- * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
- * with the License.  You may obtain a copy of the License at                                                              *
- *                                                                                                                         *
- *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
- *                                                                                                                         *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
- * specific language governing permissions and limitations under the License.                                              *
- ***************************************************************************************************************************
--->
-
-<html>
-<head>
-
-<style class='text/css'>
-
-.method-button {
-  	display: inline-block;
-	font-size: 14px;
-    font-weight: 700;
-    min-width: 60px;
-    padding: 6px 15px;
-    text-align: center;
-    border-radius: 3px;
-    text-shadow: 0 1px 0 rgba(0,0,0,.1);
-    font-family: Titillium Web,sans-serif;
-    color: #fff;
-}
-	
-.get .method-button        { background: rgb(97,175,254); }
-.put .method-button        { background: rgb(252,161,48); }
-.post .method-button       { background: rgb(73,204,144); }
-.delete .method-button     { background: rgb(249,62,62); }
-.options .method-button    { background: rgb(153,102,255); }
-.deprecated .method-button { background: rgb(170,170,170); }
-.other .method-button      { background: rgb(230,230,0); }
-
-.opblock {
-	margin: 0 0 15px;
-	font-family: Open Sans,sans-serif;	
-    align-items: center;
-    cursor: pointer;
-    border-radius: 4px;
-}
-
-.opblock.get        { background: rgba(97,175,254,.1); border: 1px solid rgb(97,175,254); }
-.opblock.put        { background: rgba(252,161,48,.1); border: 1px solid rgb(252,161,48); }
-.opblock.post       { background: rgba(73,204,144,.1); border: 1px solid rgb(73,204,144); }
-.opblock.options    { background: rgba(153,102,255,.1); border: 1px solid rgb(153,102,255); }
-.opblock.delete     { background: rgba(249,62,62,.1); border: 1px solid rgb(249,62,62); }
-.opblock.deprecated { background: rgba(170,170,170,.1); border: 1px solid rgb(170,170,170); }
-.opblock.other      { background: rgba(230,230,0,0.1); border: 1px solid rgb(230,230,0); }
-
-.opblock-summary {	
-	padding:5px;
-}
-
-.opblock-summary .path {
-	font-size: 16px;
-	word-break: break-all;
-    font-family: Source Code Pro,monospace;
-    font-weight: 600;
-	color: #3b4151;
-    padding:10px;
-}
-
-.opblock.deprecated .opblock-summary .path { color: #8f9199; text-decoration: line-through;}
-.opblock.deprecated .opblock-summary .description { color: #8f9199 }
-
-.opblock-summary .description {
-    font-family: Open Sans,sans-serif;
-    color: #3b4151;
-    font-size: 13px;
-    padding:10px;
-}
-
-.opblock-section-header {
-	padding: 8px 20px;
-    background: hsla(0,0%,100%,.8);
-    box-shadow: 0 1px 2px rgba(0,0,0,.1);
-    font-family: Open Sans,sans-ser;
-    color: #8f9199;      
-}
-
-.opblock-section-header .title {
-    font-size: 14px;
-	font-family: Titillium Web,sans-serif;
-    color: #3b4151;
-    margin: 0px;
-}
-
-.is-open .opblock-contents {
-	display: block;
-}
-.is-closed .opblock-contents {
-	display: none;
-}
-
-.parameters {
-    padding: 0 10px;
-    border-collapse: collapse;
-    margin: 20px;
-	width: 95%;
-}
-
-.parameters th {
-	font-size: 12px;
-    font-weight: 700;
-    padding: 12px 0;
-    text-align: left;
-    border-bottom: 1px solid rgba(59,65,81,.2);
-    font-family: Open Sans,sans-serif;
-    color: #3b4151;
-}
-
-	</style>
-	<script>
-
-	function toggle(e) {
-		var isOpen = e.classList.contains("is-open");
-		if (isOpen) {
-			e.classList.add("is-closed");
-			e.classList.remove("is-open");
-		} else {
-			e.classList.add("is-open");
-			e.classList.remove("is-closed");
-		}
-		window.getSelection().removeAllRanges();
-	}
-	</script>
-</head>
-<body>
-
-<div class='opblock get is-open' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>GET</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-	<div class='opblock-contents'>
-		<div class="table-container">
-			<div class="opblock-section-header">
-				<h4 class="title">Parameters</h4>
-			</div>	
-			<table class="parameters">
-				<tr>
-					<th>Name</th>
-					<th>Description</th>
-				</tr>
-				<tr>
-					<td>foo</td>
-					<td>bar</td>
-				</tr>
-			</table>
-		</div>
-	</div>
-	
-</div>
-
-<div class='opblock put is-closed' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>PUT</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-</div>
-
-<div class='opblock post is-closed' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>POST</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-</div>
-<div class='opblock delete is-closed' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>DELETE</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-</div>
-<div class='opblock deprecated is-closed' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>GET</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-</div>
-
-<div class='opblock other is-closed' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>OTHER</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-</div>
-
-<div class='opblock options is-closed' onclick='toggle(this)'>
-	<div class='opblock-summary'>
-		<span class='method-button'>OPTIONS</span>
-		<span class='path'>/pet</span>
-		<span class='description'>Everything about your Pets</span>
-	</div>
-</div>
-
-
-</body>
-
-</html>
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/files/petstore.html b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/files/petstore.html
new file mode 100644
index 0000000..b3a42d4
--- /dev/null
+++ b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/files/petstore.html
@@ -0,0 +1,522 @@
+<!--
+ ***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+ * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+ * with the License.  You may obtain a copy of the License at                                                              *
+ *                                                                                                                         *
+ *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+ *                                                                                                                         *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+ * specific language governing permissions and limitations under the License.                                              *
+ ***************************************************************************************************************************
+-->
+
+<html>
+<head>
+
+<style class='text/css'>
+
+.method-button {
+  	display: inline-block;
+	font-size: 14px;
+    font-weight: 700;
+    min-width: 60px;
+    padding: 6px 15px;
+    text-align: center;
+    border-radius: 3px;
+    text-shadow: 0 1px 0 rgba(0,0,0,.1);
+    font-family: Titillium Web,sans-serif;
+    color: #fff;
+}
+	
+.get .method-button        { background: rgb(97,175,254); }
+.put .method-button        { background: rgb(252,161,48); }
+.post .method-button       { background: rgb(73,204,144); }
+.delete .method-button     { background: rgb(249,62,62); }
+.options .method-button    { background: rgb(153,102,255); }
+.deprecated .method-button { background: rgb(170,170,170); }
+.other .method-button      { background: rgb(230,230,0); }
+
+.op-block {
+	margin: 0 0 15px;
+	font-family: Open Sans,sans-serif;	
+    align-items: center;
+    cursor: pointer;
+    border-radius: 4px;
+}
+
+.tag-block-summary {
+	margin: 10px 0px;
+	padding: 10px 0px;
+	font-family: Open Sans,sans-serif;	
+    align-items: center;
+    cursor: pointer;
+	border-bottom: 1px solid rgba(59,65,81,.2);
+}
+
+.tag-block-summary .name {
+	font-family: Titillium Web,sans-serif;
+	font-size: 22px;
+	padding: 0px 20px;
+}
+.tag-block-summary .description {
+	font-size: 16px;
+	font-weight: 400;
+	padding: 0px 20px;
+}
+.tag-block-summary .extdocs {
+	float: right;
+	font-size: 14px;
+	font-weight: 400;
+	padding: 0px 20px;
+}
+
+
+.op-block.get        { background: rgba(97,175,254,.1); border: 1px solid rgb(97,175,254); }
+.op-block.put        { background: rgba(252,161,48,.1); border: 1px solid rgb(252,161,48); }
+.op-block.post       { background: rgba(73,204,144,.1); border: 1px solid rgb(73,204,144); }
+.op-block.options    { background: rgba(153,102,255,.1); border: 1px solid rgb(153,102,255); }
+.op-block.delete     { background: rgba(249,62,62,.1); border: 1px solid rgb(249,62,62); }
+.op-block.deprecated { background: rgba(170,170,170,.1); border: 1px solid rgb(170,170,170); }
+.op-block.other      { background: rgba(230,230,0,0.1); border: 1px solid rgb(230,230,0); }
+
+.op-block-summary {	
+	padding:5px;
+}
+
+.op-block-summary .path {
+	font-size: 16px;
+	word-break: break-all;
+    font-family: Source Code Pro,monospace;
+    font-weight: 600;
+	color: #3b4151;
+    padding:10px;
+}
+
+.op-block.deprecated .op-block-summary .path { color: #8f9199; text-decoration: line-through;}
+.op-block.deprecated .op-block-summary .description { color: #8f9199 }
+
+.op-block-summary .description {
+    font-family: Open Sans,sans-serif;
+    color: #3b4151;
+    font-size: 13px;
+    padding:10px;
+}
+
+.op-block-section-header {
+	padding: 8px 20px;
+    background: hsla(0,0%,100%,.8);
+    box-shadow: 0 1px 2px rgba(0,0,0,.1);
+    font-family: Open Sans,sans-ser;
+    color: #8f9199;      
+}
+
+.op-block-section-header .title {
+    font-size: 14px;
+	font-family: Titillium Web,sans-serif;
+    color: #3b4151;
+    margin: 0px;
+}
+
+.is-tag-block-open .tag-block-contents {
+	display: block;
+}
+.is-tag-block-closed .tag-block-contents {
+	display: none;
+}
+
+.is-op-block-open .op-block-contents {
+	display: block;
+}
+.is-op-block-closed .op-block-contents {
+	display: none;
+}
+
+.parameters, .responses {
+    border-collapse: collapse;
+    margin: 20px;
+	width: 95%;
+	border-bottom: 1px solid rgba(59,65,81,.2);
+}
+
+.parameters th, .responses th {
+	font-size: 12px;
+    font-weight: 700;
+    padding: 5px 0;
+    text-align: left;
+    border-bottom: 1px solid rgba(59,65,81,.2);
+    font-family: Open Sans,sans-serif;
+    color: #3b4151;
+}
+
+.parameters td, .responses td {
+    padding: 12px 0;
+    text-align: left;
+    border-bottom: 1px solid rgba(59,65,81,.2);
+}
+
+.parameter-key .in {
+	font-size: 12px;
+    font-family: Source Code Pro,monospace;
+    font-weight: 600;
+    font-style: italic;
+    color: gray;
+}
+
+.parameter-key .name {
+	font-size: 16px;
+    font-weight: 400;
+    font-family: Titillium Web,sans-serif;
+    color: #3b4151;
+}
+
+.parameter-key .name.required {
+    font-weight: 700;
+}
+
+.parameter-key .type {
+    font-size: 12px;
+    padding: 5px 0;
+    font-family: Source Code Pro,monospace;
+    font-weight: 600;
+    color: #3b4151;
+}
+
+.tab {
+    display: flex;
+    margin: 20px 0 10px;
+    padding: 0;
+    list-style: none;
+    cursor: pointer;
+    font-family: Titillium Web,sans-serif;
+    color: #3b4151;
+    font-size: 12px;
+}
+
+.tab li {
+	margin: 0px 2px;
+}
+
+.tab .active {
+    font-weight: 700;
+}
+
+.tab li:not(:last-child):after {
+    content: " -";
+    font-weight:400;
+    box-sizing: inherit;
+}
+
+.tab li:hover {
+    font-weight:700;
+}
+
+.example {
+    font-size: 12px;
+    margin: 0;
+    padding: 5px 20px;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+    hyphens: auto;
+    border-radius: 4px;
+    background: #41444e;
+    overflow-wrap: break-word;
+    font-family: Source Code Pro,monospace;
+    font-weight: 400;
+    color: #fff;
+	display:none;
+}
+
+.example.active {
+	display:block;
+}
+
+.section {
+	font-size: 12px;
+    font-weight: 700;
+    padding: 5px 0;
+    text-align: left;
+    font-family: Open Sans,sans-serif;
+    color: #3b4151;
+}
+
+.headers .name {
+    font-size: 12px;
+    padding: 5px 0;
+    font-family: Source Code Pro,monospace;
+    font-weight: 600;
+    color: #3b4151;
+}
+
+div.headers {
+	margin: 20px 0px;
+}
+
+.headers .type {
+    font-size: 12px;
+    padding: 5px 0;
+    font-family: Source Code Pro,monospace;
+    font-weight: 600;
+    color: #3b4151;
+}
+
+.section-name {
+	display: inline-block;
+	vertical-align: top;
+	margin-right: 20px;
+	font-size: 12px;
+    font-weight: 700;
+    padding: 5px 0;
+    text-align: left;
+    font-family: Open Sans,sans-serif;
+    color: #3b4151;
+	
+}
+
+.section-table {
+	display: inline-block;
+}
+
+.responses .section-table td {
+	padding: 5px 20px 5px 0px;
+	text-align: left;
+    border-bottom: 1px solid rgba(59,65,81,.2);
+}
+
+
+	</style>
+	<script>
+
+	function toggleOpBlock(e) {
+		e = e.parentNode;
+		var isOpen = e.classList.contains("is-op-block-open");
+		if (isOpen) {
+			e.classList.add("is-op-block-closed");
+			e.classList.remove("is-op-block-open");
+		} else {
+			e.classList.add("is-op-block-open");
+			e.classList.remove("is-op-block-closed");
+		}
+		window.getSelection().removeAllRanges();
+	}
+	
+	function toggleTagBlock(e) {
+		e = e.parentNode;
+		var isOpen = e.classList.contains("is-tag-block-open");
+		if (isOpen) {
+			e.classList.add("is-tag-block-closed");
+			e.classList.remove("is-tag-block-open");
+		} else {
+			e.classList.add("is-tag-block-open");
+			e.classList.remove("is-tag-block-closed");
+		}
+		window.getSelection().removeAllRanges();
+	}
+
+	function selectExample(e) {
+		var dataName = e.getAttribute("data-name");
+		var examplesNode = e.parentNode.parentNode;
+		var lis = examplesNode.getElementsByTagName("li");
+		var divs = examplesNode.getElementsByTagName("div");
+		
+		for (var i in lis) {
+			var li = lis[i], div = divs[i];
+			if (li.getAttribute("data-name") == dataName) {
+				li.classList.add("active");
+				div.classList.add("active");
+			} else {
+				li.classList.remove("active");
+				div.classList.remove("active");
+			}
+		}
+	}
+	</script>
+</head>
+<body>
+
+<div class='tag-block is-tag-block-open'>
+	<div class='tag-block-summary' onclick='toggleTagBlock(this)'>
+		<span class='name'>pet</span>
+		<span class='description'>Everything about your Pets</span>
+		<span class='extdocs'><a href='http://apache.org'>Find out more</a></span>
+	</div>
+	<div class='tag-block-contents'>
+		<div class='op-block get is-op-block-open'>
+			<div class='op-block-summary' onclick='toggleOpBlock(this)'>
+				<span class='method-button'>GET</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+			<div class='op-block-contents'>
+				<div class="table-container">
+					<div class="op-block-section-header">
+						<h4 class="title">Parameters</h4>
+					</div>	
+					<table class="parameters">
+						<tr>
+							<th>Name</th>
+							<th>Description</th>
+						</tr>
+						<tr>
+							<td class='parameter-key'>
+								<div class='name required'>body</div>
+								<div class='type'>integer</div>
+								<div class='deprecated'></div>
+								<div class='in'>(body)</div>
+							</td>
+							<td class='parameter-value'>
+								<div class='description'>Pet object that needs to be added to the store.</div>
+								<div class='examples'>
+									<ul class="tab">
+										<li class="active" data-name='model' onclick='selectExample(this)'>Model</li>
+										<li data-name='application/json' onclick='selectExample(this)'>application/json</li>
+										<li data-name='text/xml' onclick='selectExample(this)'>text/xml</li>
+										<li data-name='text/html' onclick='selectExample(this)'>text/html</li>
+										<li data-name='octal/messagepack' onclick='selectExample(this)'>octal/messagepack</li>
+									</ul>
+									<div data-name='model' class='example active'>
+		MODEL
+									</div>
+									<div data-name='application/json' class='example'>
+		APPLICATION/JSON
+									</div>
+									<div data-name='text/xml' class='example'>
+		TEXT/XML
+									</div>
+									<div data-name='text/html' class='example'>
+		TEXT/HTML
+									</div>
+									<div data-name='octal/messagepack' class='example'>
+		OCTAL/MESSAGEPACK
+									</div>
+								</div>
+							</td>
+						</tr>
+					</table>
+					<div class="op-block-section-header">
+						<h4 class="title">Responses</h4>
+					</div>	
+					<table class="responses">
+						<tr>
+							<th>Code</th>
+							<th>Description</th>
+						</tr>
+						<tr>
+							<td>200</td>
+							<td>
+								<div class='description'>Successful operation</div>
+								<div class='examples'>
+									<ul class="tab">
+										<li class="active" data-name='model' onclick='selectExample(this)'>Model</li>
+										<li data-name='application/json' onclick='selectExample(this)'>application/json</li>
+										<li data-name='text/xml' onclick='selectExample(this)'>text/xml</li>
+										<li data-name='text/html' onclick='selectExample(this)'>text/html</li>
+										<li data-name='octal/messagepack' onclick='selectExample(this)'>octal/messagepack</li>
+									</ul>
+									<div data-name='model' class='example active'>
+		MODEL
+									</div>
+									<div data-name='application/json' class='example'>
+		APPLICATION/JSON
+									</div>
+									<div data-name='text/xml' class='example'>
+		TEXT/XML
+									</div>
+									<div data-name='text/html' class='example'>
+		TEXT/HTML
+									</div>
+									<div data-name='octal/messagepack' class='example'>
+		OCTAL/MESSAGEPACK
+									</div>
+								</div>
+								<div class='headers'>
+									<div class='section-name'>Headers:</div>
+									<table class='section-table'>
+										<tr>
+											<th>Name</th>
+											<th>Description</th>
+											<th>Type</th>
+										</tr>
+										<tr>
+											<td class='name'>X-Rate-Limit</td>
+											<td class='description'>Calls per hour allowed by the user</td>
+											<td class='type'>integer</td>
+										</tr>
+									</table>
+								</div>
+							</td>
+						</tr>
+						<tr>
+							<td>400</td>
+							<td>
+								<div class='description'>Bad thing happened</div>
+							</td>
+						</tr>
+					</table>
+				</div>
+			</div>
+			
+		</div>
+		
+		<div class='op-block put is-closed' onclick='toggle(this)'>
+			<div class='op-block-summary'>
+				<span class='method-button'>PUT</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+		</div>
+		
+		<div class='op-block post is-closed' onclick='toggle(this)'>
+			<div class='op-block-summary'>
+				<span class='method-button'>POST</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+		</div>
+	</div>
+</div>
+<div class='tag-block is-tag-block-open'>
+	<div class='tag-block-summary' onclick='toggleTagBlock(this)'>
+		<span class='name'>owner</span>
+		<span class='description'>Everything about your Owner</span>
+		<span class='extdocs'><a href='http://apache.org'>Find out more</a></span>
+	</div>
+	<div class='tag-block-contents'>
+		<div class='op-block delete is-closed' onclick='toggle(this)'>
+			<div class='op-block-summary'>
+				<span class='method-button'>DELETE</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+		</div>
+		<div class='op-block deprecated is-closed' onclick='toggle(this)'>
+			<div class='op-block-summary'>
+				<span class='method-button'>GET</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+		</div>
+		
+		<div class='op-block other is-closed' onclick='toggle(this)'>
+			<div class='op-block-summary'>
+				<span class='method-button'>OTHER</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+		</div>
+		
+		<div class='op-block options is-closed' onclick='toggle(this)'>
+			<div class='op-block-summary'>
+				<span class='method-button'>OPTIONS</span>
+				<span class='path'>/pet</span>
+				<span class='description'>Everything about your Pets</span>
+			</div>
+		</div>
+	</div>
+</div>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/files/petstore.json b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/files/petstore.json
new file mode 100644
index 0000000..54791ba
--- /dev/null
+++ b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/files/petstore.json
@@ -0,0 +1,1055 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+
+{
+	"swagger": "2.0",
+	"info": {
+		"description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
+		"version": "1.0.0",
+		"title": "Swagger Petstore",
+		"termsOfService": "http://swagger.io/terms/",
+		"contact": {
+			"email": "apiteam@swagger.io"
+		},
+		"license": {
+			"name": "Apache 2.0",
+			"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
+		}
+	},
+	"host": "petstore.swagger.io",
+	"basePath": "/v2",
+	"tags": [
+		{
+			"name": "pet",
+			"description": "Everything about your Pets",
+			"externalDocs": {
+				"description": "Find out more",
+				"url": "http://swagger.io"
+			}
+		},
+		{
+			"name": "store",
+			"description": "Access to Petstore orders"
+		},
+		{
+			"name": "user",
+			"description": "Operations about user",
+			"externalDocs": {
+				"description": "Find out more about our store",
+				"url": "http://swagger.io"
+			}
+		}
+	],
+	"schemes": [
+		"http"
+	],
+	"paths": {
+		"/pet": {
+			"post": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Add a new pet to the store",
+				"description": "",
+				"operationId": "addPet",
+				"consumes": [
+					"application/json",
+					"application/xml"
+				],
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "Pet object that needs to be added to the store",
+						"required": true,
+						"schema": {
+							"$ref": "#/definitions/Pet"
+						}
+					}
+				],
+				"responses": {
+					"405": {
+						"description": "Invalid input"
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				]
+			},
+			"put": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Update an existing pet",
+				"description": "",
+				"operationId": "updatePet",
+				"consumes": [
+					"application/json",
+					"application/xml"
+				],
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "Pet object that needs to be added to the store",
+						"required": true,
+						"schema": {
+							"$ref": "#/definitions/Pet"
+						}
+					}
+				],
+				"responses": {
+					"400": {
+						"description": "Invalid ID supplied"
+					},
+					"404": {
+						"description": "Pet not found"
+					},
+					"405": {
+						"description": "Validation exception"
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				]
+			}
+		},
+		"/pet/findByStatus": {
+			"get": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Finds Pets by status",
+				"description": "Multiple status values can be provided with comma separated strings",
+				"operationId": "findPetsByStatus",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "status",
+						"in": "query",
+						"description": "Status values that need to be considered for filter",
+						"required": true,
+						"type": "array",
+						"items": {
+							"type": "string",
+							"enum": [
+								"available",
+								"pending",
+								"sold"
+							],
+							"default": "available"
+						},
+						"collectionFormat": "multi"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"type": "array",
+							"items": {
+								"$ref": "#/definitions/Pet"
+							}
+						}
+					},
+					"400": {
+						"description": "Invalid status value"
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				]
+			}
+		},
+		"/pet/findByTags": {
+			"get": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Finds Pets by tags",
+				"description": "Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+				"operationId": "findPetsByTags",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "tags",
+						"in": "query",
+						"description": "Tags to filter by",
+						"required": true,
+						"type": "array",
+						"items": {
+							"type": "string"
+						},
+						"collectionFormat": "multi"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"type": "array",
+							"items": {
+								"$ref": "#/definitions/Pet"
+							}
+						}
+					},
+					"400": {
+						"description": "Invalid tag value"
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				],
+				"deprecated": true
+			}
+		},
+		"/pet/{petId}": {
+			"get": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Find pet by ID",
+				"description": "Returns a single pet",
+				"operationId": "getPetById",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "petId",
+						"in": "path",
+						"description": "ID of pet to return",
+						"required": true,
+						"type": "integer",
+						"format": "int64"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"$ref": "#/definitions/Pet"
+						}
+					},
+					"400": {
+						"description": "Invalid ID supplied"
+					},
+					"404": {
+						"description": "Pet not found"
+					}
+				},
+				"security": [
+					{
+						"api_key": []
+					}
+				]
+			},
+			"post": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Updates a pet in the store with form data",
+				"description": "",
+				"operationId": "updatePetWithForm",
+				"consumes": [
+					"application/x-www-form-urlencoded"
+				],
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "petId",
+						"in": "path",
+						"description": "ID of pet that needs to be updated",
+						"required": true,
+						"type": "integer",
+						"format": "int64"
+					},
+					{
+						"name": "name",
+						"in": "formData",
+						"description": "Updated name of the pet",
+						"required": false,
+						"type": "string"
+					},
+					{
+						"name": "status",
+						"in": "formData",
+						"description": "Updated status of the pet",
+						"required": false,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"405": {
+						"description": "Invalid input"
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				]
+			},
+			"delete": {
+				"tags": [
+					"pet"
+				],
+				"summary": "Deletes a pet",
+				"description": "",
+				"operationId": "deletePet",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "api_key",
+						"in": "header",
+						"required": false,
+						"type": "string"
+					},
+					{
+						"name": "petId",
+						"in": "path",
+						"description": "Pet id to delete",
+						"required": true,
+						"type": "integer",
+						"format": "int64"
+					}
+				],
+				"responses": {
+					"400": {
+						"description": "Invalid ID supplied"
+					},
+					"404": {
+						"description": "Pet not found"
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				]
+			}
+		},
+		"/pet/{petId}/uploadImage": {
+			"post": {
+				"tags": [
+					"pet"
+				],
+				"summary": "uploads an image",
+				"description": "",
+				"operationId": "uploadFile",
+				"consumes": [
+					"multipart/form-data"
+				],
+				"produces": [
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "petId",
+						"in": "path",
+						"description": "ID of pet to update",
+						"required": true,
+						"type": "integer",
+						"format": "int64"
+					},
+					{
+						"name": "additionalMetadata",
+						"in": "formData",
+						"description": "Additional data to pass to server",
+						"required": false,
+						"type": "string"
+					},
+					{
+						"name": "file",
+						"in": "formData",
+						"description": "file to upload",
+						"required": false,
+						"type": "file"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"$ref": "#/definitions/ApiResponse"
+						}
+					}
+				},
+				"security": [
+					{
+						"petstore_auth": [
+							"write:pets",
+							"read:pets"
+						]
+					}
+				]
+			}
+		},
+		"/store/inventory": {
+			"get": {
+				"tags": [
+					"store"
+				],
+				"summary": "Returns pet inventories by status",
+				"description": "Returns a map of status codes to quantities",
+				"operationId": "getInventory",
+				"produces": [
+					"application/json"
+				],
+				"parameters": [],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"type": "object",
+							"additionalProperties": {
+								"type": "integer",
+								"format": "int32"
+							}
+						}
+					}
+				},
+				"security": [
+					{
+						"api_key": []
+					}
+				]
+			}
+		},
+		"/store/order": {
+			"post": {
+				"tags": [
+					"store"
+				],
+				"summary": "Place an order for a pet",
+				"description": "",
+				"operationId": "placeOrder",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "order placed for purchasing the pet",
+						"required": true,
+						"schema": {
+							"$ref": "#/definitions/Order"
+						}
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"$ref": "#/definitions/Order"
+						}
+					},
+					"400": {
+						"description": "Invalid Order"
+					}
+				}
+			}
+		},
+		"/store/order/{orderId}": {
+			"get": {
+				"tags": [
+					"store"
+				],
+				"summary": "Find purchase order by ID",
+				"description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions",
+				"operationId": "getOrderById",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "orderId",
+						"in": "path",
+						"description": "ID of pet that needs to be fetched",
+						"required": true,
+						"type": "integer",
+						"maximum": 10.0,
+						"minimum": 1.0,
+						"format": "int64"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"$ref": "#/definitions/Order"
+						}
+					},
+					"400": {
+						"description": "Invalid ID supplied"
+					},
+					"404": {
+						"description": "Order not found"
+					}
+				}
+			},
+			"delete": {
+				"tags": [
+					"store"
+				],
+				"summary": "Delete purchase order by ID",
+				"description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors",
+				"operationId": "deleteOrder",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "orderId",
+						"in": "path",
+						"description": "ID of the order that needs to be deleted",
+						"required": true,
+						"type": "integer",
+						"minimum": 1.0,
+						"format": "int64"
+					}
+				],
+				"responses": {
+					"400": {
+						"description": "Invalid ID supplied"
+					},
+					"404": {
+						"description": "Order not found"
+					}
+				}
+			}
+		},
+		"/user": {
+			"post": {
+				"tags": [
+					"user"
+				],
+				"summary": "Create user",
+				"description": "This can only be done by the logged in user.",
+				"operationId": "createUser",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "Created user object",
+						"required": true,
+						"schema": {
+							"$ref": "#/definitions/User"
+						}
+					}
+				],
+				"responses": {
+					"default": {
+						"description": "successful operation"
+					}
+				}
+			}
+		},
+		"/user/createWithArray": {
+			"post": {
+				"tags": [
+					"user"
+				],
+				"summary": "Creates list of users with given input array",
+				"description": "",
+				"operationId": "createUsersWithArrayInput",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "List of user object",
+						"required": true,
+						"schema": {
+							"type": "array",
+							"items": {
+								"$ref": "#/definitions/User"
+							}
+						}
+					}
+				],
+				"responses": {
+					"default": {
+						"description": "successful operation"
+					}
+				}
+			}
+		},
+		"/user/createWithList": {
+			"post": {
+				"tags": [
+					"user"
+				],
+				"summary": "Creates list of users with given input array",
+				"description": "",
+				"operationId": "createUsersWithListInput",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"in": "body",
+						"name": "body",
+						"description": "List of user object",
+						"required": true,
+						"schema": {
+							"type": "array",
+							"items": {
+								"$ref": "#/definitions/User"
+							}
+						}
+					}
+				],
+				"responses": {
+					"default": {
+						"description": "successful operation"
+					}
+				}
+			}
+		},
+		"/user/login": {
+			"get": {
+				"tags": [
+					"user"
+				],
+				"summary": "Logs user into the system",
+				"description": "",
+				"operationId": "loginUser",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "username",
+						"in": "query",
+						"description": "The user name for login",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"name": "password",
+						"in": "query",
+						"description": "The password for login in clear text",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"type": "string"
+						},
+						"headers": {
+							"X-Rate-Limit": {
+								"type": "integer",
+								"format": "int32",
+								"description": "calls per hour allowed by the user"
+							},
+							"X-Expires-After": {
+								"type": "string",
+								"format": "date-time",
+								"description": "date in UTC when token expires"
+							}
+						}
+					},
+					"400": {
+						"description": "Invalid username/password supplied"
+					}
+				}
+			}
+		},
+		"/user/logout": {
+			"get": {
+				"tags": [
+					"user"
+				],
+				"summary": "Logs out current logged in user session",
+				"description": "",
+				"operationId": "logoutUser",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [],
+				"responses": {
+					"default": {
+						"description": "successful operation"
+					}
+				}
+			}
+		},
+		"/user/{username}": {
+			"get": {
+				"tags": [
+					"user"
+				],
+				"summary": "Get user by user name",
+				"description": "",
+				"operationId": "getUserByName",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "username",
+						"in": "path",
+						"description": "The name that needs to be fetched. Use user1 for testing. ",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"200": {
+						"description": "successful operation",
+						"schema": {
+							"$ref": "#/definitions/User"
+						}
+					},
+					"400": {
+						"description": "Invalid username supplied"
+					},
+					"404": {
+						"description": "User not found"
+					}
+				}
+			},
+			"put": {
+				"tags": [
+					"user"
+				],
+				"summary": "Updated user",
+				"description": "This can only be done by the logged in user.",
+				"operationId": "updateUser",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "username",
+						"in": "path",
+						"description": "name that need to be updated",
+						"required": true,
+						"type": "string"
+					},
+					{
+						"in": "body",
+						"name": "body",
+						"description": "Updated user object",
+						"required": true,
+						"schema": {
+							"$ref": "#/definitions/User"
+						}
+					}
+				],
+				"responses": {
+					"400": {
+						"description": "Invalid user supplied"
+					},
+					"404": {
+						"description": "User not found"
+					}
+				}
+			},
+			"delete": {
+				"tags": [
+					"user"
+				],
+				"summary": "Delete user",
+				"description": "This can only be done by the logged in user.",
+				"operationId": "deleteUser",
+				"produces": [
+					"application/xml",
+					"application/json"
+				],
+				"parameters": [
+					{
+						"name": "username",
+						"in": "path",
+						"description": "The name that needs to be deleted",
+						"required": true,
+						"type": "string"
+					}
+				],
+				"responses": {
+					"400": {
+						"description": "Invalid username supplied"
+					},
+					"404": {
+						"description": "User not found"
+					}
+				}
+			}
+		}
+	},
+	"securityDefinitions": {
+		"petstore_auth": {
+			"type": "oauth2",
+			"authorizationUrl": "http://petstore.swagger.io/oauth/dialog",
+			"flow": "implicit",
+			"scopes": {
+				"write:pets": "modify pets in your account",
+				"read:pets": "read your pets"
+			}
+		},
+		"api_key": {
+			"type": "apiKey",
+			"name": "api_key",
+			"in": "header"
+		}
+	},
+	"definitions": {
+		"Order": {
+			"type": "object",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"format": "int64"
+				},
+				"petId": {
+					"type": "integer",
+					"format": "int64"
+				},
+				"quantity": {
+					"type": "integer",
+					"format": "int32"
+				},
+				"shipDate": {
+					"type": "string",
+					"format": "date-time"
+				},
+				"status": {
+					"type": "string",
+					"description": "Order Status",
+					"enum": [
+						"placed",
+						"approved",
+						"delivered"
+					]
+				},
+				"complete": {
+					"type": "boolean",
+					"default": false
+				}
+			},
+			"xml": {
+				"name": "Order"
+			}
+		},
+		"User": {
+			"type": "object",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"format": "int64"
+				},
+				"username": {
+					"type": "string"
+				},
+				"firstName": {
+					"type": "string"
+				},
+				"lastName": {
+					"type": "string"
+				},
+				"email": {
+					"type": "string"
+				},
+				"password": {
+					"type": "string"
+				},
+				"phone": {
+					"type": "string"
+				},
+				"userStatus": {
+					"type": "integer",
+					"format": "int32",
+					"description": "User Status"
+				}
+			},
+			"xml": {
+				"name": "User"
+			}
+		},
+		"Category": {
+			"type": "object",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"format": "int64"
+				},
+				"name": {
+					"type": "string"
+				}
+			},
+			"xml": {
+				"name": "Category"
+			}
+		},
+		"Tag": {
+			"type": "object",
+			"properties": {
+				"id": {
+					"type": "integer",
+					"format": "int64"
+				},
+				"name": {
+					"type": "string"
+				}
+			},
+			"xml": {
+				"name": "Tag"
+			}
+		},
+		"Pet": {
+			"type": "object",
+			"required": [
+				"name",
+				"photoUrls"
+			],
+			"properties": {
+				"id": {
+					"type": "integer",
+					"format": "int64"
+				},
+				"category": {
+					"$ref": "#/definitions/Category"
+				},
+				"name": {
+					"type": "string",
+					"example": "doggie"
+				},
+				"photoUrls": {
+					"type": "array",
+					"xml": {
+						"name": "photoUrl",
+						"wrapped": true
+					},
+					"items": {
+						"type": "string"
+					}
+				},
+				"tags": {
+					"type": "array",
+					"xml": {
+						"name": "tag",
+						"wrapped": true
+					},
+					"items": {
+						"$ref": "#/definitions/Tag"
+					}
+				},
+				"status": {
+					"type": "string",
+					"description": "pet status in the store",
+					"enum": [
+						"available",
+						"pending",
+						"sold"
+					]
+				}
+			},
+			"xml": {
+				"name": "Pet"
+			},
+			"x-examples": {
+				"model": "{foo:}",
+				"application/json": "{foo:'bar'}",
+				"text/xml": "<foo>bar</foo>",
+				"text/html": "<table>foo</table>",
+				"octal-messagepack": "123456ABCDEF"
+			},
+		},
+		"ApiResponse": {
+			"type": "object",
+			"properties": {
+				"code": {
+					"type": "integer",
+					"format": "int32"
+				},
+				"type": {
+					"type": "string"
+				},
+				"message": {
+					"type": "string"
+				}
+			}
+		}
+	},
+	"externalDocs": {
+		"description": "Find out more about Swagger",
+		"url": "http://swagger.io"
+	}
+}
\ No newline at end of file
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 73cb895..f2be599 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -3534,7 +3534,7 @@ public final class RestContext extends BeanContext {
 						return p.parse(in, c);
 					}
 				} catch (ParseException e) {
-					throw new ServletException("Could not parse resource '' as media type '"+mediaType+"'.");
+					throw new ServletException("Could not parse resource '"+name+" as media type '"+mediaType+"'.", e);
 				}
 			}
 			throw new ServletException("Unknown media type '"+mediaType+"'");

-- 
To stop receiving notification emails like this one, please contact
jamesbognar@apache.org.

Mime
View raw message