You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/10/08 14:31:14 UTC

[juneau] branch master updated: PetStore updates. Minor bug fixes.

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 205d20f  PetStore updates.  Minor bug fixes.
205d20f is described below

commit 205d20f8237fe5892256f07cf124d3fe700047cf
Author: JamesBognar <ja...@apache.org>
AuthorDate: Mon Oct 8 10:31:01 2018 -0400

    PetStore updates.  Minor bug fixes.
---
 .../java/org/apache/juneau/internal/DateUtils.java |   12 +
 .../org/apache/juneau/internal/StringUtils.java    |    2 +-
 juneau-examples/juneau-examples-rest/pom.xml       |   10 +
 .../examples/rest/petstore/ExpiresAfter.java}      |   33 +-
 .../examples/rest/petstore/InvalidUsername.java    |    2 +-
 .../juneau/examples/rest/petstore/Order.java       |   66 --
 .../juneau/examples/rest/petstore/PetCreate.java   |   54 -
 .../juneau/examples/rest/petstore/PetStore.java    |  561 +++++------
 .../examples/rest/petstore/PetStoreResource.java   |  407 +++-----
 .../examples/rest/petstore/PetStoreService.java    |  259 +++++
 .../rest/petstore/{ => dto}/CreateOrder.java       |   58 +-
 .../{PetUpdate.java => dto/CreatePet.java}         |   78 +-
 .../juneau/examples/rest/petstore/dto/Order.java   |  138 +++
 .../rest/petstore/{ => dto}/OrderStatus.java       |    2 +-
 .../examples/rest/petstore/{ => dto}/Pet.java      |  135 ++-
 .../rest/petstore/{ => dto}/PetStatus.java         |    2 +-
 .../examples/rest/petstore/{ => dto}/PetTag.java   |    2 +-
 .../examples/rest/petstore/{ => dto}/Species.java  |   44 +-
 .../examples/rest/petstore/dto/UpdatePet.java      |   97 ++
 .../examples/rest/petstore/{ => dto}/User.java     |   79 +-
 .../rest/petstore/{ => dto}/UserStatus.java        |    2 +-
 .../src/main/resources/META-INF/persistence.xml    |   34 +
 .../rest/petstore/PetStoreResource_orig.json       | 1048 --------------------
 .../apache/juneau/examples/rest/petstore/Pets.json |   24 -
 .../examples/rest/petstore/{ => init}/Orders.json  |    0
 .../rest/petstore/{Users.json => init/Pets.json}   |   14 +-
 .../petstore/{Species.json => init/Users.json}     |    9 +-
 .../rest/client/remote/RemoteMethodMeta.java       |    2 +-
 .../apache/juneau/rest/BasicRestCallHandler.java   |    2 +
 .../java/org/apache/juneau/rest/RestContext.java   |   38 +
 .../java/org/apache/juneau/rest/RestResponse.java  |   10 +-
 .../java/org/apache/juneau/rest/RestServlet.java   |   21 +-
 .../juneau/rest/util/FinishablePrintWriter.java    |    5 +-
 33 files changed, 1290 insertions(+), 1960 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java
index e052602..36a6da8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/DateUtils.java
@@ -125,6 +125,18 @@ public final class DateUtils {
 	}
 
 	/**
+	 * Parses an ISO8601 string and converts it to a {@link Date}.
+	 *
+	 * @param s The string to parse.
+	 * @return The parsed value, or <jk>null</jk> if the string was <jk>null</jk> or empty.
+	 */
+	public static Date parseISO8601(String s) {
+		if (isEmpty(s))
+			return null;
+		return DatatypeConverter.parseDateTime(toValidISO8601DT(s)).getTime();
+	}
+
+	/**
 	 * Formats the given date according to the RFC 1123 pattern.
 	 *
 	 * @param date The date to format.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index 1a4f169..1beabc9 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -1882,7 +1882,7 @@ public final class StringUtils {
 	}
 
 	/**
-	 * Same as {@link #urlEncode(String)} except only excapes characters that absolutely need to be escaped.
+	 * Same as {@link #urlEncode(String)} except only escapes characters that absolutely need to be escaped.
 	 *
 	 * @param s The string to escape.
 	 * @return The encoded string, or <jk>null</jk> if input is <jk>null</jk>.
diff --git a/juneau-examples/juneau-examples-rest/pom.xml b/juneau-examples/juneau-examples-rest/pom.xml
index a33df7e..a614d52 100644
--- a/juneau-examples/juneau-examples-rest/pom.xml
+++ b/juneau-examples/juneau-examples-rest/pom.xml
@@ -77,6 +77,16 @@
 			<groupId>junit</groupId>
 			<artifactId>junit</artifactId>
 		</dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-core</artifactId>
+            <version>5.0.9.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-entitymanager</artifactId>
+            <version>5.0.9.Final</version>
+        </dependency>
 	</dependencies>
 
 	<build>
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Tags.json b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/ExpiresAfter.java
similarity index 73%
rename from juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Tags.json
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/ExpiresAfter.java
index 1a1caef..d7a2f37 100644
--- a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Tags.json
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/ExpiresAfter.java
@@ -2,7 +2,7 @@
 // * 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                                                              *
+// * with the License.  You may obtain a copy of the License at                                                              * 
 // *                                                                                                                         *
 // *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
 // *                                                                                                                         *
@@ -10,13 +10,26 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
+package org.apache.juneau.examples.rest.petstore;
 
-[
-	{id:101, name:'friendly'},
-	{id:102, name:'special'},
-	{id:103, name:'intelligent'},
-	{id:104, name:'mean'},
-	{id:105, name:'easy to care for'},
-	{id:106, name:'smells nice'},
-	{id:107, name:'loyal'}
-]
+import java.util.*;
+
+import org.apache.juneau.http.annotation.*;
+
+@ResponseHeader(
+	name="X-Expires-After",
+	type="string",
+	format="date-time",
+	description="Date in UTC when token expires",
+	example="2012-10-21"
+)
+public class ExpiresAfter {
+	private final Calendar c;
+	public ExpiresAfter(Date d) {
+		this.c = new GregorianCalendar();
+		c.setTime(d);
+	}
+	public Calendar toCalendar() {
+		return c;
+	}
+}
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/InvalidUsername.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/InvalidUsername.java
index a48f0c9..5553ec9 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/InvalidUsername.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/InvalidUsername.java
@@ -26,6 +26,6 @@ public class InvalidUsername extends BadRequest {
 	 * Constructor.
 	 */
 	public InvalidUsername() {
-		super("Invalid username provided.  Must be 8 or more characters or digits.");
+		super("Invalid username provided.  Must be between 3 and 8 characters or digits.");
 	}
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Order.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Order.java
deleted file mode 100644
index 8876e8d..0000000
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Order.java
+++ /dev/null
@@ -1,66 +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.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
-
-import java.util.*;
-
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.html.annotation.*;
-import org.apache.juneau.transforms.*;
-
-@Bean(fluentSetters=true, properties="id,petId,shipDate,status")
-@Example("{id:123,petId:456,shipDate:'2012-12-21',status:'APPROVED'}")
-public class Order {
-	private long id, petId;
-	private Date shipDate;
-	private OrderStatus status;
-
-	public long getId() {
-		return id;
-	}
-
-	@Html(link="servlet:/store/order/{id}")
-	public Order id(long id) {
-		this.id = id;
-		return this;
-	}
-
-	@Html(link="servlet:/pet/{id}")
-	public long getPetId() {
-		return petId;
-	}
-
-	public Order petId(long petId) {
-		this.petId = petId;
-		return this;
-	}
-
-	@Swap(DateSwap.ISO8601D.class)
-	public Date getShipDate() {
-		return shipDate;
-	}
-
-	public Order shipDate(Date shipDate) {
-		this.shipDate = shipDate;
-		return this;
-	}
-
-	public OrderStatus getStatus() {
-		return status;
-	}
-
-	public Order status(OrderStatus status) {
-		this.status = status;
-		return this;
-	}
-}
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetCreate.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetCreate.java
deleted file mode 100644
index 19428ae..0000000
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetCreate.java
+++ /dev/null
@@ -1,54 +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.                                              *
-// ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
-
-import org.apache.juneau.annotation.*;
-
-/**
- * Bean for creating {@link Pet} objects.
- */
-public class PetCreate {
-
-	private final String name;
-	private final float price;
-	private final String species;
-	private final String[] tags;
-
-	@BeanConstructor(properties="name,price,species,tags")
-	public PetCreate(String name, float price, String species, String[] tags) {
-		this.name = name;
-		this.price = price;
-		this.species = species;
-		this.tags = tags;
-	}
-
-	public static PetCreate example() {
-		return new PetCreate("Doggie", 9.99f, "Dog", new String[] {"friendly","cute"});
-	}
-
-	public String getName() {
-		return name;
-	}
-
-	public float getPrice() {
-		return price;
-	}
-
-	public String getSpecies() {
-		return species;
-	}
-
-	public String[] getTags() {
-		return tags;
-	}
-}
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStore.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStore.java
index b9a4208..24c0cfc 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStore.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStore.java
@@ -12,348 +12,233 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.examples.rest.petstore;
 
-import java.io.*;
+import static org.apache.juneau.http.HttpMethodName.*;
+
 import java.util.*;
-import java.util.concurrent.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.json.*;
-import org.apache.juneau.transform.*;
-import org.apache.juneau.utils.*;
+import org.apache.juneau.examples.rest.petstore.dto.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.client.remote.*;
+import org.apache.juneau.rest.exception.*;
+import org.apache.juneau.rest.response.*;
 
 /**
- * Pet store database application.
+ * Defines the interface for both the server-side and client-side pet store application.
  */
-public class PetStore {
-
-	// Our "databases".
-	IdMap<Long,Pet> petDb = IdMap.createLongMap(Pet.class);
-	IdMap<Long,Species> speciesDb = IdMap.createLongMap(Species.class);
-	IdMap<Long,Order> orderDb = IdMap.createLongMap(Order.class);
-	IdMap<Long,PetTag> tagDb = IdMap.createLongMap(PetTag.class);
-	ConcurrentHashMap<String,User> userDb = new ConcurrentHashMap<>();
-
-	public PetStore init() throws Exception {
-
-		// Load our databases from local JSON files.
-
-		JsonParser parser = JsonParser.create().build();
-
-		// Note that these must be loaded in the specified order to prevent IdNotFound exceptions.
-		for (Species s : parser.parse(getStream("Species.json"), Species[].class))
-			add(s);
-		for (PetTag t : parser.parse(getStream("Tags.json"), PetTag[].class))
-			add(t);
-
-		parser = parser.builder().pojoSwaps(new CategorySwap(), new TagSwap()).build();
-		for (Pet p : parser.parse(getStream("Pets.json"), Pet[].class))
-			add(p);
-
-		parser = parser.builder().pojoSwaps(new PetSwap()).build();
-		for (Order o : parser.parse(getStream("Orders.json"), Order[].class))
-			add(o);
-
-		for (User u : parser.parse(getStream("Users.json"), User[].class))
-			add(u);
-
-		return this;
-	}
-
-	private InputStream getStream(String fileName) {
-		return getClass().getResourceAsStream(fileName);
-	}
-
-	public Pet getPet(long id) throws IdNotFound {
-		Pet value = petDb.get(id);
-		if (value == null)
-			throw new IdNotFound(id, Pet.class);
-		return value;
-	}
-
-	public Species getSpecies(long id) throws IdNotFound {
-		Species value = speciesDb.get(id);
-		if (value == null)
-			throw new IdNotFound(id, Pet.class);
-		return value;
-	}
-
-	public Species getSpecies(String name) throws IdNotFound {
-		for (Species value : speciesDb.values())
-			if (value.getName().equals(name))
-				return value;
-		throw new InvalidSpecies();
-	}
-
-	public Order getOrder(long id) throws IdNotFound {
-		Order value = orderDb.get(id);
-		if (value == null)
-			throw new IdNotFound(id, Pet.class);
-		return value;
-	}
-
-	public PetTag getTag(long id) throws IdNotFound {
-		PetTag value =  tagDb.get(id);
-		if (value == null)
-			throw new IdNotFound(id, Pet.class);
-		return value;
-	}
-
-	public PetTag getTag(String name) throws InvalidTag  {
-		for (PetTag value : tagDb.values())
-			if (value.getName().equals(name))
-				return value;
-		throw new InvalidTag();
-	}
-
-	public User getUser(String username) throws InvalidUsername, IdNotFound  {
-		assertValidUsername(username);
-		for (User user : userDb.values())
-			if (user.getUsername().equals(username))
-				return user;
-		throw new IdNotFound(username, User.class);
-	}
-
-	public boolean isValid(String username, String password) {
-		for (User user : userDb.values())
-			if (user.getUsername().equals(username))
-				return user.getPassword().equals(password);
-		return false;
-	}
-
-	public Collection<Pet> getPets() {
-		return petDb.values();
-	}
-
-	public Collection<Species> getCategories() {
-		return speciesDb.values();
-	}
-
-	public Collection<Order> getOrders() {
-		return orderDb.values();
-	}
-
-	public Collection<PetTag> getTags() {
-		return tagDb.values();
-	}
-
-	public Collection<User> getUsers() {
-		return userDb.values();
-	}
-
-	public Pet add(Pet value) throws IdConflict {
-		if (value.getId() == 0)
-			value.id(petDb.nextId());
-		else
-			petDb.lbId(value.getId());
-		Pet old = petDb.putIfAbsent(value.getId(), value);
-		if (old != null)
-			throw new IdConflict(value.getId(), Pet.class);
-		return value;
-	}
-
-	public Species add(Species value) throws IdConflict {
-		if (value.getId() == 0)
-			value.id(speciesDb.nextId());
-		Species old = speciesDb.putIfAbsent(value.getId(), value);
-		if (old != null)
-			throw new IdConflict(value.getId(), Species.class);
-		return value;
-	}
-
-	public Order add(Order value) throws IdConflict {
-		if (value.getId() == 0)
-			value.id(orderDb.nextId());
-		Order old = orderDb.putIfAbsent(value.getId(), value);
-		if (old != null)
-			throw new IdConflict(value.getId(), Order.class);
-		return value;
-	}
-
-	public PetTag add(PetTag value) throws IdConflict {
-		if (value.getId() == 0)
-			value.id(tagDb.nextId());
-		PetTag old = tagDb.putIfAbsent(value.getId(), value);
-		if (old != null)
-			throw new IdConflict(value.getId(), PetTag.class);
-		return value;
-	}
-
-	public User add(User value) throws IdConflict, InvalidUsername {
-		assertValidUsername(value.getUsername());
-		User old = userDb.putIfAbsent(value.getUsername(), value);
-		if (old != null)
-			throw new IdConflict(value.getUsername(), User.class);
-		return value;
-	}
-
-	public Pet create(PetCreate pc) {
-		Pet p = new Pet();
-		p.name(pc.getName());
-		p.price(pc.getPrice());
-		p.species(getSpecies(pc.getSpecies()));
-		p.tags(getTags(pc.getTags()));
-		p.status(PetStatus.AVAILABLE);
-		return add(p);
-	}
-
-	public Pet update(PetUpdate pu) throws IdNotFound {
-		Pet p = petDb.get(pu.getId());
-		if (p == null)
-			throw new IdNotFound(pu.getId(), Pet.class);
-		p.name(pu.getName());
-		p.price(pu.getPrice());
-		p.species(getSpecies(pu.getSpecies()));
-		p.tags(getTags(pu.getTags()));
-		p.status(pu.getStatus());
-		return p;
-	}
-
-	public Pet update(Pet pet) {
-		petDb.put(pet.getId(), pet);
-		return pet;
-	}
-
-	public Order create(CreateOrder c) {
-		Order o = new Order();
-		o.petId(c.getPetId());
-		o.shipDate(c.getShipDate());
-		o.status(OrderStatus.PLACED);
-		return add(o);
-	}
-
-	private PetTag[] getTags(String[] tags) {
-		if (tags == null)
-			return null;
-		PetTag[] l = new PetTag[tags.length];
-		for (int i = 0; i < tags.length; i++)
-			l[i]= getOrCreateTag(tags[i]);
-		return l;
-	}
-
-	private PetTag getOrCreateTag(String name) {
-		for (PetTag t : tagDb.values())
-			if (t.getName().equals(name))
-				return t;
-		return add(new PetTag().name(name));
-	}
-
-	public Order update(Order value) throws IdNotFound {
-		Order old = orderDb.replace(value.getId(), value);
-		if (old == null)
-			throw new IdNotFound(value.getId(), Order.class);
-		return value;
-	}
-
-	public PetTag update(PetTag value) throws IdNotFound, InvalidTag {
-		assertValidTag(value.getName());
-		PetTag old = tagDb.replace(value.getId(), value);
-		if (old == null)
-			throw new IdNotFound(value.getId(), PetTag.class);
-		return value;
-	}
-
-	public User update(User value) throws IdNotFound, InvalidUsername {
-		assertValidUsername(value.getUsername());
-		User old = userDb.replace(value.getUsername(), value);
-		if (old == null)
-			throw new IdNotFound(value.getUsername(), User.class);
-		return value;
-	}
-
-	public void removePet(long id) throws IdNotFound {
-		petDb.remove(getPet(id).getId());
-	}
-
-	public void removeCategory(long id) throws IdNotFound {
-		speciesDb.remove(getSpecies(id).getId());
-	}
-
-	public void removeOrder(long id) throws IdNotFound {
-		orderDb.remove(getOrder(id).getId());
-	}
-
-	public void removeTag(long id) throws IdNotFound {
-		tagDb.remove(getTag(id).getId());
-	}
-
-	public void removeUser(String username) throws IdNotFound {
-		userDb.remove(getUser(username).getUsername());
-	}
-
-	private void assertValidUsername(String username) throws InvalidUsername {
-		if (username == null || ! username.matches("[\\w\\d]{8,}"))
-			throw new InvalidUsername();
-	}
-
-	private void assertValidTag(String tag) throws InvalidTag {
-		if (tag == null || ! tag.matches("[\\w\\d]{1,8}"))
-			throw new InvalidTag();
-	}
-
-	public Collection<Pet> getPetsByStatus(PetStatus[] status) {
-		List<Pet> list = new ArrayList<>();
-		for (Pet p : petDb.values())
-			if (p.hasStatus(status))
-				list.add(p);
-		return list;
-	}
-
-	public Collection<Pet> getPetsByTags(String[] tags) throws InvalidTag {
-		for (String tag : tags)
-			assertValidTag(tag);
-		List<Pet> list = new ArrayList<>();
-		for (Pet p : petDb.values())
-			if (p.hasTag(tags))
-				list.add(p);
-		return list;
-	}
-
-	public Map<PetStatus,Integer> getInventory() {
-		Map<PetStatus,Integer> m = new LinkedHashMap<>();
-		for (Pet p : petDb.values()) {
-			PetStatus ps = p.getStatus();
-			if (! m.containsKey(ps))
-				m.put(ps, 1);
-			else
-				m.put(ps, m.get(ps) + 1);
-		}
-		return m;
-	}
-
-	//-----------------------------------------------------------------------------------------------------------------
-	// Helper beans
-	//-----------------------------------------------------------------------------------------------------------------
-
-	public class CategorySwap extends PojoSwap<Species,String> {
-		@Override
-		public String swap(BeanSession bs, Species o) throws Exception {
-			return o.getName();
-		}
-		@Override
-		public Species unswap(BeanSession bs, String o, ClassMeta<?> hint) throws Exception {
-			return getSpecies(o);
-		}
-	}
-
-	public class TagSwap extends PojoSwap<PetTag,String> {
-		@Override
-		public String swap(BeanSession bs, PetTag o) throws Exception {
-			return o.getName();
-		}
-		@Override
-		public PetTag unswap(BeanSession bs, String o, ClassMeta<?> hint) throws Exception {
-			return getTag(o);
-		}
-	}
-
-	public class PetSwap extends PojoSwap<Pet,Long> {
-		@Override
-		public Long swap(BeanSession bs, Pet o) throws Exception {
-			return o.getId();
-		}
-		@Override
-		public Pet unswap(BeanSession bs, Long o, ClassMeta<?> hint) throws Exception {
-			return petDb.get(o);
-		}
-	}
+@RemoteResource(path="/petstore")
+public interface PetStore {
+
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+	// Pets
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+	@RemoteMethod(method=GET, path="/pet")
+	public Collection<Pet> getPets() throws NotAcceptable;
+
+	@RemoteMethod(path="/pet/{petId}") /* method inferred from method name */
+	public Pet getPet(
+		@Path(
+			name="petId",
+			description="ID of pet to return",
+			example="123"
+		)
+		long petId
+	) throws IdNotFound, NotAcceptable;
+
+	@RemoteMethod /* method and path inferred from method name */
+	public long postPet(
+		@Body(
+			description="Pet object to add to the store"
+		) CreatePet pet
+	) throws IdConflict, NotAcceptable, UnsupportedMediaType;
+
+	@RemoteMethod(method=PUT, path="/pet/{petId}")
+	public Ok updatePet(
+		@Body(
+			description="Pet object that needs to be added to the store"
+		) UpdatePet pet
+	) throws IdNotFound, NotAcceptable, UnsupportedMediaType;
+
+	@RemoteMethod(method=GET, path="/pet/findByStatus")
+	public Collection<Pet> findPetsByStatus(
+		@Query(
+			name="status",
+			description="Status values that need to be considered for filter.",
+			required=true,
+			type="array",
+			collectionFormat="csv",
+			items=@Items(
+				type="string",
+				_enum="AVAILABLE,PENDING,SOLD",
+				_default="AVAILABLE"
+			),
+			example="AVALIABLE,PENDING"
+		)
+		PetStatus[] status
+	) throws NotAcceptable;
+
+	@RemoteMethod(method=GET, path="/pet/findByTags")
+	@Deprecated
+	public Collection<Pet> findPetsByTags(
+		@Query(
+			name="tags",
+			description="Tags to filter by",
+			required=true,
+			example="['tag1','tag2']"
+		)
+		String[] tags
+	) throws InvalidTag, NotAcceptable;
+
+	@RemoteMethod(method=DELETE, path="/pet/{petId}")
+	public Ok deletePet(
+		@Header(
+			name="api_key",
+			description="Security API key",
+			required=true,
+			example="foobar"
+		)
+		String apiKey,
+		@Path(
+			name="petId",
+			description="Pet id to delete",
+			example="123"
+		)
+		long petId
+	) throws IdNotFound, NotAcceptable;
+
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+	// Orders
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+	@RemoteMethod(method=GET, path="/store/order")
+	public Collection<Order> getOrders() throws NotAcceptable;
+
+	@RemoteMethod(method=GET, path="/store/order/{orderId}")
+	public Order getOrder(
+		@Path(
+			name="orderId",
+			description="ID of order to fetch",
+			maximum="1000",
+			minimum="1",
+			example="123"
+		)
+		long orderId
+	) throws InvalidId, IdNotFound, NotAcceptable;
+
+	@RemoteMethod(method=POST, path="/store/order")
+	public long placeOrder(
+		@FormData(
+			name="petId",
+			description="Pet ID"
+		)
+		long petId,
+		@FormData(
+			name="username",
+			description="The username of the user creating the order"
+		)
+		String username
+	) throws IdConflict, NotAcceptable, UnsupportedMediaType;
+
+	@RemoteMethod(method=DELETE, path="/store/order/{orderId}")
+	public Ok deleteOrder(
+		@Path(
+			name="orderId",
+			description="ID of the order that needs to be deleted",
+			minimum="1",
+			example="5"
+		)
+		long orderId
+	) throws InvalidId, IdNotFound, NotAcceptable;
+
+	@RemoteMethod(method=GET, path="/store/inventory")
+	public Map<PetStatus,Integer> getStoreInventory() throws NotAcceptable;
+
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+	// Users
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+	@RemoteMethod(method=GET, path="/user")
+	public Collection<User> getUsers() throws NotAcceptable;
+
+	@RemoteMethod(method=GET, path="/user/{username}")
+	public User getUser(
+		@Path(
+			name="username",
+			description="The name that needs to be fetched. Use user1 for testing."
+		)
+		String username
+	) throws InvalidUsername, IdNotFound, NotAcceptable;
+
+	@RemoteMethod
+	public Ok postUser(
+		@Body(
+			description="Created user object"
+		)
+		User user
+	) throws InvalidUsername, IdConflict, NotAcceptable, UnsupportedMediaType;
+
+	@RemoteMethod(method=POST, path="/user/createWithArray")
+	public Ok createUsers(
+		@Body(
+			description="List of user objects"
+		)
+		User[] users
+	) throws InvalidUsername, IdConflict, NotAcceptable, UnsupportedMediaType;
+
+	@RemoteMethod(method=PUT, path="/user/{username}")
+	public Ok updateUser(
+		@Path(
+			name="username",
+			description="Name that need to be updated"
+		)
+		String username,
+		@Body(
+			description="Updated user object"
+		)
+		User user
+	) throws InvalidUsername, IdNotFound, NotAcceptable, UnsupportedMediaType;
+
+	@RemoteMethod(method=DELETE, path="/user/{username}")
+	public Ok deleteUser(
+		@Path(
+			name="username",
+			description="The name that needs to be deleted"
+		)
+		String username
+	) throws InvalidUsername, IdNotFound, NotAcceptable;
+
+	@RemoteMethod(method=GET, path="/user/login")
+	public Ok login(
+		@Query(
+			name="username",
+			description="The username for login",
+			required=true,
+			example="myuser"
+		)
+		String username,
+		@Query(
+			name="password",
+			description="The password for login in clear text",
+			required=true,
+			example="abc123"
+		)
+		String password,
+		@ResponseHeader(
+			name="X-Rate-Limit",
+			type="integer",
+			format="int32",
+			description="Calls per hour allowed by the user.",
+			example="123"
+		)
+		Value<Integer> rateLimit,
+		Value<ExpiresAfter> expiresAfter,
+		RestRequest req,
+		RestResponse res
+	) throws InvalidLogin, NotAcceptable;
+
+	@RemoteMethod(method=GET, path="/user/logout")
+	public Ok logout() throws NotAcceptable;
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
index f612025..42bde51 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreResource.java
@@ -23,9 +23,9 @@ import java.util.Map;
 
 import org.apache.juneau.*;
 import org.apache.juneau.dto.html5.*;
+import org.apache.juneau.examples.rest.petstore.dto.*;
 import org.apache.juneau.http.annotation.*;
-import org.apache.juneau.http.annotation.Body;
-import org.apache.juneau.http.annotation.Header;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.jsonschema.annotation.*;
 import org.apache.juneau.microservice.*;
 import org.apache.juneau.rest.*;
@@ -55,6 +55,7 @@ import org.apache.juneau.rest.converters.*;
 		navlinks={
 			"up: request:/..",
 			"options: servlet:/?method=OPTIONS",
+			"init: servlet:/init",
 			"$W{ContentTypeMenuItem}",
 			"$W{ThemeMenuItem}",
 			"source: $C{Source/gitHub}/org/apache/juneau/examples/rest/petstore/$R{servletClassSimple}.java"
@@ -122,14 +123,14 @@ import org.apache.juneau.rest.converters.*;
 	),
 	staticFiles={"htdocs:htdocs"}
 )
-public class PetStoreResource extends BasicRestServletJena {
+public class PetStoreResource extends BasicRestServletJena implements PetStore {
 	private static final long serialVersionUID = 1L;
 
-	private PetStore store;
+	private PetStoreService store;
 
 	@RestHook(INIT)
-	public void initDatabase(RestContextBuilder builder) throws Exception {
-		store = new PetStore().init();
+	public void startup(RestContextBuilder builder) throws Exception {
+		store = new PetStoreService();
 	}
 
 	@RestMethod(
@@ -146,9 +147,52 @@ public class PetStoreResource extends BasicRestServletJena {
 	}
 
 	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+	// Initialization
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+	@RestMethod(
+		summary="Initialize database form entry page"
+	)
+	public Div getInit() {
+		return div(
+			form("servlet:/init").method(POST).target("buf").children(
+				table(
+					tr(
+						th("Initialize petstore database:"),
+						td(input("radio").name("init-method").value("direct").checked(true), "direct", input("radio").name("init-method").value("rest"), "rest"),
+						td(button("submit", "Submit").style("float:right").onclick("scrolling=true"))
+					)
+				)
+			),
+			br(),
+			iframe().id("buf").name("buf").style("width:800px;height:600px;").onload("window.parent.scrolling=false;"),
+			script("text/javascript",
+				"var scrolling = false;",
+				"function scroll() { if (scrolling) { document.getElementById('buf').contentWindow.scrollBy(0,50); } setTimeout('scroll()',200); } ",
+				"scroll();"
+			)
+		);
+	}
+
+	@RestMethod(
+		summary="Initialize database"
+	)
+	public void postInit(
+		@FormData("init-method") String initMethod,
+		RestResponse res
+	) throws Exception {
+		res.setHeader("Content-Encoding", "identity");
+		if ("direct".equals(initMethod))
+			store.initDirect(res.getDirectWriter("text/plain"));
+		else
+			store.initViaRest(res.getDirectWriter("text/plain"));
+	}
+
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 	// Pets
 	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
+	@Override /* PetStore */
 	@RestMethod(
 		name=GET,
 		path="/pet",
@@ -177,6 +221,7 @@ public class PetStoreResource extends BasicRestServletJena {
 		return store.getPets();
 	}
 
+	@Override /* PetStore */
 	@RestMethod(
 		name=GET,
 		path="/pet/{petId}",
@@ -189,18 +234,11 @@ public class PetStoreResource extends BasicRestServletJena {
 			}
 		)
 	)
-	public Pet getPet(
-			@Path(
-				name="petId",
-				description="ID of pet to return",
-				example="123"
-			)
-			long petId
-		) throws IdNotFound, NotAcceptable {
-
+	public Pet getPet(long petId) throws IdNotFound, NotAcceptable {
 		return store.getPet(petId);
 	}
 
+	@Override /* PetStore */
 	@RestMethod(
 		summary="Add a new pet to the store",
 		swagger=@MethodSwagger(
@@ -210,14 +248,11 @@ public class PetStoreResource extends BasicRestServletJena {
 			}
 		)
 	)
-	public Ok postPet(
-			@Body(description="Pet object to add to the store") PetCreate pet
-		) throws IdConflict, NotAcceptable, UnsupportedMediaType {
-
-		store.create(pet);
-		return OK;
+	public long postPet(CreatePet pet) throws IdConflict, NotAcceptable, UnsupportedMediaType {
+		return store.create(pet).getId();
 	}
 
+	@Override /* PetStore */
 	@RestMethod(
 		name=PUT,
 		path="/pet/{petId}",
@@ -229,14 +264,67 @@ public class PetStoreResource extends BasicRestServletJena {
 			}
 		)
 	)
-	public Ok updatePet(
-			@Body(description="Pet object that needs to be added to the store") PetUpdate pet
-		) throws IdNotFound, NotAcceptable, UnsupportedMediaType {
-
+	public Ok updatePet(UpdatePet pet) throws IdNotFound, NotAcceptable, UnsupportedMediaType {
 		store.update(pet);
 		return OK;
 	}
 
+	@Override /* PetStore */
+	@RestMethod(
+		name=GET,
+		path="/pet/findByStatus",
+		summary="Finds Pets by status",
+		description="Multiple status values can be provided with comma separated strings.",
+		swagger=@MethodSwagger(
+			tags="pet",
+			value={
+				"security:[{petstore_auth:['write:pets','read:pets']}]"
+			}
+		)
+	)
+	public Collection<Pet> findPetsByStatus(PetStatus[] status) throws NotAcceptable {
+		return store.getPetsByStatus(status);
+	}
+
+	@Override /* PetStore */
+	@RestMethod(
+		name=GET,
+		path="/pet/findByTags",
+		summary="Finds Pets by tags",
+		description="Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
+		swagger=@MethodSwagger(
+			tags="pet",
+			value={
+				"security:[ { petstore_auth:[ 'write:pets','read:pets' ] } ]"
+			}
+		)
+	)
+	@Deprecated
+	public Collection<Pet> findPetsByTags(String[] tags) throws InvalidTag, NotAcceptable {
+		return store.getPetsByTags(tags);
+	}
+
+	@Override /* PetStore */
+	@RestMethod(
+		name=DELETE,
+		path="/pet/{petId}",
+		summary="Deletes a pet",
+		swagger=@MethodSwagger(
+			tags="pet",
+			value={
+				"security:[ { petstore_auth:[ 'write:pets','read:pets' ] } ]"
+			}
+		)
+	)
+	public Ok deletePet(String apiKey, long petId) throws IdNotFound, NotAcceptable {
+		store.removePet(petId);
+		return OK;
+	}
+
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+	// Pets - extra methods
+	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
 	@RestMethod(
 		name=GET,
 		path="/pet/{petId}/edit",
@@ -288,7 +376,7 @@ public class PetStoreResource extends BasicRestServletJena {
 					),
 					tr(
 						th("Tags:"),
-						td(input().name("tags").type("text").value(PetTag.asString(pet.getTags()))),
+						td(input().name("tags").type("text").value(StringUtils.join(pet.getTags(), ','))),
 						td(new Tooltip("&#x2753;", "Arbitrary textual tags (comma-delimited).", br(), "e.g. 'fluffy,friendly'"))
 					),
 					tr(
@@ -312,131 +400,6 @@ public class PetStoreResource extends BasicRestServletJena {
 		);
 	}
 
-	@RestMethod(
-		name=GET,
-		path="/pet/findByStatus",
-		summary="Finds Pets by status",
-		description="Multiple status values can be provided with comma separated strings.",
-		swagger=@MethodSwagger(
-			tags="pet",
-			value={
-				"security:[{petstore_auth:['write:pets','read:pets']}]"
-			}
-		)
-	)
-	public Collection<Pet> findPetsByStatus(
-			@Query(
-				name="status",
-				description="Status values that need to be considered for filter.",
-				required=true,
-				type="array",
-				collectionFormat="csv",
-				items=@Items(
-					type="string",
-					_enum="AVAILABLE,PENDING,SOLD",
-					_default="AVAILABLE"
-				),
-				example="AVALIABLE,PENDING"
-			)
-			PetStatus[] status
-		) throws NotAcceptable {
-
-		return store.getPetsByStatus(status);
-	}
-
-	@RestMethod(
-		name=GET,
-		path="/pet/findByTags",
-		summary="Finds Pets by tags",
-		description="Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
-		swagger=@MethodSwagger(
-			tags="pet",
-			value={
-				"security:[ { petstore_auth:[ 'write:pets','read:pets' ] } ]"
-			}
-		)
-	)
-	@Deprecated
-	public Collection<Pet> findPetsByTags(
-			@Query(
-				name="tags",
-				description="Tags to filter by",
-				required=true,
-				example="['tag1','tag2']"
-			)
-			String[] tags
-		) throws InvalidTag, NotAcceptable {
-
-		return store.getPetsByTags(tags);
-	}
-
-	@RestMethod(
-		name=DELETE,
-		path="/pet/{petId}",
-		summary="Deletes a pet",
-		swagger=@MethodSwagger(
-			tags="pet",
-			value={
-				"security:[ { petstore_auth:[ 'write:pets','read:pets' ] } ]"
-			}
-		)
-	)
-	public Ok deletePet(
-			@Header(
-				name="api_key",
-				description="Security API key",
-				required=true,
-				example="foobar"
-			)
-			String apiKey,
-			@Path(
-				name="petId",
-				description="Pet id to delete",
-				example="123"
-			)
-			long petId
-		) throws IdNotFound, NotAcceptable {
-
-		store.removePet(petId);
-		return OK;
-	}
-
-	@RestMethod(
-		name=POST,
-		path="/pet/{petId}/uploadImage",
-		summary="Uploads an image",
-		swagger=@MethodSwagger(
-			tags="pet",
-			value={
-				"security:[ { petstore_auth:[ 'write:pets','read:pets' ] } ]"
-			}
-		)
-	)
-	public Ok uploadImage(
-			@Path(
-				name="petId",
-				description="ID of pet to update",
-				example="123"
-			)
-			long petId,
-			@FormData(
-				name="additionalMetadata",
-				description="Additional data to pass to server",
-				example="Foobar"
-			)
-			String additionalMetadata,
-			@FormData(
-				name="file",
-				description="file to upload",
-				required=true,
-				type="file"
-			)
-			byte[] file
-		) throws NotAcceptable, UnsupportedMediaType {
-
-		return OK;
-	}
-
 	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 	// Orders
 	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -454,6 +417,7 @@ public class PetStoreResource extends BasicRestServletJena {
 		;
 	}
 
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/store/order",
@@ -477,6 +441,7 @@ public class PetStoreResource extends BasicRestServletJena {
 		return store.getOrders();
 	}
 
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/store/order/{orderId}",
@@ -486,22 +451,13 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="store"
 		)
 	)
-	public Order getOrder(
-			@Path(
-				name="orderId",
-				description="ID of order to fetch",
-				maximum="1000",
-				minimum="101",
-				example="123"
-			)
-			long orderId
-		) throws InvalidId, IdNotFound, NotAcceptable {
-
-		if (orderId < 101 || orderId > 1000)
+	public Order getOrder(long orderId) throws InvalidId, IdNotFound, NotAcceptable {
+		if (orderId < 1 || orderId > 1000)
 			throw new InvalidId();
 		return store.getOrder(orderId);
 	}
 
+	@Override
 	@RestMethod(
 		name=POST,
 		path="/store/order",
@@ -513,23 +469,12 @@ public class PetStoreResource extends BasicRestServletJena {
 			DateSwap.ISO8601D.class
 		}
 	)
-	public Order placeOrder(
-			@FormData(
-				name="petId",
-				description="Pet ID"
-			)
-			long petId,
-			@FormData(
-				name="shipDate",
-				description="Ship date"
-			)
-			Date shipDate
-		) throws IdConflict, NotAcceptable, UnsupportedMediaType {
-
-		CreateOrder co = new CreateOrder(petId, shipDate);
-		return store.create(co);
+	public long placeOrder(long petId, String username) throws IdConflict, NotAcceptable, UnsupportedMediaType {
+		CreateOrder co = new CreateOrder(petId, username);
+		return store.create(co).getId();
 	}
 
+	@Override
 	@RestMethod(
 		name=DELETE,
 		path="/store/order/{orderId}",
@@ -542,22 +487,14 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="store"
 		)
 	)
-	public Ok deletePurchaseOrder(
-			@Path(
-				name="orderId",
-				description="ID of the order that needs to be deleted",
-				minimum="1",
-				example="5"
-			)
-			long orderId
-		) throws InvalidId, IdNotFound, NotAcceptable {
-
+	public Ok deleteOrder(long orderId) throws InvalidId, IdNotFound, NotAcceptable {
 		if (orderId < 0)
 			throw new InvalidId();
 		store.removeOrder(orderId);
 		return OK;
 	}
 
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/store/inventory",
@@ -581,6 +518,7 @@ public class PetStoreResource extends BasicRestServletJena {
 	// Users
 	//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/user",
@@ -594,6 +532,7 @@ public class PetStoreResource extends BasicRestServletJena {
 		return store.getUsers();
 	}
 
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/user/{username}",
@@ -602,17 +541,11 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="user"
 		)
 	)
-	public User getUser(
-			@Path(
-				name="username",
-				description="The name that needs to be fetched. Use user1 for testing."
-			)
-			String username
-		) throws InvalidUsername, IdNotFound, NotAcceptable {
-
+	public User getUser(String username) throws InvalidUsername, IdNotFound, NotAcceptable {
 		return store.getUser(username);
 	}
 
+	@Override
 	@RestMethod(
 		summary="Create user",
 		description="This can only be done by the logged in user.",
@@ -620,14 +553,12 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="user"
 		)
 	)
-	public Ok postUser(
-			@Body(description="Created user object") User user
-		) throws InvalidUsername, IdConflict, NotAcceptable, UnsupportedMediaType {
-
-		store.add(user);
+	public Ok postUser(User user) throws InvalidUsername, IdConflict, NotAcceptable, UnsupportedMediaType {
+		store.create(user);
 		return OK;
 	}
 
+	@Override
 	@RestMethod(
 		name=POST,
 		path="/user/createWithArray",
@@ -636,15 +567,13 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="user"
 		)
 	)
-	public Ok createUsers(
-			@Body(description="List of user objects") User[] users
-		) throws InvalidUsername, IdConflict, NotAcceptable, UnsupportedMediaType {
-
+	public Ok createUsers(User[] users) throws InvalidUsername, IdConflict, NotAcceptable, UnsupportedMediaType {
 		for (User user : users)
-			store.add(user);
+			store.create(user);
 		return OK;
 	}
 
+	@Override
 	@RestMethod(
 		name=PUT,
 		path="/user/{username}",
@@ -654,22 +583,12 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="user"
 		)
 	)
-	public Ok updateUser(
-			@Path(
-				name="username",
-				description="Name that need to be updated"
-			)
-			String username,
-			@Body(
-				description="Updated user object"
-			)
-			User user
-		) throws InvalidUsername, IdNotFound, NotAcceptable, UnsupportedMediaType {
-
+	public Ok updateUser(String username, User user) throws InvalidUsername, IdNotFound, NotAcceptable, UnsupportedMediaType {
 		store.update(user);
 		return OK;
 	}
 
+	@Override
 	@RestMethod(
 		name=DELETE,
 		path="/user/{username}",
@@ -679,18 +598,12 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="user"
 		)
 	)
-	public Ok deleteUser(
-			@Path(
-				name="username",
-				description="The name that needs to be deleted"
-			)
-			String username
-		) throws InvalidUsername, IdNotFound, NotAcceptable {
-
+	public Ok deleteUser(String username) throws InvalidUsername, IdNotFound, NotAcceptable {
 		store.removeUser(username);
 		return OK;
 	}
 
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/user/login",
@@ -700,27 +613,8 @@ public class PetStoreResource extends BasicRestServletJena {
 		)
 	)
 	public Ok login(
-			@Query(
-				name="username",
-				description="The username for login",
-				required=true,
-				example="myuser"
-			)
 			String username,
-			@Query(
-				name="password",
-				description="The password for login in clear text",
-				required=true,
-				example="abc123"
-			)
 			String password,
-			@ResponseHeader(
-				name="X-Rate-Limit",
-				type="integer",
-				format="int32",
-				description="Calls per hour allowed by the user.",
-				example="123"
-			)
 			Value<Integer> rateLimit,
 			Value<ExpiresAfter> expiresAfter,
 			RestRequest req,
@@ -737,24 +631,7 @@ public class PetStoreResource extends BasicRestServletJena {
 		return OK;
 	}
 
-	@ResponseHeader(
-		name="X-Expires-After",
-		type="string",
-		format="date-time",
-		description="Date in UTC when token expires",
-		example="2012-10-21"
-	)
-	public static class ExpiresAfter {
-		private final Calendar c;
-		public ExpiresAfter(Date d) {
-			this.c = new GregorianCalendar();
-			c.setTime(d);
-		}
-		public Calendar toCalendar() {
-			return c;
-		}
-	}
-
+	@Override
 	@RestMethod(
 		name=GET,
 		path="/user/logout",
@@ -763,8 +640,8 @@ public class PetStoreResource extends BasicRestServletJena {
 			tags="user"
 		)
 	)
-	public Ok logout(RestRequest req) throws NotAcceptable {
-		req.getSession().removeAttribute("login-expires");
+	public Ok logout() throws NotAcceptable {
+		getRequest().getSession().removeAttribute("login-expires");
 		return OK;
 	}
 }
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java
new file mode 100644
index 0000000..817271d
--- /dev/null
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java
@@ -0,0 +1,259 @@
+// ***************************************************************************************************************************
+// * 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.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.examples.rest.petstore;
+
+import static java.text.MessageFormat.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.persistence.*;
+
+import org.apache.juneau.examples.rest.petstore.dto.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.rest.client.*;
+
+/**
+ * Pet store database application.
+ */
+public class PetStoreService {
+
+	private final EntityManagerFactory entityManagerFactory;
+
+	public PetStoreService() {
+		entityManagerFactory = Persistence.createEntityManagerFactory("test");
+	}
+
+	public PetStoreService initDirect(PrintWriter w) throws Exception {
+
+		EntityManager em = getEntityManager();
+		EntityTransaction et = em.getTransaction();
+		JsonParser parser = JsonParser.create().build();
+
+		et.begin();
+
+		for (Pet x : em.createQuery("select X from Pet X", Pet.class).getResultList()) {
+			em.remove(x);
+			w.println(format("Deleted pet:  id={0}", x.getId()));
+		}
+		for (Order x : em.createQuery("select X from PetstoreOrder X", Order.class).getResultList()) {
+			em.remove(x);
+			w.println(format("Deleted order:  id={0}", x.getId()));
+		}
+		for (User x : em.createQuery("select X from PetstoreUser X", User.class).getResultList()) {
+			em.remove(x);
+			w.println(format("Deleted user:  username={0}", x.getUsername()));
+		}
+
+		et.commit();
+		et.begin();
+
+		for (Pet x : parser.parse(getStream("init/Pets.json"), Pet[].class)) {
+			x = em.merge(x);
+			w.println(format("Created pet:  id={0}, name={1}", x.getId(), x.getName()));
+		}
+		for (Order x : parser.parse(getStream("init/Orders.json"), Order[].class)) {
+			x = em.merge(x);
+			w.println(format("Created order:  id={0}", x.getId()));
+		}
+		for (User x: parser.parse(getStream("init/Users.json"), User[].class)) {
+			x = em.merge(x);
+			w.println(format("Created user:  username={0}", x.getUsername()));
+		}
+
+		et.commit();
+
+		return this;
+	}
+
+	public PetStoreService initViaRest(PrintWriter w) throws Exception {
+		JsonParser parser = JsonParser.create().ignoreUnknownBeanProperties().build();
+
+		try (RestClient rc = RestClient.create().json().rootUrl("http://localhost:10000").build()) {
+			PetStore ps = rc.getRemoteResource(PetStore.class);
+
+			for (Pet x : ps.getPets()) {
+				ps.deletePet("apiKey", x.getId());
+				w.println(format("Deleted pet:  id={0}", x.getId()));
+			}
+			for (Order x : ps.getOrders()) {
+				ps.deleteOrder(x.getId());
+				w.println(format("Deleted order:  id={0}", x.getId()));
+			}
+			for (User x : ps.getUsers()) {
+				ps.deleteUser(x.getUsername());
+				w.println(format("Deleted user:  username={0}", x.getUsername()));
+			}
+			for (CreatePet x : parser.parse(getStream("init/Pets.json"), CreatePet[].class)) {
+				long id = ps.postPet(x);
+				w.println(format("Created pet:  id={0}, name={1}", id, x.getName()));
+			}
+			for (Order x : parser.parse(getStream("init/Orders.json"), Order[].class)) {
+				long id = ps.placeOrder(x.getPetId(), x.getUsername());
+				w.println(format("Created order:  id={0}", id));
+			}
+			for (User x: parser.parse(getStream("init/Users.json"), User[].class)) {
+				ps.postUser(x);
+				w.println(format("Created user:  username={0}", x.getUsername()));
+			}
+		}
+
+		return this;
+	}
+
+
+	public Pet getPet(long id) throws IdNotFound {
+		return find(Pet.class, id);
+	}
+
+	public Order getOrder(long id) throws IdNotFound {
+		return find(Order.class, id);
+	}
+
+	public User getUser(String username) throws InvalidUsername, IdNotFound  {
+		assertValidUsername(username);
+		return find(User.class, username);
+	}
+
+	public List<Pet> getPets() {
+		return query("select X from Pet X", Pet.class);
+	}
+
+	public List<Order> getOrders() {
+		return query("select X from PetstoreOrder X", Order.class);
+	}
+
+	public List<User> getUsers() {
+		return query("select X from PetstoreUser X", User.class);
+	}
+
+	public Pet create(CreatePet c) {
+		return merge(new Pet().status(PetStatus.AVAILABLE).apply(c));
+	}
+
+	public Order create(CreateOrder c) {
+		return merge(new Order().status(OrderStatus.PLACED).apply(c));
+	}
+
+	public User create(User c) {
+		return merge(new User().apply(c));
+	}
+
+	public Pet update(UpdatePet u) throws IdNotFound {
+		return merge(getPet(u.getId()).apply(u));
+	}
+
+	public Order update(Order o) throws IdNotFound {
+		return merge(getOrder(o.getId()).apply(o));
+	}
+
+	public User update(User u) throws IdNotFound, InvalidUsername {
+		assertValidUsername(u.getUsername());
+		return merge(getUser(u.getUsername()).apply(u));
+	}
+
+	public void removePet(long id) throws IdNotFound {
+		remove(getPet(id));
+	}
+
+	public void removeOrder(long id) throws IdNotFound {
+		remove(getOrder(id));
+	}
+
+	public void removeUser(String username) throws IdNotFound {
+		remove(getUser(username));
+	}
+
+	public Collection<Pet> getPetsByStatus(PetStatus[] status) {
+		return getEntityManager()
+			.createQuery("select X from Pet X where X.status in :status", Pet.class)
+			.setParameter("status", status)
+			.getResultList();
+	}
+
+	public Collection<Pet> getPetsByTags(String[] tags) throws InvalidTag {
+		return getEntityManager()
+			.createQuery("select X from Pet X where X.tags in :tags", Pet.class)
+			.setParameter("tags", tags)
+			.getResultList();
+	}
+
+	public Map<PetStatus,Integer> getInventory() {
+		Map<PetStatus,Integer> m = new LinkedHashMap<>();
+		for (Pet p : getPets()) {
+			PetStatus ps = p.getStatus();
+			if (! m.containsKey(ps))
+				m.put(ps, 1);
+			else
+				m.put(ps, m.get(ps) + 1);
+		}
+		return m;
+	}
+
+	public boolean isValid(String username, String password) {
+		return getUser(username).getPassword().equals(password);
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Helper methods
+	//-----------------------------------------------------------------------------------------------------------------
+
+	private void assertValidUsername(String username) throws InvalidUsername {
+		if (username == null || ! username.matches("[\\w\\d]{3,8}"))
+			throw new InvalidUsername();
+	}
+
+	private EntityManager getEntityManager() {
+		return entityManagerFactory.createEntityManager();
+	}
+
+	private <T> T merge(T t) {
+		EntityManager em = getEntityManager();
+		try {
+			EntityTransaction et = em.getTransaction();
+			et.begin();
+			t = em.merge(t);
+			et.commit();
+			return t;
+		} finally {
+			em.close();
+		}
+	}
+
+	private <T> void remove(T t) {
+		EntityManager em = getEntityManager();
+		try {
+			EntityTransaction et = em.getTransaction();
+			et.begin();
+			em.remove(t);
+			et.commit();
+		} finally {
+			em.close();
+		}
+	}
+
+	private <T> List<T> query(String query, Class<T> t) {
+		return getEntityManager().createQuery(query, t).getResultList();
+	}
+
+	private <T> T find(Class<T> t, Object id) throws IdNotFound {
+		T o = getEntityManager().find(t, id);
+		if (o == null)
+			throw new IdNotFound(id, t);
+		return o;
+	}
+
+	private InputStream getStream(String fileName) {
+		return getClass().getResourceAsStream(fileName);
+	}
+}
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/CreateOrder.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
similarity index 57%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/CreateOrder.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
index 23f4ab9..2b42888 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/CreateOrder.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
@@ -10,35 +10,65 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
-
-import java.util.*;
+package org.apache.juneau.examples.rest.petstore.dto;
 
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.internal.*;
 
 /**
  * Bean for creating {@link Order} objects.
+ * Also serves as the superclass for the {@link Order} object.
  */
+@Bean(fluentSetters=true, properties="petId,username")
 public class CreateOrder {
-	private final long petId;
-	private final Date shipDate;
 
-	@BeanConstructor(properties="petId,shipDate")
-	public CreateOrder(long petId, Date shipDate) {
+	private long petId;
+	private String username;
+
+	/**
+	 * Optional constructor.
+	 */
+	@BeanConstructor(properties="petId,username")
+	public CreateOrder(long petId, String username) {
 		this.petId = petId;
-		this.shipDate = shipDate;
+		this.username = username;
 	}
 
-	public static CreateOrder example() {
-		return new CreateOrder(123, StringUtils.parseIsoDate("2012-12-21"));
-	}
+	/**
+	 * Constructor needed by JPA.
+	 */
+	public CreateOrder() {}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Bean properties
+	//-----------------------------------------------------------------------------------------------------------------
 
 	public long getPetId() {
 		return petId;
 	}
 
-	public Date getShipDate() {
-		return shipDate;
+	public CreateOrder petId(long value) {
+		this.petId = value;
+		return this;
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public CreateOrder username(String value) {
+		this.username = value;
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * Used to populate Swagger examples.
+	 * Example is inferred from the method name.
+	 */
+	public static CreateOrder example() {
+		return new CreateOrder(123, "sampleuser");
 	}
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetUpdate.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreatePet.java
similarity index 53%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetUpdate.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreatePet.java
index c406359..1cd665c 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetUpdate.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreatePet.java
@@ -10,57 +10,89 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.jsonschema.annotation.*;
 
 /**
  * Bean for creating {@link Pet} objects.
  */
-public class PetUpdate {
-
-	private final long id;
-	private final String name;
-	private final float price;
-	private final String species;
-	private final String[] tags;
-	private final PetStatus status;
-
-	@BeanConstructor(properties="id,name,price,species,tags,status")
-	public PetUpdate(long id, String name, float price, String species, String[] tags, PetStatus status) {
-		this.id = id;
+@Bean(fluentSetters=true, properties="name,price,species,tags")
+public class CreatePet {
+
+	@Schema(description="Pet name.", minLength=3, maxLength=50)
+	private String name;
+
+	@Schema(description="Price of pet.", maximum="999.99")
+	private float price;
+
+	@Schema(description="Pet species.")
+	private Species species;
+
+	@Schema(description="Pet attributes.", example="friendly,smart")
+	private String[] tags;
+
+	/**
+	 * Constructor.
+	 */
+	public CreatePet(String name, float price, Species species, String[] tags) {
 		this.name = name;
 		this.price = price;
 		this.species = species;
 		this.tags = tags;
-		this.status = status;
 	}
 
-	public static PetUpdate example() {
-		return new PetUpdate(123l, "Doggie", 9.99f, "doc", new String[] {"friendly","cute"}, PetStatus.AVAILABLE);
-	}
+	/**
+	 * Empty constructor.
+	 */
+	public CreatePet() {}
 
-	public long getId() {
-		return id;
-	}
+	//-----------------------------------------------------------------------------------------------------------------
+	// Bean properties
+	//-----------------------------------------------------------------------------------------------------------------
 
 	public String getName() {
 		return name;
 	}
 
+	public CreatePet name(String value) {
+		this.name = value;
+		return this;
+	}
+
 	public float getPrice() {
 		return price;
 	}
 
-	public String getSpecies() {
+	public CreatePet price(float value) {
+		this.price = value;
+		return this;
+	}
+
+	public Species getSpecies() {
 		return species;
 	}
 
+	public CreatePet species(Species value) {
+		this.species = value;
+		return this;
+	}
+
 	public String[] getTags() {
 		return tags;
 	}
 
-	public PetStatus getStatus() {
-		return status;
+	public CreatePet tags(String...value) {
+		this.tags = value;
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other
+	//-----------------------------------------------------------------------------------------------------------------
+
+	public static CreatePet example() {
+		return new CreatePet("Doggie", 9.99f, Species.DOG, new String[]{"smart","friendly"});
 	}
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Order.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Order.java
new file mode 100644
index 0000000..c977b36
--- /dev/null
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Order.java
@@ -0,0 +1,138 @@
+// ***************************************************************************************************************************
+// * 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.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.examples.rest.petstore.dto;
+
+import static javax.persistence.TemporalType.*;
+
+import java.util.*;
+
+import javax.persistence.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.transforms.*;
+
+@Bean(fluentSetters=true, properties="id,petId,username,status,shipDate")
+@Example("{id:123,petId:456,shipDate:'2012-12-21',status:'APPROVED'}")
+@Entity(name="PetstoreOrder")
+public class Order {
+
+	@Column @Id @GeneratedValue
+	@Schema(description="Unique identifier for this order.")
+	@Html(link="servlet:/store/order/{id}")
+	private long id;
+
+	@Column
+	@Schema(description="Pet unique identifier.")
+	@Html(link="servlet:/pet/{id}")
+	private long petId;
+
+	@Column(length=20)
+	@Schema(description="User who created this order.", minLength=3, maxLength=20)
+	@Html(link="servlet:/user/{username}")
+	private String username;
+
+	@Column
+	@Enumerated(EnumType.STRING)
+	@Schema(description="The current order status.")
+	private OrderStatus status;
+
+	@Column @Temporal(TIMESTAMP)
+	@Schema(description="The ship date for this order.", format="date-time")
+	@Swap(DateSwap.ISO8601D.class)
+	private Date shipDate;
+
+	public Order apply(CreateOrder o) {
+		this.petId = o.getPetId();
+		this.username = o.getUsername();
+		return this;
+	}
+
+	public Order apply(Order o) {
+		this.id = o.getId();
+		this.petId = o.getPetId();
+		this.username = o.getUsername();
+		this.status = o.getStatus();
+		this.shipDate = o.getShipDate();
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Bean properties
+	//-----------------------------------------------------------------------------------------------------------------
+
+	public long getId() {
+		return id;
+	}
+
+	public Order id(long id) {
+		this.id = id;
+		return this;
+	}
+
+	public Date getShipDate() {
+		return shipDate;
+	}
+
+	public Order shipDate(Date value) {
+		this.shipDate = value;
+		return this;
+	}
+
+	public OrderStatus getStatus() {
+		return status;
+	}
+
+	public Order status(OrderStatus value) {
+		this.status = value;
+		return this;
+	}
+
+	public long getPetId() {
+		return petId;
+	}
+
+	public Order petId(long value) {
+		this.petId = value;;
+		return this;
+	}
+
+	public String getUsername() {
+		return username;
+	}
+
+	public Order username(String value) {
+		this.username = value;
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * This shows an example generated from a static method.
+	 */
+	@Example
+	public static Order example() {
+		return new Order()
+			.id(123)
+			.username("sampleuser")
+			.petId(456)
+			.status(OrderStatus.APPROVED)
+			.shipDate(DateUtils.parseISO8601("2020-10-10"))
+		;
+	}
+}
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/OrderStatus.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/OrderStatus.java
similarity index 97%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/OrderStatus.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/OrderStatus.java
index 5299c69..8eec9f5 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/OrderStatus.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/OrderStatus.java
@@ -10,7 +10,7 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
 
 import org.apache.juneau.html.*;
 import org.apache.juneau.html.annotation.*;
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Pet.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Pet.java
similarity index 58%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Pet.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Pet.java
index e0c3a67..ffb4fb0 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Pet.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Pet.java
@@ -10,39 +10,75 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
+
+import static javax.persistence.EnumType.*;
 
 import java.util.*;
 
+import javax.persistence.*;
+
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.html.*;
 import org.apache.juneau.html.annotation.*;
-import org.apache.juneau.xml.annotation.*;
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.serializer.*;
 
 /**
  * Pet bean.
  */
-@Bean(typeName="Pet", fluentSetters=true, properties="id,species,name,photoUrls,tags,price,status")
+@Bean(typeName="Pet", fluentSetters=true, properties="id,species,name,tags,price,status")
+@Entity
 public class Pet {
+
+	@Column @Id @GeneratedValue
+	@Schema(description="Unique identifier for this pet.")
+	@Html(link="servlet:/pet/{id}")
 	private long id;
+
+	@Column(length=50)
+	@Schema(description="Pet name.", minLength=3, maxLength=50)
+	private String name;
+
+	@Column
+	@Schema(description="Price of pet.", maximum="999.99")
+	@Html(render=PriceRender.class)
 	private float price;
+
+	@Column
+	@Schema(description="Pet species.")
 	private Species species;
-	private String name;
-	private List<String> photoUrls;
-	private List<PetTag> tags;
+
+	@ElementCollection @OrderColumn
+	@Schema(description="Pet attributes.", example="friendly,smart")
+	private List<String> tags;
+
+	@Column @Enumerated(STRING)
+	@Schema(description="Pet species.")
 	private PetStatus status;
 
-	// This shows an example generated from a static method.
-	@Example
-	public static Pet example() {
-		return new Pet()
-			.id(123)
-			.species(Species.example())
-			.name("Doggie")
-			.tags(PetTag.example())
-			.status(PetStatus.AVAILABLE);
+	public Pet apply(CreatePet x) {
+		this.name = x.getName();
+		this.price = x.getPrice();
+		this.species = x.getSpecies();
+		this.tags = x.getTags() == null ? null : Arrays.asList(x.getTags());
+		return this;
 	}
 
-	@Html(link="servlet:/pet/{id}")
+	public Pet apply(UpdatePet x) {
+		this.id = x.getId();
+		this.name = x.getName();
+		this.price = x.getPrice();
+		this.species = x.getSpecies();
+		this.tags = Arrays.asList(x.getTags());
+		this.status = x.getStatus();
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Bean properties
+	//-----------------------------------------------------------------------------------------------------------------
+
 	public long getId() {
 		return id;
 	}
@@ -52,45 +88,43 @@ public class Pet {
 		return this;
 	}
 
-	public Species getSpecies() {
-		return species;
+	public String getName() {
+		return name;
 	}
 
-	public Pet species(Species species) {
-		this.species = species;
+	public Pet name(String name) {
+		this.name = name;
 		return this;
 	}
 
-	public String getName() {
-		return name;
+	public float getPrice() {
+		return price;
 	}
 
-	public Pet name(String name) {
-		this.name = name;
+	public Pet price(float price) {
+		this.price = price;
 		return this;
 	}
 
-	@URI
-	@Xml(childName="photoUrl")
-	public List<String> getPhotoUrls() {
-		return photoUrls;
+	public Species getSpecies() {
+		return species;
 	}
 
-	public Pet photoUrls(List<String> photoUrls) {
-		this.photoUrls = photoUrls;
+	public Pet species(Species species) {
+		this.species = species;
 		return this;
 	}
 
-	public List<PetTag> getTags() {
+	public List<String> getTags() {
 		return tags;
 	}
 
-	public Pet tags(List<PetTag> tags) {
+	public Pet tags(List<String> tags) {
 		this.tags = tags;
 		return this;
 	}
 
-	public Pet tags(PetTag...tags) {
+	public Pet tags(String...tags) {
 		this.tags = Arrays.asList(tags);
 		return this;
 	}
@@ -113,23 +147,38 @@ public class Pet {
 
 	public boolean hasTag(String...tags) {
 		for (String tag : tags)
-			for (PetTag t : this.tags)
-				if (t.getName().equals(tag))
+			for (String t : this.tags)
+				if (t.equals(tag))
 					return true;
 		return false;
 	}
 
-	@BeanProperty(format="$%.2f")  // Renders price in dollars.
-	public float getPrice() {
-		return price;
+	public java.net.URI getEdit() {
+		return java.net.URI.create("servlet:/pet/edit/{id}");
 	}
 
-	public Pet price(float price) {
-		this.price = price;
-		return this;
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * This shows an example generated from a static method.
+	 */
+	@Example
+	public static Pet example() {
+		return new Pet()
+			.id(123)
+			.species(Species.DOG)
+			.name("Doggie")
+			.tags("friendly","smart")
+			.status(PetStatus.AVAILABLE);
 	}
 
-	public java.net.URI getEdit() {
-		return java.net.URI.create("servlet:/pet/edit/{id}");
+
+	public static final class PriceRender extends HtmlRender<Float> {
+		@Override
+		public Object getContent(SerializerSession session, Float value) {
+			return value == null ? null : String.format("$%.2f", value);
+		}
 	}
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStatus.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/PetStatus.java
similarity index 97%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStatus.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/PetStatus.java
index 241f80a..8e67d5d 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStatus.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/PetStatus.java
@@ -10,7 +10,7 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
 
 import org.apache.juneau.html.*;
 import org.apache.juneau.html.annotation.*;
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetTag.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/PetTag.java
similarity index 98%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetTag.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/PetTag.java
index 1257f3d..f968e74 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetTag.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/PetTag.java
@@ -10,7 +10,7 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
 
 import java.util.*;
 
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Species.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Species.java
similarity index 71%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Species.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Species.java
index f9b41c9..0acb1cb 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/Species.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/Species.java
@@ -10,58 +10,26 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
 
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.dto.html5.Img;
+import org.apache.juneau.dto.html5.*;
 import org.apache.juneau.html.*;
-import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.html.annotation.Html;
 import org.apache.juneau.serializer.*;
 
-@Bean(typeName="Species", fluentSetters=true)
 @Html(render=Species.SpeciesRender.class)
-public class Species {
-	private long id;
-	private String name;
+public enum Species {
 
-	public long getId() {
-		return id;
-	}
-
-	public Species id(long id) {
-		this.id = id;
-		return this;
-	}
-
-	public String getName() {
-		return name;
-	}
-
-	public Species name(String name) {
-		this.name = name;
-		return this;
-	}
-
-	@Example
-	public static Species example() {
-		return new Species()
-			.id(123)
-			.name("Dog");
-	}
+	BIRD, CAT, DOG, FISH, MOUSE, RABBIT, SNAKE;
 
 	public static class SpeciesRender extends HtmlRender<Species> {
 		@Override
 		public Object getContent(SerializerSession session, Species value) {
-			return new Img().src("servlet:/htdocs/"+value.getName().toLowerCase()+".png");
+			return new Img().src("servlet:/htdocs/"+value.name().toLowerCase()+".png");
 		}
 		@Override
 		public String getStyle(SerializerSession session, Species value) {
 			return "background-color:#FDF2E9";
 		}
 	}
-
-	@Override /* Object */
-	public String toString() {
-		return name;
-	}
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/UpdatePet.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/UpdatePet.java
new file mode 100644
index 0000000..bfde4f1
--- /dev/null
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/UpdatePet.java
@@ -0,0 +1,97 @@
+// ***************************************************************************************************************************
+// * 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.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.examples.rest.petstore.dto;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.jsonschema.annotation.*;
+
+/**
+ * Bean for updating {@link Pet} objects.
+ */
+@Bean(fluentSetters=true, properties="id,name,price,species,tags,status")
+public class UpdatePet extends CreatePet {
+
+	@Schema(description="Pet identifier.", minimum="1")
+	private long id;
+
+	@Schema(description="Updated pet status.")
+	private PetStatus status;
+
+	/**
+	 * Constructor.
+	 */
+	public UpdatePet(long id, String name, float price, Species species, String[] tags, PetStatus status) {
+		super(name, price, species, tags);
+		this.id = id;
+		this.status = status;
+	}
+
+	/**
+	 * Empty constructor.
+	 */
+	public UpdatePet() {}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Bean properties
+	//-----------------------------------------------------------------------------------------------------------------
+
+	public long getId() {
+		return id;
+	}
+
+	public UpdatePet id(long value) {
+		this.id = value;
+		return this;
+	}
+
+	public PetStatus getStatus() {
+		return status;
+	}
+
+	public UpdatePet status(PetStatus value) {
+		this.status = value;
+		return this;
+	}
+
+	@Override
+	public UpdatePet name(String value) {
+		super.name(value);
+		return this;
+	}
+
+	@Override
+	public UpdatePet price(float value) {
+		super.price(value);
+		return this;
+	}
+
+	@Override
+	public UpdatePet species(Species value) {
+		super.species(value);
+		return this;
+	}
+
+	@Override
+	public UpdatePet tags(String...value) {
+		super.tags(value);
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other
+	//-----------------------------------------------------------------------------------------------------------------
+
+	public static UpdatePet example() {
+		return new UpdatePet(123, "Doggie", 9.99f, Species.DOG, new String[]{"smart","friendly"}, PetStatus.SOLD);
+	}
+}
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/User.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/User.java
similarity index 62%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/User.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/User.java
index 512fdf9..6610ce8 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/User.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/User.java
@@ -10,27 +10,65 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
+
+import static javax.persistence.EnumType.*;
+
+import javax.persistence.*;
 
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.jsonschema.annotation.*;
 
 @Bean(typeName="User", fluentSetters=true, properties="username,firstName,lastName,email,password,phone,userStatus")
+@Entity(name="PetstoreUser")
 public class User {
-	private String username, firstName, lastName, email, password, phone;
+
+	@Id
+	@Column(length=8)
+	@Schema(description="Username.", minLength=3, maxLength=8)
+	@Html(link="servlet:/user/{username}")
+	private String username;
+
+	@Column(length=50)
+	@Schema(description="First name.", maxLength=50)
+	private String firstName;
+
+	@Column(length=50)
+	@Schema(description="First name.", maxLength=50)
+	private String lastName;
+
+	@Column(length=50)
+	@Schema(description="First name.", maxLength=50, pattern="\\S+\\@\\S+")
+	private String email;
+
+	@Column(length=8)
+	@Schema(description="Password.", minLength=3, maxLength=8, pattern="[\\w\\d]{3,8}")
+	private String password;
+
+	@Column
+	@Schema(description="Phone number.", minLength=12, maxLength=12, pattern="\\d{3}\\-\\d{3}\\-\\d{4}")
+	private String phone;
+
+	@Column
+	@Enumerated(STRING)
 	private UserStatus userStatus;
 
-	// This shows an example provided as a static field.
-	@Example
-	public static User EXAMPLE = new User()
-		.username("billy")
-		.firstName("Billy")
-		.lastName("Bob")
-		.email("billy@apache.org")
-		.userStatus(UserStatus.ACTIVE)
-		.phone("111-222-3333");
+	public User apply(User c) {
+		this.username = c.getUsername();
+		this.firstName = c.getFirstName();
+		this.lastName = c.getLastName();
+		this.email = c.getEmail();
+		this.password = c.getPassword();
+		this.phone = c.getPhone();
+		this.userStatus = c.getUserStatus();
+		return this;
+	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Bean properties
+	//-----------------------------------------------------------------------------------------------------------------
 
-	@Html(link="servlet:/user/{username}")
 	public String getUsername() {
 		return username;
 	}
@@ -93,4 +131,21 @@ public class User {
 		this.userStatus = userStatus;
 		return this;
 	}
+
+	//-----------------------------------------------------------------------------------------------------------------
+	// Other
+	//-----------------------------------------------------------------------------------------------------------------
+
+	/**
+	 * This shows an example generated from a static method.
+	 */
+	@Example
+	public static User EXAMPLE = new User()
+		.username("billy")
+		.firstName("Billy")
+		.lastName("Bob")
+		.email("billy@apache.org")
+		.userStatus(UserStatus.ACTIVE)
+		.phone("111-222-3333");
+
 }
diff --git a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/UserStatus.java b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/UserStatus.java
similarity index 97%
rename from juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/UserStatus.java
rename to juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/UserStatus.java
index cb5aa50..5fd004f 100644
--- a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/UserStatus.java
+++ b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/UserStatus.java
@@ -10,7 +10,7 @@
 // * "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.                                              *
 // ***************************************************************************************************************************
-package org.apache.juneau.examples.rest.petstore;
+package org.apache.juneau.examples.rest.petstore.dto;
 
 import org.apache.juneau.html.*;
 import org.apache.juneau.html.annotation.*;
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/META-INF/persistence.xml b/juneau-examples/juneau-examples-rest/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..7e1213e
--- /dev/null
+++ b/juneau-examples/juneau-examples-rest/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,34 @@
+<!--
+ ***************************************************************************************************************************
+ * 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.                                              *
+ ***************************************************************************************************************************
+-->
+<persistence
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
+	version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
+	<persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
+		<class>org.apache.juneau.examples.rest.petstore.dto.Pet</class>
+		<class>org.apache.juneau.examples.rest.petstore.dto.Order</class>
+		<class>org.apache.juneau.examples.rest.petstore.dto.User</class>
+		<properties>
+			<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
+			<property name="javax.persistence.jdbc.url" value="jdbc:derby:target/derby/testDB;create=true" />
+			<property name="javax.persistence.jdbc.user" value="" />
+			<property name="javax.persistence.jdbc.password" value="" />
+			<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyDialect" />
+			<property name="hibernate.hbm2ddl.auto" value="create-drop" />
+			<property name="show_sql" value="true" />
+			<property name="hibernate.temp.use_jdbc_metadata_defaults" value="false" />
+		</properties>
+	</persistence-unit>
+</persistence>
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/PetStoreResource_orig.json b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/PetStoreResource_orig.json
deleted file mode 100644
index 2b290a6..0000000
--- a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/PetStoreResource_orig.json
+++ /dev/null
@@ -1,1048 +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.                                              *
-// ***************************************************************************************************************************
-
-{
-	"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"
-			}
-		},
-		"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-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Pets.json b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Pets.json
deleted file mode 100644
index 9976a4a..0000000
--- a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Pets.json
+++ /dev/null
@@ -1,24 +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.                                              *
-// ***************************************************************************************************************************
-
-[
-	{id:101, species:'cat', name:'Mr. Frisky', price:39.99, tags:['friendly'], status:'AVAILABLE'},
-	{id:102, species:'dog', name:'Kibbles', price:99.99, tags:['loyal'], status:'AVAILABLE'},
-	{id:103, species:'rabbit', name:'Hoppy', price:49.99, tags:['friendly','smells nice'], status:'AVAILABLE'},
-	{id:104, species:'rabbit', name:'Hoppy 2', price:49.99, status:'AVAILABLE'},
-	{id:105, species:'rabbit', name:'Hoppy 3', price:49.99, status:'AVAILABLE'},
-	{id:106, species:'rabbit', name:'Hoppy 4', price:49.99, status:'AVAILABLE'},
-	{id:107, species:'fish', name:'Gorton', price:1.99, status:'PENDING'},
-	{id:108, species:'mouse', name:'Hackwrench', price:4.99, status:'SOLD'},
-	{id:109, species:'snake', name:'Just Snake', price:9.99, status:'SOLD'}
-]
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Orders.json b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Orders.json
similarity index 100%
rename from juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Orders.json
rename to juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Orders.json
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Users.json b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Pets.json
similarity index 69%
rename from juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Users.json
rename to juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Pets.json
index 5e340c1..c0aa31f 100644
--- a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Users.json
+++ b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Pets.json
@@ -12,7 +12,13 @@
 // ***************************************************************************************************************************
 
 [
-	{username:'mariewatson',firstName:'Marie',lastName:'Watson',email:'marie.watson@fakemail.com',password:'123456',phone:'444-555-7777',userStatus:'ACTIVE'},
-	{username:'danielvaughn',firstName:'Daniel',lastName:'Vaughn',email:'daniel.vaughn@test.com',password:'123456',phone:'666-777-3333',userStatus:'ACTIVE'},
-	{username:'brendafuller',firstName:'Brenda',lastName:'Fuller',email:'brenda.fuller@example.com',password:'123456',phone:'777-888-3333',userStatus:'INACTIVE'}
-]
+	{species:'CAT', name:'Mr. Frisky', price:39.99, tags:['friendly'], status:'AVAILABLE'},
+	{species:'DOG', name:'Kibbles', price:99.99, tags:['loyal'], status:'AVAILABLE'},
+	{species:'RABBIT', name:'Hoppy', price:49.99, tags:['friendly','smells nice'], status:'AVAILABLE'},
+	{species:'RABBIT', name:'Hoppy 2', price:49.99, status:'AVAILABLE'},
+	{species:'RABBIT', name:'Hoppy 3', price:49.99, status:'AVAILABLE'},
+	{species:'RABBIT', name:'Hoppy 4', price:49.99, status:'AVAILABLE'},
+	{species:'FISH', name:'Gorton', price:1.99, status:'PENDING'},
+	{species:'MOUSE', name:'Hackwrench', price:4.99, status:'SOLD'},
+	{species:'SNAKE', name:'Just Snake', price:9.99, status:'SOLD'}
+]
\ No newline at end of file
diff --git a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Species.json b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Users.json
similarity index 77%
rename from juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Species.json
rename to juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Users.json
index fc5ecbf..7f02a3c 100644
--- a/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/Species.json
+++ b/juneau-examples/juneau-examples-rest/src/main/resources/org/apache/juneau/examples/rest/petstore/init/Users.json
@@ -12,10 +12,7 @@
 // ***************************************************************************************************************************
 
 [
-	{id:101, name:'cat'},
-	{id:102, name:'dog'},
-	{id:103, name:'rabbit'},
-	{id:104, name:'fish'},
-	{id:105, name:'mouse'},
-	{id:106, name:'snake'}
+	{username:'mwatson',firstName:'Marie',lastName:'Watson',email:'marie.watson@fakemail.com',password:'123456',phone:'444-555-7777',userStatus:'ACTIVE'},
+	{username:'dvaughn',firstName:'Daniel',lastName:'Vaughn',email:'daniel.vaughn@test.com',password:'123456',phone:'666-777-3333',userStatus:'ACTIVE'},
+	{username:'bfuller',firstName:'Brenda',lastName:'Fuller',email:'brenda.fuller@example.com',password:'123456',phone:'777-888-3333',userStatus:'INACTIVE'}
 ]
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java
index 13e737d..97f088e 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteMethodMeta.java
@@ -107,7 +107,7 @@ public class RemoteMethodMeta {
 
 			methodReturn = new RemoteMethodReturn(m);
 
-			fullPath = path.indexOf("://") != -1 ? path : (parentPath.isEmpty() ? urlEncode(path) : (trimSlashes(parentPath) + '/' + urlEncode(path)));
+			fullPath = path.indexOf("://") != -1 ? path : (parentPath.isEmpty() ? urlEncodePath(path) : (trimSlashes(parentPath) + '/' + urlEncodePath(path)));
 
 			for (int i = 0; i < m.getParameterTypes().length; i++) {
 				RemoteMethodArg rma = RemoteMethodArg.create(m, i);
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
index 1e4520f..de95ef6 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestCallHandler.java
@@ -141,6 +141,8 @@ public class BasicRestCallHandler implements RestCallHandler {
 
 			req = createRequest(r1);
 			RestResponse res = createResponse(req, r2);
+			context.setRequest(req);
+			context.setResponse(res);
 			String method = req.getMethod();
 			String methodUC = method.toUpperCase(Locale.ENGLISH);
 
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 0bb1bf7..5b5a505 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
@@ -3042,6 +3042,9 @@ public final class RestContext extends BeanContext {
 	private final ClasspathResourceManager staticResourceManager;
 	private final ConcurrentHashMap<Integer,AtomicInteger> stackTraceHashes = new ConcurrentHashMap<>();
 
+	private final ThreadLocal<RestRequest> req = new ThreadLocal<>();
+	private final ThreadLocal<RestResponse> res = new ThreadLocal<>();
+
 	/**
 	 * Constructor.
 	 *
@@ -4763,4 +4766,39 @@ public final class RestContext extends BeanContext {
 	public BeanSessionArgs createDefaultSessionArgs() {
 		throw new NoSuchMethodError();
 	}
+
+	/**
+	 * Returns the HTTP request object for the current request.
+	 *
+	 * @return The HTTP request object, or <jk>null</jk> if it hasn't been created.
+	 */
+	public RestRequest getRequest() {
+		return req.get();
+	}
+
+	void setRequest(RestRequest req) {
+		this.req.set(req);
+	}
+
+	/**
+	 * Returns the HTTP response object for the current request.
+	 *
+	 * @return The HTTP response object, or <jk>null</jk> if it hasn't been created.
+	 */
+	public RestResponse getResponse() {
+		return res.get();
+	}
+
+	void setResponse(RestResponse res) {
+		this.res.set(res);
+	}
+
+	/**
+	 * Clear any request state information on this context.
+	 * This should always be called in a finally block in the RestServlet.
+	 */
+	void clearState() {
+		req.remove();
+		res.remove();
+	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
index e8f218a..61c5bbd 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestResponse.java
@@ -414,7 +414,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
 	 */
 	@Override /* ServletResponse */
 	public PrintWriter getWriter() throws IOException {
-		return getWriter(true);
+		return getWriter(true, false);
 	}
 
 	/**
@@ -435,7 +435,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
 		setContentType(contentType);
 		setHeader("X-Content-Type-Options", "nosniff");
 		setHeader("Content-Encoding", "identity");
-		return getWriter();
+		return getWriter(true, true);
 	}
 
 	/**
@@ -448,11 +448,11 @@ public final class RestResponse extends HttpServletResponseWrapper {
 	 * @throws IOException
 	 */
 	public FinishablePrintWriter getNegotiatedWriter() throws NotAcceptable, IOException {
-		return getWriter(false);
+		return getWriter(false, false);
 	}
 
 	@SuppressWarnings("resource")
-	private FinishablePrintWriter getWriter(boolean raw) throws NotAcceptable, IOException {
+	private FinishablePrintWriter getWriter(boolean raw, boolean autoflush) throws NotAcceptable, IOException {
 		if (w != null)
 			return w;
 
@@ -462,7 +462,7 @@ public final class RestResponse extends HttpServletResponseWrapper {
 
 		try {
 			OutputStream out = (raw ? getOutputStream() : getNegotiatedOutputStream());
-			w = new FinishablePrintWriter(out, getCharacterEncoding());
+			w = new FinishablePrintWriter(out, getCharacterEncoding(), autoflush);
 			return w;
 		} catch (UnsupportedEncodingException e) {
 			String ce = getCharacterEncoding();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
index 8dba115..05ebb3a 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServlet.java
@@ -42,7 +42,6 @@ public abstract class RestServlet extends HttpServlet {
 	private boolean isInitialized = false;
 	private Exception initException;
 
-
 	@Override /* Servlet */
 	public final synchronized void init(ServletConfig servletConfig) throws ServletException {
 		try {
@@ -148,6 +147,8 @@ public abstract class RestServlet extends HttpServlet {
 			r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage());
 		} catch (Throwable e) {
 			r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage());
+		} finally {
+			context.clearState();
 		}
 	}
 
@@ -206,6 +207,24 @@ public abstract class RestServlet extends HttpServlet {
 		}
 	}
 
+	/**
+	 * Returns the current HTTP request.
+	 *
+	 * @return The current HTTP request, or <jk>null</jk> if it wasn't created.
+	 */
+	public RestRequest getRequest() {
+		return context.getRequest();
+	}
+
+	/**
+	 * Returns the current HTTP response.
+	 *
+	 * @return The current HTTP response, or <jk>null</jk> if it wasn't created.
+	 */
+	public RestResponse getResponse() {
+		return context.getResponse();
+	}
+
 	@Override /* GenericServlet */
 	public synchronized void destroy() {
 		if (context != null)
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/FinishablePrintWriter.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/FinishablePrintWriter.java
index 904dcb1..63495c5 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/FinishablePrintWriter.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/util/FinishablePrintWriter.java
@@ -28,10 +28,11 @@ public class FinishablePrintWriter extends PrintWriter implements Finishable {
 	 *
 	 * @param out The wrapped output stream.
 	 * @param characterEncoding The character encoding of the output stream.
+	 * @param autoFlush Automatically flush after every println.
 	 * @throws IOException
 	 */
-	public FinishablePrintWriter(OutputStream out, String characterEncoding) throws IOException {
-		super(new OutputStreamWriter(out, characterEncoding));
+	public FinishablePrintWriter(OutputStream out, String characterEncoding, boolean autoFlush) throws IOException {
+		super(new OutputStreamWriter(out, characterEncoding), autoFlush);
 		f = (out instanceof Finishable ? (Finishable)out : null);
 	}