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 522f1a0  JUNEAU-140 Provide initial contents of PetStore modules.
522f1a0 is described below

commit 522f1a076c92ca55bcd7f2da677afdadc2d24ecd
Author: JamesBognar <[email protected]>
AuthorDate: Fri Sep 6 22:18:07 2019 -0400

    JUNEAU-140 Provide initial contents of PetStore modules.
---
 .../java/org/apache/juneau/pojotools/PageArgs.java |  37 ++
 .../java/org/apache/juneau/petstore/PetStore.java  | 421 +++++++++++++
 .../juneau-examples-petstore-client/pom.xml        |   1 +
 .../main/java/org/apache/juneau/petstore/Main.java | 105 ++++
 .../juneau-examples-petstore-server/pom.xml        |  31 +
 .../main/java/org/apache/juneau/petstore/App.java  |  34 +
 .../apache/juneau/petstore/AppConfiguration.java   |  56 ++
 .../juneau/petstore/rest/PetStoreResource.java     | 685 +++++++++++++++++++++
 .../juneau/petstore/rest/PhotosResource.java       | 327 ++++++++++
 .../apache/juneau/petstore/rest/RootResources.java |  66 ++
 .../juneau/petstore/rest/SqlQueryResource.java     | 249 ++++++++
 .../juneau/petstore/rest/UploadPhotoMenuItem.java  |  61 ++
 .../service/AbstractPersistenceService.java        | 273 ++++++++
 .../juneau/petstore/service/PetStoreService.java   | 329 ++++++++++
 14 files changed, 2675 insertions(+)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PageArgs.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PageArgs.java
new file mode 100644
index 0000000..2487098
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/pojotools/PageArgs.java
@@ -0,0 +1,37 @@
+// 
***************************************************************************************************************************
+// * 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.pojotools;
+
+/**
+ * TODO
+ */
+public class PageArgs {
+
+       /**
+        * TODO
+        *
+        * @return TODO
+        */
+       public int getLimit() {
+               return 0;
+       }
+
+       /**
+        * TODO
+        *
+        * @return TODO
+        */
+       public int getPosition() {
+               return 0;
+       }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-api/src/main/java/org/apache/juneau/petstore/PetStore.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-api/src/main/java/org/apache/juneau/petstore/PetStore.java
new file mode 100644
index 0000000..ef2add7
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-api/src/main/java/org/apache/juneau/petstore/PetStore.java
@@ -0,0 +1,421 @@
+// 
***************************************************************************************************************************
+// * 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.petstore;
+
+import static org.apache.juneau.http.HttpMethodName.*;
+
+import java.util.*;
+
+import org.apache.juneau.jsonschema.annotation.Items;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.http.remote.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.http.response.*;
+
+/**
+ * Defines the interface for both the server-side and client-side pet store 
application.
+ *
+ * <p>
+ * On the server side, this interface is implemented by the 
<c>PetStoreResource</c> class.
+ *
+ * <p>
+ * On the client side, this interface is instantiated as a proxy using the 
<c>RestClient.getRemoteProxy()</c> method.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+@RemoteResource(path="/petstore")
+public interface PetStore {
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Pets
+       
//------------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Returns all pets in the database.
+        *
+        * @return All pets in the database.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @RemoteMethod(method=GET, path="/pet")
+       public Collection<Pet> getPets() throws NotAcceptable;
+
+       /**
+        * Returns a pet from the database.
+        *
+        * @param petId The ID of the pet to retrieve.
+        * @return The pet.
+        * @throws IdNotFound Pet was not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * Adds a pet to the database.
+        *
+        * @param pet The pet data to add to the database.
+        * @return {@link Ok} if successful.
+        * @throws IdConflict ID already in use.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header 
specified.
+        */
+       @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;
+
+       /**
+        * Updates a pet in the database.
+        *
+        * @param pet The pet data to add to the database.
+        * @return {@link Ok} if successful.
+        * @throws IdNotFound ID not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header 
specified.
+        */
+       @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;
+
+       /**
+        * Find all pets with the matching statuses.
+        *
+        * @param status The statuses to match against.
+        * @return The pets that match the specified statuses.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * Find all pets with the specified tags.
+        *
+        * @param tags The tags to match against.
+        * @return The pets that match the specified tags.
+        * @throws InvalidTag Invalid tag was specified.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * Deletes the specified pet.
+        *
+        * @param apiKey Security key.
+        * @param petId ID of pet to delete.
+        * @return {@link Ok} if successful.
+        * @throws IdNotFound Pet not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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
+       
//------------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Returns all orders in the database.
+        *
+        * @return All orders in the database.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @RemoteMethod(method=GET, path="/store/order")
+       public Collection<Order> getOrders() throws NotAcceptable;
+
+       /**
+        * Returns an order from the database.
+        *
+        * @param orderId The ID of the order to retreieve.
+        * @return The retrieved order.
+        * @throws InvalidId ID was invalid.
+        * @throws IdNotFound Order was not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * Adds an order to the database.
+        *
+        * @param petId Id of pet to order.
+        * @param username The username of the user placing the order.
+        * @return The ID of the order.
+        * @throws IdConflict ID was already in use.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header 
specified.
+        */
+       @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;
+
+       /**
+        * Deletes an order from the database.
+        *
+        * @param orderId The order ID.
+        * @return {@link Ok} if successful.
+        * @throws InvalidId ID not valid.
+        * @throws IdNotFound Order not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * Returns an inventory of pet statuses and counts.
+        *
+        * @return An inventory of pet statuses and counts.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @RemoteMethod(method=GET, path="/store/inventory")
+       public Map<PetStatus,Integer> getStoreInventory() throws NotAcceptable;
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Users
+       
//------------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Returns all users in the database.
+        *
+        * @return All users in the database.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @RemoteMethod(method=GET, path="/user")
+       public Collection<User> getUsers() throws NotAcceptable;
+
+       /**
+        * Returns a user from the database.
+        *
+        * @param username The username.
+        * @return The user.
+        * @throws InvalidUsername Invalid username.
+        * @throws IdNotFound username not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * Adds a new user to the database.
+        *
+        * @param user The user to add to the database.
+        * @return {@link Ok} if successful.
+        * @throws InvalidUsername Username was invalid.
+        * @throws IdConflict Username already in use.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header 
specified.
+        */
+       @RemoteMethod
+       public Ok postUser(
+               @Body(
+                       description="Created user object"
+               )
+               User user
+       ) throws InvalidUsername, IdConflict, NotAcceptable, 
UnsupportedMediaType;
+
+       /**
+        * Bulk creates users.
+        *
+        * @param users The users to add to the database.
+        * @return {@link Ok} if successful.
+        * @throws InvalidUsername Username was invalid.
+        * @throws IdConflict Username already in use.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header 
specified.
+        */
+       @RemoteMethod(method=POST, path="/user/createWithArray")
+       public Ok createUsers(
+               @Body(
+                       description="List of user objects"
+               )
+               User[] users
+       ) throws InvalidUsername, IdConflict, NotAcceptable, 
UnsupportedMediaType;
+
+       /**
+        * Updates a user in the database.
+        *
+        * @param username The username.
+        * @param user The updated information.
+        * @return {@link Ok} if successful.
+        * @throws InvalidUsername Username was invalid.
+        * @throws IdNotFound User was not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header 
specified.
+        */
+       @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;
+
+       /**
+        * Deletes a user from the database.
+        *
+        * @param username The username.
+        * @return {@link Ok} if successful.
+        * @throws InvalidUsername Username was not valid.
+        * @throws IdNotFound User was not found.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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;
+
+       /**
+        * User login.
+        *
+        * @param username The username for login.
+        * @param password The password for login in clear text.
+        * @param rateLimit Calls per hour allowed by the user.
+        * @param expiresAfter The <bc>Expires-After</bc> response header.
+        * @param req The servlet request.
+        * @param res The servlet response.
+        * @return {@link Ok} if successful.
+        * @throws InvalidLogin Login was unsuccessful.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @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
+       ) throws InvalidLogin, NotAcceptable;
+
+       /**
+        * User logout.
+        *
+        * @return {@link Ok} if successful.
+        * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+        */
+       @RemoteMethod(method=GET, path="/user/logout")
+       public Ok logout() throws NotAcceptable;
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/pom.xml
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/pom.xml
index ee7f019..dd5aa42 100644
--- 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/pom.xml
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/pom.xml
@@ -43,6 +43,7 @@
 
     <dependencies>
 
+
          <dependency>
             <groupId>org.apache.juneau</groupId>
             <artifactId>juneau-examples-petstore-api</artifactId>
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/src/main/java/org/apache/juneau/petstore/Main.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/src/main/java/org/apache/juneau/petstore/Main.java
new file mode 100644
index 0000000..ecc9c4e
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-client/src/main/java/org/apache/juneau/petstore/Main.java
@@ -0,0 +1,105 @@
+// 
***************************************************************************************************************************
+// * 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.petstore;
+
+import static java.text.MessageFormat.*;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.juneau.json.*;
+import org.apache.juneau.marshall.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.rest.client.*;
+
+/**
+ * Example code showing how to connect to the PetStore application using a 
remote proxy.
+ *
+ * <p>
+ * The remote proxy allows you to make REST calls against our REST interface 
through Java interface method calls.
+ */
+public class Main {
+
+       private static final JsonParser JSON_PARSER = 
JsonParser.create().ignoreUnknownBeanProperties().build();
+
+       public static void main(String[] args) {
+
+               // Create a RestClient with JSON serialization support.
+               try (RestClient rc = 
RestClient.create(SimpleJsonSerializer.class, JsonParser.class).build()) {
+
+                       // Instantiate our proxy.
+                       PetStore petStore = 
rc.getRemoteResource(PetStore.class, "http://localhost:5000";);
+
+                       // Print out the pets in the store.
+                       Collection<Pet> pets = petStore.getPets();
+
+                       // Pretty-print them to SYSOUT.
+                       SimpleJson.DEFAULT_READABLE.println(pets);
+
+                       // Initialize the application through REST calls.
+                       init(new PrintWriter(System.err), petStore);
+
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+
+       /**
+        * Initialize the petstore database by using a remote resource 
interface against our REST.
+        *
+        * @param w Console output.
+        * @return This object (for method chaining).
+        * @throws ParseException Malformed input encountered.
+        * @throws IOException Thrown by client stream.
+        */
+       public static void init(PrintWriter w, PetStore ps) throws 
ParseException, IOException {
+
+               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 : load("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 : load("init/Orders.json", Order[].class)) {
+                       long id = ps.placeOrder(x.getPetId(), x.getUsername());
+                       w.println(format("Created order:  id={0}", id));
+               }
+               for (User x : load("init/Users.json", User[].class)) {
+                       ps.postUser(x);
+                       w.println(format("Created user:  username={0}", 
x.getUsername()));
+               }
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       private static <T> T load(String fileName, Class<T> c) throws 
ParseException, IOException {
+               return JSON_PARSER.parse(getStream(fileName), c);
+       }
+
+       private static InputStream getStream(String fileName) {
+               return Main.class.getResourceAsStream(fileName);
+       }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/pom.xml
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/pom.xml
index 5f70636..504140d 100644
--- 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/pom.xml
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/pom.xml
@@ -42,6 +42,37 @@
 
 
     <dependencies>
+        <!-- Juneau dependencies -->
+        <dependency>
+            <groupId>org.apache.juneau</groupId>
+            <artifactId>juneau-examples-petstore-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.juneau</groupId>
+            <artifactId>juneau-rest-server-springboot</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        
+        <!-- Spring Boot dependencies-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>${springframework.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+               <!-- Needed for @Inject support -->
+               <dependency>
+                       <groupId>javax.inject</groupId>
+                       <artifactId>javax.inject</artifactId>
+                       <version>${javax.inject.version}</version>
+               </dependency>
 
     </dependencies>
 
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/App.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/App.java
new file mode 100644
index 0000000..062b75d
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/App.java
@@ -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.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.petstore;
+
+import org.apache.juneau.rest.springboot.JuneauRestInitializer;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * Entry point for PetStore application.
+ */
+@SpringBootApplication
+public class App {
+
+    public static void main(String[] args) {
+        new App().start(args);
+    }
+
+    protected void start(String[] args) {
+        ConfigurableApplicationContext context = new 
SpringApplicationBuilder(App.class).initializers(new 
JuneauRestInitializer(App.class)).run(args);
+        AppConfiguration.setAppContext(context);
+    }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/AppConfiguration.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/AppConfiguration.java
new file mode 100644
index 0000000..462b274
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/AppConfiguration.java
@@ -0,0 +1,56 @@
+// 
***************************************************************************************************************************
+// * 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.petstore;
+
+import org.apache.juneau.petstore.rest.*;
+import org.apache.juneau.rest.springboot.annotation.JuneauRestRoot;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.*;
+
+@Configuration
+public class AppConfiguration {
+
+    public static String DEFAULT_JDBC_URL = 
"jdbc:h2:mem:testdb;MODE=PostgreSQL";
+    public static String DEFAULT_JDBC_USERNAME = "sa";
+    public static String DEFAULT_JDBC_PASSWORD = "";
+
+    @Autowired
+    private static volatile ApplicationContext appContext;
+
+    public static ApplicationContext getAppContext() {
+        return appContext;
+    }
+
+    public static void setAppContext(ApplicationContext appContext) {
+        AppConfiguration.appContext = appContext;
+    }
+
+    
//-----------------------------------------------------------------------------------------------------------------
+    // Services
+    
//-----------------------------------------------------------------------------------------------------------------
+
+    
//-----------------------------------------------------------------------------------------------------------------
+    // REST
+    
//-----------------------------------------------------------------------------------------------------------------
+
+    @Bean @JuneauRestRoot
+    public RootResources rootResources() {
+        return new RootResources();
+    }
+
+    @Bean
+    public PetStoreResource petStoreResource() {
+        return new PetStoreResource();
+    }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/PetStoreResource.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/PetStoreResource.java
new file mode 100644
index 0000000..49807b3
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/PetStoreResource.java
@@ -0,0 +1,685 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.rest;
+
+import static org.apache.juneau.dto.html5.HtmlBuilder.*;
+import static org.apache.juneau.dto.swagger.ui.SwaggerUI.*;
+import static org.apache.juneau.http.HttpMethodName.*;
+import static org.apache.juneau.http.response.Ok.*;
+
+import java.util.*;
+import java.util.Map;
+
+import javax.inject.*;
+
+import org.apache.juneau.jsonschema.annotation.*;
+import org.apache.juneau.petstore.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.petstore.service.*;
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.dto.html5.*;
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.http.annotation.Path;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.rest.helper.*;
+import org.apache.juneau.http.response.*;
+import org.apache.juneau.rest.widget.*;
+import org.apache.juneau.transforms.*;
+import org.apache.juneau.rest.converters.*;
+
+/**
+ * Sample Petstore application.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+@RestResource(
+       path="/petstore",
+       title="Petstore application",
+       description={
+               "This is a sample server Petstore server based on the Petstore 
sample at Swagger.io.",
+               "You can find out more about Swagger at http://swagger.io.";,
+       },
+       properties= {
+               // Resolve recursive references when showing schema info in the 
swagger.
+               @Property(name=SWAGGERUI_resolveRefsMaxDepth, value="99")
+       },
+       swagger=@ResourceSwagger(
+               version="1.0.0",
+               title="Swagger Petstore",
+               termsOfService="You are on your own.",
+               contact=@Contact(
+                       name="Juneau Development Team",
+                       email="[email protected]",
+                       url="http://juneau.apache.org";
+               ),
+               license=@License(
+                       name="Apache 2.0",
+                       url="http://www.apache.org/licenses/LICENSE-2.0.html";
+               ),
+               externalDocs=@ExternalDocs(
+                       description="Find out more about Juneau",
+                       url="http://juneau.apache.org";
+               ),
+               tags={
+                       @Tag(
+                               name="pet",
+                               description="Everything about your Pets",
+                               externalDocs=@ExternalDocs(
+                                       description="Find out more",
+                                       url="http://juneau.apache.org";
+                               )
+                       ),
+                       @Tag(
+                               name="store",
+                               description="Access to Petstore orders"
+                       ),
+                       @Tag(
+                               name="user",
+                               description="Operations about user",
+                               externalDocs=@ExternalDocs(
+                                       description="Find out more about our 
store",
+                                       url="http://juneau.apache.org";
+                               )
+                       )
+               }
+       ),
+       staticFiles={"htdocs:htdocs"},  // Expose static files in htdocs 
subpackage.
+       children={
+               SqlQueryResource.class,
+               PhotosResource.class
+       }
+)
+@HtmlDocConfig(
+       widgets={
+               ContentTypeMenuItem.class,
+               ThemeMenuItem.class,
+       },
+       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"
+       },
+       head={
+               "<link rel='icon' href='$U{servlet:/htdocs/cat.png}'/>"  // Add 
a cat icon to the page.
+       },
+       header={
+               "<h1>$R{resourceTitle}</h1>",
+               "<h2>$R{methodSummary}</h2>",
+               "$C{PetStore/headerImage}"
+       },
+       aside={
+               "<div style='max-width:400px' class='text'>",
+               "       <p>This page shows a standard nested REST 
resource.</p>",
+               "       <p>It shows how different properties can be rendered on 
the same bean in different views.</p>",
+               "       <p>It also shows examples of HtmlRender classes and 
@BeanProperty(format) annotations.</p>",
+               "       <p>It also shows how the Queryable converter and query 
widget can be used to create searchable interfaces.</p>",
+               "</div>"
+       },
+       stylesheet="servlet:/htdocs/themes/dark.css"  // Use dark theme by 
default.
+)
+public class PetStoreResource extends BasicRest implements PetStore {
+
+       @Inject
+       private PetStoreService store;
+
+       /**
+        * Navigation page
+        *
+        * @return Navigation page contents.
+        */
+       @RestMethod(
+               name=GET,
+               path="/",
+               summary="Navigation page"
+       )
+       @HtmlDocConfig(
+               style={
+                       "INHERIT",  // Flag for inheriting resource-level CSS.
+                       "body { ",
+                               "background-image: 
url('petstore/htdocs/background.jpg'); ",
+                               "background-color: black; ",
+                               "background-size: cover; ",
+                               "background-attachment: fixed; ",
+                       "}"
+               }
+       )
+       public ResourceDescriptions getTopPage() {
+               return new ResourceDescriptions()
+                       .append("pet", "All pets in the store")
+                       .append("store", "Orders and inventory")
+                       .append("user", "Petstore users")
+                       .append("photos", "Photos service")
+                       .append("sql", "SQL query service")
+               ;
+       }
+
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+       // Initialization
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Initialize database form entry page.
+        *
+        * @return Initialize database form entry page contents.
+        */
+       @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();"
+                       )
+               );
+       }
+
+       /**
+        * Initialize database.
+        *
+        * @param initMethod The method used to initialize the database.
+        * @param res HTTP request.
+        * @throws Exception Error occurred.
+        */
+       @RestMethod(
+               summary="Initialize database"
+       )
+       public void postInit(
+               RestResponse res
+       ) throws Exception {
+               res.setHeader("Content-Encoding", "identity");
+               store.initDirect(res.getDirectWriter("text/plain"));
+       }
+
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+       // Pets
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+       @Override /* PetStore */
+       @RestMethod(
+               name=GET,
+               path="/pet",
+               summary="All pets in the store",
+               swagger=@MethodSwagger(
+                       tags="pet",
+                       parameters={
+                               Queryable.SWAGGER_PARAMS
+                       }
+               ),
+               converters={Queryable.class}
+       )
+       @BeanConfig(
+               bpx="Pet: tags,photo"
+       )
+       public Collection<Pet> getPets() throws NotAcceptable {
+               return store.getPets();
+       }
+
+       @Override /* PetStore */
+       @RestMethod(
+               name=GET,
+               path="/pet/{petId}",
+               summary="Find pet by ID",
+               description="Returns a single pet",
+               swagger=@MethodSwagger(
+                       tags="pet",
+                       value={
+                               "security:[ { api_key:[] } ]"
+                       }
+               )
+       )
+       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(
+                       tags="pet",
+                       value={
+                               "security:[ { 
petstore_auth:['write:pets','read:pets'] } ]"
+                       }
+               )
+       )
+       public long postPet(CreatePet pet) throws IdConflict, NotAcceptable, 
UnsupportedMediaType {
+               return store.create(pet).getId();
+       }
+
+       @Override /* PetStore */
+       @RestMethod(
+               name=PUT,
+               path="/pet/{petId}",
+               summary="Update an existing pet",
+               swagger=@MethodSwagger(
+                       tags="pet",
+                       value={
+                               "security:[ { petstore_auth: 
['write:pets','read:pets'] } ]"
+                       }
+               )
+       )
+       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
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Displays the pet edit page.
+        *
+        * @param petId ID of pet to edit
+        * @return Edit page contents.
+        * @throws NotAcceptable Unsupported <bc>Accept</bc> header value 
specified.
+        * @throws UnsupportedMediaType Unsupported <bc>Content-Type</bc> 
header value specified.
+        */
+       @RestMethod(
+               name=GET,
+               path="/pet/{petId}/edit",
+               summary="Pet edit page",
+               swagger=@MethodSwagger(
+                       tags="pet",
+                       value={
+                               "security:[ { 
petstore_auth:['write:pets','read:pets'] } ]"
+                       }
+               )
+       )
+       public Div editPetPage(
+                       @Path(
+                               name="petId",
+                               description="ID of pet to edit",
+                               example="123"
+                       )
+                       long petId
+               ) throws NotAcceptable, UnsupportedMediaType {
+
+               Pet pet = getPet(petId);
+
+               return div(
+                       form().id("form").action("servlet:/pet/" + 
petId).method(POST).children(
+                               table(
+                                       tr(
+                                               th("Id:"),
+                                               
td(input().name("id").type("text").value(petId).readonly(true)),
+                                               td(new Tooltip("&#x2753;", "The 
name of the pet.", br(), "e.g. 'Fluffy'"))
+                                       ),
+                                       tr(
+                                               th("Name:"),
+                                               
td(input().name("name").type("text").value(pet.getName())),
+                                               td(new Tooltip("&#x2753;", "The 
name of the pet.", br(), "e.g. 'Fluffy'"))
+                                       ),
+                                       tr(
+                                               th("Species:"),
+                                               td(
+                                                       
select().name("species").children(
+                                                               option("cat"), 
option("dog"), option("bird"), option("fish"), option("mouse"), 
option("rabbit"), option("snake")
+                                                       
).choose(pet.getSpecies())
+                                               ),
+                                               td(new Tooltip("&#x2753;", "The 
kind of animal."))
+                                       ),
+                                       tr(
+                                               th("Price:"),
+                                               
td(input().name("price").type("number").placeholder("1.0").step("0.01").min(1).max(100).value(pet.getPrice())),
+                                               td(new Tooltip("&#x2753;", "The 
price to charge for this pet."))
+                                       ),
+                                       tr(
+                                               th("Tags:"),
+                                               
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(
+                                               th("Status:"),
+                                               td(
+                                                       
select().name("status").children(
+                                                               
option("AVAILABLE"), option("PENDING"), option("SOLD")
+                                                       
).choose(pet.getStatus())
+                                               ),
+                                               td(new Tooltip("&#x2753;", "The 
current status of the animal."))
+                                       ),
+                                       tr(
+                                               
td().colspan(2).style("text-align:right").children(
+                                                       button("reset", 
"Reset"),
+                                                       
button("button","Cancel").onclick("window.location.href='/'"),
+                                                       button("submit", 
"Submit")
+                                               )
+                                       )
+                               ).style("white-space:nowrap")
+                       )
+               );
+       }
+
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+       // Orders
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Store navigation page.
+        *
+        * @return Store navigation page contents.
+        */
+       @RestMethod(
+               summary="Store navigation page",
+               swagger=@MethodSwagger(
+                       tags="store"
+               )
+       )
+       public ResourceDescriptions getStore() {
+               return new ResourceDescriptions()
+                       .append("store/order", "Petstore orders")
+                       .append("store/inventory", "Petstore inventory")
+               ;
+       }
+
+       @Override
+       @RestMethod(
+               name=GET,
+               path="/store/order",
+               summary="Petstore orders",
+               swagger=@MethodSwagger(
+                       tags="store"
+               )
+       )
+       @HtmlDocConfig(
+               widgets={
+                       QueryMenuItem.class
+               },
+               navlinks={
+                       "INHERIT",                // Inherit links from class.
+                       "[2]:$W{QueryMenuItem}",  // Insert QUERY link in 
position 2.
+                       "[3]:$W{AddOrderMenuItem}"  // Insert ADD link in 
position 3.
+               }
+       )
+       public Collection<Order> getOrders() throws NotAcceptable {
+               return store.getOrders();
+       }
+
+       @Override
+       @RestMethod(
+               name=GET,
+               path="/store/order/{orderId}",
+               summary="Find purchase order by ID",
+               description="Returns a purchase order by ID.",
+               swagger=@MethodSwagger(
+                       tags="store"
+               )
+       )
+       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",
+               summary="Place an order for a pet",
+               swagger=@MethodSwagger(
+                       tags="store"
+               ),
+               pojoSwaps={
+                       TemporalDateSwap.IsoLocalDate.class
+               }
+       )
+       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}",
+               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."
+               },
+               swagger=@MethodSwagger(
+                       tags="store"
+               )
+       )
+       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",
+               summary="Returns pet inventories by status",
+               description="Returns a map of status codes to quantities",
+               swagger=@MethodSwagger(
+                       tags="store",
+                       responses={
+                               "200:{ 'x-example':{AVAILABLE:123} }",
+                       },
+                       value={
+                               "security:[ { api_key:[] } ]"
+                       }
+               )
+       )
+       public Map<PetStatus,Integer> getStoreInventory() throws NotAcceptable {
+               return store.getInventory();
+       }
+
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+       // Users
+       
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+       @Override
+       @RestMethod(
+               name=GET,
+               path="/user",
+               summary="Petstore users",
+               bpx="User: email,password,phone",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       public Collection<User> getUsers() throws NotAcceptable {
+               return store.getUsers();
+       }
+
+       @Override
+       @RestMethod(
+               name=GET,
+               path="/user/{username}",
+               summary="Get user by user name",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       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.",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       public Ok postUser(User user) throws InvalidUsername, IdConflict, 
NotAcceptable, UnsupportedMediaType {
+               store.create(user);
+               return OK;
+       }
+
+       @Override
+       @RestMethod(
+               name=POST,
+               path="/user/createWithArray",
+               summary="Creates list of users with given input array",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       public Ok createUsers(User[] users) throws InvalidUsername, IdConflict, 
NotAcceptable, UnsupportedMediaType {
+               for (User user : users)
+                       store.create(user);
+               return OK;
+       }
+
+       @Override
+       @RestMethod(
+               name=PUT,
+               path="/user/{username}",
+               summary="Update user",
+               description="This can only be done by the logged in user.",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       public Ok updateUser(String username, User user) throws 
InvalidUsername, IdNotFound, NotAcceptable, UnsupportedMediaType {
+               store.update(user);
+               return OK;
+       }
+
+       @Override
+       @RestMethod(
+               name=DELETE,
+               path="/user/{username}",
+               summary="Delete user",
+               description="This can only be done by the logged in user.",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       public Ok deleteUser(String username) throws InvalidUsername, 
IdNotFound, NotAcceptable {
+               store.removeUser(username);
+               return OK;
+       }
+
+       @Override
+       @RestMethod(
+               name=GET,
+               path="/user/login",
+               summary="Logs user into the system",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       public Ok login(
+                       String username,
+                       String password,
+                       Value<Integer> rateLimit,
+                       Value<ExpiresAfter> expiresAfter
+               ) throws InvalidLogin, NotAcceptable {
+
+               RestRequest req = getRequest();
+
+               if (! store.isValid(username, password))
+                       throw new InvalidLogin();
+
+               Date d = new Date(System.currentTimeMillis() + 30 * 60 * 1000);
+               req.getSession().setAttribute("login-expires", d);
+               rateLimit.set(1000);
+               expiresAfter.set(new ExpiresAfter(d));
+               return OK;
+       }
+
+       @Override
+       @RestMethod(
+               name=GET,
+               path="/user/logout",
+               summary="Logs out current logged in user session",
+               swagger=@MethodSwagger(
+                       tags="user"
+               )
+       )
+       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-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/PhotosResource.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/PhotosResource.java
new file mode 100644
index 0000000..1eeb058
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/PhotosResource.java
@@ -0,0 +1,327 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.rest;
+
+import static org.apache.juneau.http.HttpMethodName.*;
+
+import org.apache.juneau.jsonschema.annotation.ExternalDocs;
+import org.apache.juneau.jsonschema.annotation.Schema;
+import java.awt.image.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.Map;
+
+import javax.imageio.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.http.annotation.Body;
+import org.apache.juneau.http.annotation.Path;
+import org.apache.juneau.http.annotation.Response;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.rest.helper.*;
+import org.apache.juneau.rest.matchers.*;
+import org.apache.juneau.serializer.*;
+
+/**
+ * Sample resource that allows images to be uploaded and retrieved.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+@RestResource(
+       path="/photos",
+       messages="nls/PhotosResource",
+       title="Photo REST service",
+       description="Sample resource that allows images to be uploaded and 
retrieved.",
+       swagger=@ResourceSwagger(
+               contact=@Contact(name="Juneau 
Developer",email="[email protected]"),
+               license=@License(name="Apache 
2.0",url="http://www.apache.org/licenses/LICENSE-2.0.html";),
+               version="2.0",
+               termsOfService="You are on your own.",
+               externalDocs=@ExternalDocs(description="Apache 
Juneau",url="http://juneau.apache.org";)
+       )
+)
+@HtmlDocConfig(
+       navlinks={
+               "up: request:/..",
+               "options: servlet:/?method=OPTIONS",
+               "$W{UploadPhotoMenuItem}",
+               "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"
+       },
+       aside={
+               "<div style='max-width:400px;min-width:200px' class='text'>",
+               "       <p>Shows an example of using custom serializers and 
parsers to create REST interfaces over binary resources.</p>",
+               "       <p>In this case, our resources are marshalled jpeg and 
png binary streams and are stored in an in-memory 'database' (also known as a 
<c>TreeMap</c>).</p>",
+               "</div>"
+       },
+       widgets={
+               UploadPhotoMenuItem.class
+       },
+       stylesheet="servlet:/htdocs/themes/dark.css"
+)
+@HtmlConfig(
+       // Make the anchor text on URLs be just the path relative to the 
servlet.
+       uriAnchorText="SERVLET_RELATIVE"
+)
+public class PhotosResource extends BasicRestServlet {
+       private static final long serialVersionUID = 1L;
+
+       // Our cache of photos
+       private Map<String,Photo> photos = new TreeMap<>();
+
+       @Override /* Servlet */
+       public void init() {
+               try (InputStream is = 
getClass().getResourceAsStream("photos/cat.jpg")) {
+                       BufferedImage image = ImageIO.read(is);
+                       Photo photo = new Photo("cat", image);
+                       photos.put(photo.id, photo);
+               } catch (IOException e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /** Our bean class for storing photos */
+       public static class Photo {
+               String id;
+               BufferedImage image;
+
+               Photo(String id, BufferedImage image) {
+                       this.id = id;
+                       this.image = image;
+               }
+
+               /**
+                * @return The URI of this photo.
+                * @throws URISyntaxException ID could not be converted to a 
URI.
+                */
+               public URI getURI() throws URISyntaxException {
+                       return new URI("servlet:/" + id);
+               }
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // REST methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Show the list of all currently loaded photos
+        *
+        * @return The list of all currently loaded photos.
+        * @throws Exception Error occurred.
+        */
+       @RestMethod(
+               name=GET,
+               path="/",
+               summary="Show the list of all currently loaded photos"
+       )
+       public Collection<Photo> getAllPhotos() throws Exception {
+               return photos.values();
+       }
+
+       /**
+        * Shows how to use a custom serializer to serialize a BufferedImage 
object to a stream.
+        *
+        * @param id The photo ID.
+        * @return The image.
+        * @throws NotFound Image was not found.
+        */
+       @RestMethod(
+               name=GET,
+               path="/{id}",
+               serializers=ImageSerializer.class,
+               summary="Get a photo by ID",
+               description="Shows how to use a custom serializer to serialize 
a BufferedImage object to a stream."
+       )
+       @Response(
+               schema=@Schema(type="file")
+       )
+       public BufferedImage getPhoto(@Path("id") String id) throws NotFound {
+               Photo p = photos.get(id);
+               if (p == null)
+                       throw new NotFound("Photo not found");
+               return p.image;
+       }
+
+       /**
+        * Shows how to use a custom parser to parse a stream into a 
BufferedImage object.
+        *
+        * @param id The photo ID.
+        * @param image Binary contents of image.
+        * @return <js>"OK"</jk> if successful.
+        * @throws Exception Error occurred.
+        */
+       @RestMethod(
+               name=PUT,
+               path="/{id}",
+               parsers=ImageParser.class,
+               summary="Add or overwrite a photo",
+               description="Shows how to use a custom parser to parse a stream 
into a BufferedImage object."
+       )
+       public String addPhoto(
+                       @Path("id") String id,
+                       @Body(
+                               description="Binary contents of image.",
+                               schema=@Schema(type="file")
+                       )
+                       BufferedImage image
+               ) throws Exception {
+               photos.put(id, new Photo(id, image));
+               return "OK";
+       }
+
+       /**
+        * Shows how to use a custom parser to parse a stream into a 
BufferedImage object.
+        *
+        * @param image Binary contents of image.
+        * @return The Photo bean.
+        * @throws Exception Error occurred.
+        */
+       @RestMethod(
+               name=POST,
+               path="/",
+               parsers=ImageParser.class,
+               summary="Add a photo",
+               description="Shows how to use a custom parser to parse a stream 
into a BufferedImage object."
+       )
+       public Photo setPhoto(
+                       @Body(
+                               description="Binary contents of image.",
+                               schema=@Schema(type="file")
+                       )
+                       BufferedImage image
+               ) throws Exception {
+               Photo p = new Photo(UUID.randomUUID().toString(), image);
+               photos.put(p.id, p);
+               return p;
+       }
+
+       /**
+        * Upload a photo from a multipart form post.
+        *
+        * @param req HTTP request.
+        * @return Redirect to servlet root.
+        * @throws Exception Error occurred.
+        */
+       @RestMethod(
+               name=POST,
+               path="/upload",
+               matchers=MultipartFormDataMatcher.class,
+               summary="Upload a photo from a multipart form post",
+               description="Shows how to parse a multipart form post 
containing a binary field.",
+               swagger=@MethodSwagger(
+                       parameters={
+                               "{in:'formData', name:'id', description:'Unique 
identifier to assign to image.', type:'string', required:false},",
+                               "{in:'formData', name:'file', description:'The 
binary contents of the image file.', type:'file', required:true}"
+                       }
+               )
+       )
+    public SeeOtherRoot uploadFile(RestRequest req) throws Exception {
+       MultipartConfigElement multipartConfigElement = new 
MultipartConfigElement((String)null);
+       req.setAttribute("org.eclipse.jetty.multipartConfig", 
multipartConfigElement);
+       String id = UUID.randomUUID().toString();
+       BufferedImage img = null;
+       for (Part part : req.getParts()) {
+          switch (part.getName()) {
+                  case "id":
+                          id = IOUtils.read(part.getInputStream());
+                          break;
+                  case "file":
+                          img = ImageIO.read(part.getInputStream());
+          }
+       }
+       addPhoto(id, img);
+       return new SeeOtherRoot(); // Redirect to the servlet root.
+    }
+
+       /**
+        * Removes a photo from the database.
+        *
+        * @param id ID of photo to remove.
+        * @return <js>"OK"</jk> if successful.
+        * @throws NotFound Photo was not found.
+        */
+       @RestMethod(
+               name=DELETE,
+               path="/{id}",
+               summary="Delete a photo by ID"
+       )
+       public String deletePhoto(@Path("id") String id) throws NotFound {
+               Photo p = photos.remove(id);
+               if (p == null)
+                       throw new NotFound("Photo not found");
+               return "OK";
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Custom serializers and parsers.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /** Serializer for converting images to byte streams */
+       public static class ImageSerializer extends OutputStreamSerializer {
+
+               /**
+                * Constructor.
+                * @param ps The property store containing all the settings for 
this object.
+                */
+               public ImageSerializer(PropertyStore ps) {
+                       super(ps, null, "image/png,image/jpeg");
+               }
+
+               @Override /* Serializer */
+               public OutputStreamSerializerSession 
createSession(SerializerSessionArgs args) {
+                       return new OutputStreamSerializerSession(args) {
+
+                               @Override /* SerializerSession */
+                               protected void doSerialize(SerializerPipe out, 
Object o) throws IOException, SerializeException {
+                                       RenderedImage image = (RenderedImage)o;
+                                       String mediaType = 
getProperty("mediaType", String.class, (String)null);
+                                       ImageIO.write(image, 
mediaType.substring(mediaType.indexOf('/')+1), out.getOutputStream());
+                               }
+                       };
+               }
+       }
+
+       /** Parser for converting byte streams to images */
+       public static class ImageParser extends InputStreamParser {
+
+               /**
+                * Constructor.
+                * @param ps The property store containing all the settings for 
this object.
+                */
+               public ImageParser(PropertyStore ps) {
+                       super(ps, "image/png", "image/jpeg");
+               }
+
+               @Override /* Parser */
+               public InputStreamParserSession createSession(final 
ParserSessionArgs args) {
+                       return new InputStreamParserSession(args) {
+
+                               @Override /* ParserSession */
+                               @SuppressWarnings("unchecked")
+                               protected <T> T doParse(ParserPipe pipe, 
ClassMeta<T> type) throws IOException, ParseException, ExecutableException {
+                                       return 
(T)ImageIO.read(pipe.getInputStream());
+                               }
+                       };
+               }
+       }
+}
\ No newline at end of file
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/RootResources.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/RootResources.java
new file mode 100644
index 0000000..424b8f3
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/RootResources.java
@@ -0,0 +1,66 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.rest;
+
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.widget.*;
+import org.apache.juneau.serializer.annotation.*;
+
+/**
+ * Sample REST resource showing how to implement a "router" resource page.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+@RestResource(
+       path="/*",
+       title="Root resources",
+       description="Example of a router resource page.",
+       children={
+               PetStoreResource.class
+       }
+)
+@HtmlDocConfig(
+       widgets={
+               ContentTypeMenuItem.class,
+               ThemeMenuItem.class
+       },
+       navlinks={
+               "options: ?method=OPTIONS",
+               "$W{ContentTypeMenuItem}",
+               "$W{ThemeMenuItem}",
+               "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"
+       },
+       aside={
+               "<div style='max-width:400px' class='text'>",
+               "       <p>This is an example of a 'router' page that serves as 
a jumping-off point to child resources.</p>",
+               "       <p>Resources can be nested arbitrarily deep through 
router pages.</p>",
+               "       <p>Note the <span class='link'>options</span> link 
provided that lets you see the generated swagger doc for this page.</p>",
+               "       <p>Also note the <span class='link'>sources</span> link 
on these pages to view the source code for the page.</p>",
+               "       <p>All content on pages in the UI are serialized POJOs. 
 In this case, it's a serialized array of beans with 2 properties, 'name' and 
'description'.</p>",
+               "       <p>Other features (such as this aside) are added 
through annotations.</p>",
+               "</div>"
+       }
+)
+@SerializerConfig(
+       // For testing purposes, we want to use single quotes in all the 
serializers so it's easier to do simple
+       // String comparisons.
+       // You can apply any of the Serializer/Parser/BeanContext settings this 
way.
+       quoteChar="'"
+)
+public class RootResources extends BasicRestServletGroup {
+       private static final long serialVersionUID = 1L;
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/SqlQueryResource.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/SqlQueryResource.java
new file mode 100644
index 0000000..de015b0
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/SqlQueryResource.java
@@ -0,0 +1,249 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.rest;
+
+import static org.apache.juneau.dto.html5.HtmlBuilder.*;
+import static org.apache.juneau.http.HttpMethodName.*;
+import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.rest.annotation.HookEvent.*;
+
+import java.sql.*;
+import java.util.*;
+
+import org.apache.juneau.jsonschema.annotation.ExternalDocs;
+import org.apache.juneau.config.*;
+import org.apache.juneau.dto.*;
+import org.apache.juneau.dto.html5.*;
+import org.apache.juneau.html.annotation.*;
+import org.apache.juneau.http.annotation.*;
+import org.apache.juneau.http.annotation.Body;
+import org.apache.juneau.http.annotation.Query;
+import org.apache.juneau.http.annotation.Response;
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.http.exception.*;
+import org.apache.juneau.rest.widget.*;
+
+/**
+ * Sample resource that shows how Juneau can serialize ResultSets.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+@RestResource(
+       path="/sql",
+       title="SQL query service",
+       description="Executes queries against the local derby 
'$C{SqlQueryResource/connectionUrl}' database",
+       swagger=@ResourceSwagger(
+               contact=@Contact(name="Juneau 
Developer",email="[email protected]"),
+               license=@License(name="Apache 
2.0",url="http://www.apache.org/licenses/LICENSE-2.0.html";),
+               version="2.0",
+               termsOfService="You are on your own.",
+               externalDocs=@ExternalDocs(description="Apache 
Juneau",url="http://juneau.apache.org";)
+       )
+)
+@HtmlDocConfig(
+       widgets={
+               ThemeMenuItem.class
+       },
+       navlinks={
+               "up: request:/..",
+               "options: servlet:/?method=OPTIONS",
+               "$W{ThemeMenuItem}",
+               "source: 
$C{Source/gitHub}/org/apache/juneau/examples/rest/$R{servletClassSimple}.java"
+       },
+       aside={
+               "<div style='min-width:200px' class='text'>",
+               "       <p>An example of a REST interface over a relational 
database that serializes ResultSet objects.</p>",
+               "       <p>Specify one or more queries delimited by 
semicolons.</p>",
+               "       <h5>Examples:</h5>",
+               "       <ul>",
+               "               <li><a class='link' 
href='?sql=select+*+from+sys.systables'>Tables</a>",
+               "               <li><a class='link' 
href='?sql=select+*+from+PetstorePet'>Pets</a>",
+               "               <li><a class='link' 
href='?sql=select+*+from+PetstoreOrder'>Orders</a>",
+               "               <li><a class='link' 
href='?sql=select+*+from+PetstoreUser'>Users</a>",
+               "       </ul>",
+               "</div>"
+       },
+       stylesheet="servlet:/htdocs/themes/dark.css"
+)
+public class SqlQueryResource extends BasicRestServlet {
+       private static final long serialVersionUID = 1L;
+
+       private String driver, connectionUrl;
+       private boolean allowUpdates, allowTempUpdates, includeRowNums;
+
+       /**
+        * Initializes the registry URL and rest client.
+        *
+        * @param builder The resource config.
+        */
+       @RestHook(INIT)
+       public void initConnection(RestContextBuilder builder) {
+               Config cf = builder.getConfig();
+
+               driver = cf.getString("SqlQueryResource/driver");
+               connectionUrl = cf.getString("SqlQueryResource/connectionUrl");
+               allowUpdates = cf.getBoolean("SqlQueryResource/allowUpdates", 
false);
+               allowTempUpdates = 
cf.getBoolean("SqlQueryResource/allowTempUpdates", false);
+               includeRowNums = 
cf.getBoolean("SqlQueryResource/includeRowNums", false);
+
+               try {
+                       Class.forName(driver).newInstance();
+               } catch (Exception e) {
+                       e.printStackTrace(System.err);
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /**
+        * Displays the query entry page.
+        *
+        * @param sql Text to prepopulate the SQL query field with.
+        * @return The HTML div tag to serialize.
+        */
+       @RestMethod(
+               summary="Display the query entry page"
+       )
+       public Div get(
+                       @Query(
+                               name="sql",
+                               description="Text to prepopulate the SQL query 
field with.",
+                               example="select * from sys.systables"
+                       )
+                       String sql
+               ) {
+
+               return div(
+                       script("text/javascript",
+                               "// Quick and dirty function to allow tabs in 
textarea.",
+                               "function checkTab(e) {",
+                               "       if (e.keyCode == 9) {",
+                               "               var t = e.target;",
+                               "               var ss = t.selectionStart, se = 
t.selectionEnd;",
+                               "               t.value = 
t.value.slice(0,ss).concat('\\t').concat(t.value.slice(ss,t.value.length));",
+                               "               e.preventDefault();",
+                               "       }",
+                               "}",
+                               "// Load results from IFrame into this 
document.",
+                               "function loadResults(b) {",
+                               "       var doc = b.contentDocument || 
b.contentWindow.document;",
+                               "       var data = doc.getElementById('data') 
|| doc.getElementsByTagName('body')[0];",
+                               "       
document.getElementById('results').innerHTML = data.innerHTML;",
+                               "}"
+                       ),
+                       form("servlet:/").method(POST).target("buf").children(
+                               table(
+                                       tr(
+                                               th("Position 
(1-10000):").style("white-space:nowrap"),
+                                               
td(input().name("pos").type("number").value(1)),
+                                               th("Limit 
(1-10000):").style("white-space:nowrap"),
+                                               
td(input().name("limit").type("number").value(100)),
+                                               td(button("submit", "Submit"), 
button("reset", "Reset"))
+                                       ),
+                                       tr(
+                                               td().colspan(5).children(
+                                                       
textarea().name("sql").text(sql == null ? " " : 
sql).style("width:100%;height:200px;font-family:Courier;font-size:9pt;").onkeydown("checkTab(event)")
+                                               )
+                                       )
+                               )
+                       ),
+                       br(),
+                       div().id("results"),
+                       
iframe().name("buf").style("display:none").onload("parent.loadResults(this)")
+               );
+       }
+
+       /**
+        * Execute one or more queries.
+        *
+        * @param in
+        *      Query input
+        * @return
+        *      Query results.
+        *      <br>Each entry in the array is a result of one query.
+        *      <b>Each result can be a result set (for queries) or update 
count (for updates).
+        * @throws BadRequest Invalid SQL detected.
+        */
+       @RestMethod(
+               summary="Execute one or more queries"
+       )
+       @Response(
+               description="Query results.\nEach entry in the array is a 
result of one query.\nEach result can be a result set (for queries) or update 
count (for updates)."
+       )
+       public List<Object> post(
+                       @Body(
+                               description="Query input",
+                               example="{sql:'select * from 
sys.systables',pos:1,limit:100}"
+                       )
+                       PostInput in
+               ) throws BadRequest {
+
+               List<Object> results = new LinkedList<>();
+
+               // Don't try to submit empty input.
+               if (isEmpty(in.sql))
+                       return results;
+
+               if (in.pos < 1 || in.pos > 10000)
+                       throw new BadRequest("Invalid value for position.  Must 
be between 1-10000");
+               if (in.limit < 1 || in.limit > 10000)
+                       throw new BadRequest("Invalid value for limit.  Must be 
between 1-10000");
+
+               String sql = null;
+
+               // Create a connection and statement.
+               // If these fais, let the exception filter up as a 500 error.
+               try (Connection c = DriverManager.getConnection(connectionUrl)) 
{
+                       c.setAutoCommit(false);
+                       try (Statement st = c.createStatement()) {
+                               for (String s : in.sql.split(";")) {
+                                       sql = s.trim();
+                                       if (! sql.isEmpty()) {
+                                               Object o = null;
+                                               if (allowUpdates || 
(allowTempUpdates && ! sql.matches("(?:i)commit.*"))) {
+                                                       if (st.execute(sql)) {
+                                                               try (ResultSet 
rs = st.getResultSet()) {
+                                                                       o = new 
ResultSetList(rs, in.pos, in.limit, includeRowNums);
+                                                               }
+                                                       } else {
+                                                               o = 
st.getUpdateCount();
+                                                       }
+                                               } else {
+                                                       try (ResultSet rs = 
st.executeQuery(sql)) {
+                                                               o = new 
ResultSetList(rs, in.pos, in.limit, includeRowNums);
+                                                       }
+                                               }
+                                               results.add(o);
+                                       }
+                               }
+                       }
+                       if (allowUpdates)
+                               c.commit();
+                       else if (allowTempUpdates)
+                               c.rollback();
+               } catch (SQLException e) {
+                       throw new BadRequest(e, "Invalid query:  {0}", sql);
+               }
+
+               return results;
+       }
+
+       /** The parsed form post */
+       @SuppressWarnings("javadoc")
+       public static class PostInput {
+               public String sql = "";
+               public int pos = 1, limit = 100;
+       }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/UploadPhotoMenuItem.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/UploadPhotoMenuItem.java
new file mode 100644
index 0000000..3847890
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/rest/UploadPhotoMenuItem.java
@@ -0,0 +1,61 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.rest;
+
+import static org.apache.juneau.dto.html5.HtmlBuilder.*;
+import static org.apache.juneau.http.HttpMethodName.*;
+
+import org.apache.juneau.rest.*;
+import org.apache.juneau.rest.widget.*;
+
+/**
+ * Menu item for uploading a Photo.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+public class UploadPhotoMenuItem extends MenuItemWidget {
+
+       @Override /* MenuItemWidget */
+       public String getLabel(RestRequest req, RestResponse res) throws 
Exception {
+               return "upload";
+       }
+
+       @Override /* Widget */
+       public Object getContent(RestRequest req, RestResponse res) throws 
Exception {
+               return div(
+                       
form().id("form").action("servlet:/upload").method(POST).enctype("multipart/form-data").children(
+                               table(
+                                       tr(
+                                               th("ID:"),
+                                               
td(input().name("id").type("text")),
+                                               td(new Tooltip("&#x2753;", "The 
unique identifier of the photo.", br(), "e.g. 'Fluffy'"))
+                                       ),
+                                       tr(
+                                               th("File:"),
+                                               
td(input().name("file").type("file").accept("image/*")),
+                                               td(new Tooltip("&#x2753;", "The 
image file."))
+                                       ),
+                                       tr(
+                                               
td().colspan(2).style("text-align:right").children(
+                                                       button("reset", 
"Reset"),
+                                                       
button("button","Cancel").onclick("window.location.href='/'"),
+                                                       button("submit", 
"Submit")
+                                               )
+                                       )
+                               ).style("white-space:nowrap")
+                       )
+               );
+       }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/service/AbstractPersistenceService.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/service/AbstractPersistenceService.java
new file mode 100644
index 0000000..4e2aedf
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/service/AbstractPersistenceService.java
@@ -0,0 +1,273 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.service;
+
+import java.util.*;
+
+import javax.persistence.*;
+
+import org.apache.juneau.pojotools.*;
+
+/**
+ * Superclass for DAOs that use the JPA entity manager.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+public class AbstractPersistenceService {
+
+       private final EntityManagerFactory entityManagerFactory;
+
+       /**
+        * Constructor.
+        */
+       public AbstractPersistenceService() {
+               entityManagerFactory = 
Persistence.createEntityManagerFactory("test");
+       }
+
+       /**
+        * Retrieves an entity manager session.
+        *
+        * @return The entity manager session.
+        */
+       protected EntityManager getEntityManager() {
+               return entityManagerFactory.createEntityManager();
+       }
+
+       /**
+        * Retrieves the specified JPA bean from the repository.
+        *
+        * @param em The entity manager to use to retrieve the bean.
+        * @param t The bean type to retrieve.
+        * @param id The primary key value.
+        * @return The JPA bean, or null if not found.
+        */
+       protected <T> T find(EntityManager em, Class<T> t, Object id) {
+               return em.find(t, id);
+       }
+
+       /**
+        * Same as {@link #find(EntityManager, Class, Object)} but uses a new 
entity manager.
+        *
+        * @param t The bean type to retrieve.
+        * @param id The primary key value.
+        * @return The JPA bean, or null if not found.
+        */
+       protected <T> T find(Class<T> t, Object id) {
+               return find(getEntityManager(), t, id);
+       }
+
+       /**
+        * Store the specified JPA bean in the repository.
+        *
+        * @param em The entity manager to use to store and merge the bean.
+        * @param t The bean to store.
+        * @return The merged JPA bean returned by the {@link 
EntityManager#merge(Object)} method, or null if the bean was null.
+        */
+       protected <T> T merge(EntityManager em, T t) {
+               if (t == null)
+                       return null;
+               try {
+                       EntityTransaction et = em.getTransaction();
+                       et.begin();
+                       t = em.merge(t);
+                       et.commit();
+                       return t;
+               } finally {
+                       em.close();
+               }
+       }
+
+       /**
+        * Same as {@link #merge(EntityManager, Object)} but uses a new entity 
manager.
+        *
+        * @param t The bean to store.
+        * @return The merged JPA bean returned by the {@link 
EntityManager#merge(Object)} method, or null if the bean was null.
+        */
+       protected <T> T merge(T t) {
+               return merge(getEntityManager(), t);
+       }
+
+       /**
+        * Store the specified JPA beans in the repository.
+        *
+        * All values are persisted in the same transaction.
+        *
+        * @param em The entity manager to use to store and merge the beans.
+        * @param c The collection of beans to store.
+        * @return The merged JPA beans returned by the {@link 
EntityManager#merge(Object)} method.
+        */
+       protected <T> Collection<T> merge(EntityManager em, Collection<T> c) {
+               Collection<T> c2 = new ArrayList<>();
+               try {
+                       EntityTransaction et = em.getTransaction();
+                       et.begin();
+                       for (T t : c)
+                               c2.add(em.merge(t));
+                       et.commit();
+                       return c2;
+               } finally {
+                       em.close();
+               }
+       }
+
+       /**
+        * Same as {@link #merge(EntityManager, Collection)} but uses a new 
entity manager.
+        *
+        * @param c The collection of beans to store.
+        * @return The merged JPA beans returned by the {@link 
EntityManager#merge(Object)} method.
+        */
+       protected <T> Collection<T> merge(Collection<T> c) {
+               return merge(getEntityManager(), c);
+       }
+
+       /**
+        * Remove the specified JPA bean from the repository.
+        *
+        * @param t The bean type to remove.
+        * @param id The primary key value.
+        */
+       protected <T> void remove(Class<T> t, Object id) {
+               EntityManager em = getEntityManager();
+               remove(em, find(em, t, id));
+       }
+
+       /**
+        * Remove the specified JPA bean from the repository.
+        *
+        * @param em The entity manager used to retrieve the bean.
+        * @param t The bean to remove.  Can be null.
+        */
+       protected <T> void remove(EntityManager em, T t) {
+               if (t == null)
+                       return;
+               try {
+                       EntityTransaction et = em.getTransaction();
+                       et.begin();
+                       em.remove(t);
+                       et.commit();
+               } finally {
+                       em.close();
+               }
+       }
+
+       /**
+        * Runs a JPA query and returns the results.
+        *
+        * @param <T> The bean type.
+        * @param em The entity manager to use to retrieve the beans.
+        * @param query The JPA query.
+        * @param t The bean type.
+        * @param searchArgs Optional search arguments.
+        * @return The results.
+        */
+       protected <T> List<T> query(EntityManager em, String query, Class<T> t, 
SearchArgs searchArgs, PageArgs pageArgs) {
+               TypedQuery<T> q = em.createQuery(query, t);
+               if (pageArgs != null) {
+                       q.setMaxResults(pageArgs.getLimit() == 0 ? 100 : 
pageArgs.getLimit());
+                       q.setFirstResult(pageArgs.getPosition());
+               }
+               return em.createQuery(query, t).getResultList();
+       }
+
+       /**
+        * Same as {@link #query(EntityManager,String,Class,SearchArgs)} but 
uses a new entity manager.
+        *
+        * @param <T> The bean type.
+        * @param query The JPA query.
+        * @param t The bean type.
+        * @param searchArgs Optional search arguments.
+        * @return The results.
+        */
+       protected <T> List<T> query(String query, Class<T> t, SearchArgs 
searchArgs, PageArgs pageArgs) {
+               return query(getEntityManager(), query, t, searchArgs, 
pageArgs);
+       }
+
+       /**
+        * Runs a JPA parameterized query and returns the results.
+        *
+        * @param em The entity manager to use to retrieve the beans.
+        * @param query The JPA query.
+        * @param t The bean type.
+        * @param params The query parameter values.
+        * @return The results.
+        */
+       protected <T> List<T> query(EntityManager em, String query, Class<T> t, 
Map<String,Object> params) {
+               TypedQuery<T> tq = em.createQuery(query, t);
+               for (Map.Entry<String,Object> e : params.entrySet()) {
+                       tq.setParameter(e.getKey(), e.getValue());
+               }
+               return tq.getResultList();
+       }
+
+       /**
+        * Same as {@link #query(EntityManager,String,Class,Map)} but uses a 
new entity manager.
+        *
+        * @param query The JPA query.
+        * @param t The bean type.
+        * @param params The query parameter values.
+        * @return The results.
+        */
+       protected <T> List<T> query(String query, Class<T> t, 
Map<String,Object> params) {
+               return query(getEntityManager(), query, t, params);
+       }
+
+       /**
+        * Runs a JPA update statement.
+        *
+        * @param em The entity manager to use to run the statement.
+        * @param query The JPA update statement.
+        * @return The number of rows modified.
+        */
+       protected int update(EntityManager em, String query) {
+               return em.createQuery(query).executeUpdate();
+       }
+
+       /**
+        * Same as {@link #update(EntityManager,String)} but uses a new entity 
manager.
+        *
+        * @param query The JPA update statement.
+        * @return The number of rows modified.
+        */
+       protected int update(String query) {
+               return update(getEntityManager(), query);
+       }
+
+       /**
+        * Runs a JPA parameterized update statement.
+        *
+        * @param em The entity manager to use to run the statement.
+        * @param query The JPA update statement.
+        * @param params The query parameter values.
+        * @return The number of rows modified.
+        */
+       protected int update(EntityManager em, String query, Map<String,Object> 
params) {
+               Query q = em.createQuery(query);
+               for (Map.Entry<String,Object> e : params.entrySet()) {
+                       q.setParameter(e.getKey(), e.getValue());
+               }
+               return q.executeUpdate();
+       }
+
+       /**
+        * Same as {@link #update(EntityManager,String,Map)} but uses a new 
entity manager.
+        *
+        * @param query The JPA update statement.
+        * @param params The query parameter values.
+        * @return The number of rows modified.
+        */
+       protected int update(String query, Map<String,Object> params) {
+               return update(getEntityManager(), query, params);
+       }
+}
diff --git 
a/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/service/PetStoreService.java
 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/service/PetStoreService.java
new file mode 100644
index 0000000..ac83c4c
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-petstore/juneau-examples-petstore-server/src/main/java/org/apache/juneau/petstore/service/PetStoreService.java
@@ -0,0 +1,329 @@
+// 
***************************************************************************************************************************
+// * 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.petstore.service;
+
+import static java.text.MessageFormat.*;
+
+import java.io.*;
+import java.util.*;
+
+import javax.persistence.*;
+
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.petstore.dto.*;
+import org.apache.juneau.pojotools.*;
+import org.apache.juneau.pojotools.SearchArgs;
+
+/**
+ * Pet store database application.
+ * <p>
+ * Uses JPA persistence to store and retrieve PetStore DTOs.
+ * JPA beans are defined in <c>META-INF/persistence.xml</c>.
+ *
+ * <ul class='seealso'>
+ *     <li class='extlink'>{@source}
+ * </ul>
+ */
+public class PetStoreService extends AbstractPersistenceService {
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Initialization methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Initialize the petstore database using JPA.
+        *
+        * @param w Console output.
+        * @return This object (for method chaining).
+        * @throws ParseException Malformed input encountered.
+        * @throws IOException File could not be read from file system.
+        */
+       public PetStoreService initDirect(PrintWriter w) throws ParseException, 
IOException {
+
+               EntityManager em = getEntityManager();
+               EntityTransaction et = em.getTransaction();
+               JsonParser parser = JsonParser.create().build();
+
+               et.begin();
+
+               for (Pet x : em.createQuery("select X from PetstorePet 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;
+       }
+
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Service methods.
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /**
+        * Returns the pet with the specified ID.
+        *
+        * @param id The pet ID.
+        * @return The pet with the specified ID.  Never <jk>null</jk>.
+        * @throws IdNotFound If pet was not found.
+        */
+       public Pet getPet(long id) throws IdNotFound {
+               return find(Pet.class, id);
+       }
+
+       /**
+        * Returns the order with the specified ID.
+        *
+        * @param id The order ID.
+        * @return The order with the specified ID.  Never <jk>null</jk>.
+        * @throws IdNotFound If order was not found.
+        */
+       public Order getOrder(long id) throws IdNotFound {
+               return find(Order.class, id);
+       }
+
+       /**
+        * Returns the user with the specified username.
+        *
+        * @param username The username.
+        * @return The user with the specified username.  Never <jk>null</jk>.
+        * @throws InvalidUsername Username was not valid.
+        * @throws IdNotFound If order was not found.
+        */
+       public User getUser(String username) throws InvalidUsername, IdNotFound 
 {
+               assertValidUsername(username);
+               return find(User.class, username);
+       }
+
+       /**
+        * Returns all pets in the database.
+        *
+        * @return All pets in the database.
+        */
+       public List<Pet> getPets() {
+               return query("select X from PetstorePet X", Pet.class, 
(SearchArgs)null, (PageArgs)null);
+       }
+
+       /**
+        * Returns all orders in the database.
+        *
+        * @return All orders in the database.
+        */
+       public List<Order> getOrders() {
+               return query("select X from PetstoreOrder X", Order.class, 
(SearchArgs)null, (PageArgs)null);
+       }
+
+       /**
+        * Returns all users in the database.
+        *
+        * @return All users in the database.
+        */
+       public List<User> getUsers() {
+               return query("select X from PetstoreUser X", User.class, 
(SearchArgs)null, (PageArgs)null);
+       }
+
+       /**
+        * Creates a new pet in the database.
+        *
+        * @param c The pet input data.
+        * @return a new {@link Pet} object.
+        */
+       public Pet create(CreatePet c) {
+               return merge(new Pet().status(PetStatus.AVAILABLE).apply(c));
+       }
+
+       /**
+        * Creates a new order in the database.
+        *
+        * @param c The order input data.
+        * @return a new {@link Order} object.
+        */
+       public Order create(CreateOrder c) {
+               return merge(new Order().status(OrderStatus.PLACED).apply(c));
+       }
+
+       /**
+        * Creates a new user in the database.
+        *
+        * @param c The user input data.
+        * @return a new {@link User} object.
+        */
+       public User create(User c) {
+               return merge(new User().apply(c));
+       }
+
+       /**
+        * Updates a pet in the database.
+        *
+        * @param u The update information.
+        * @return The updated {@link Pet} object.
+        * @throws IdNotFound Pet was not found.
+        */
+       public Pet update(UpdatePet u) throws IdNotFound {
+               EntityManager em = getEntityManager();
+               return merge(em, find(em, Pet.class, u.getId()).apply(u));
+       }
+
+       /**
+        * Updates an order in the database.
+        *
+        * @param o The update information.
+        * @return The updated {@link Order} object.
+        * @throws IdNotFound Order was not found.
+        */
+       public Order update(Order o) throws IdNotFound {
+               EntityManager em = getEntityManager();
+               return merge(em, find(em, Order.class, o.getId()).apply(o));
+       }
+
+       /**
+        * Updates a user in the database.
+        *
+        * @param u The update information.
+        * @return The updated {@link User} object.
+        * @throws IdNotFound User was not found.
+        * @throws InvalidUsername The username was not valid.
+        */
+       public User update(User u) throws IdNotFound, InvalidUsername {
+               assertValidUsername(u.getUsername());
+               EntityManager em = getEntityManager();
+               return merge(em, find(em, User.class, 
u.getUsername()).apply(u));
+       }
+
+       /**
+        * Removes a pet from the database.
+        *
+        * @param id The pet ID.
+        * @throws IdNotFound Pet was not found.
+        */
+       public void removePet(long id) throws IdNotFound {
+               EntityManager em = getEntityManager();
+               remove(em, find(em, Pet.class, id));
+       }
+
+       /**
+        * Removes an order from the database.
+        *
+        * @param id The order ID.
+        * @throws IdNotFound Order was not found.
+        */
+       public void removeOrder(long id) throws IdNotFound {
+               EntityManager em = getEntityManager();
+               remove(em, find(em, Order.class, id));
+       }
+
+       /**
+        * Removes a user from the database.
+        *
+        * @param username The username.
+        * @throws IdNotFound User was not found.
+        */
+       public void removeUser(String username) throws IdNotFound {
+               EntityManager em = getEntityManager();
+               remove(em, find(em, User.class, username));
+       }
+
+       /**
+        * Returns all pets with the specified statuses.
+        *
+        * @param status Pet statuses.
+        * @return Pets with the specified statuses.
+        */
+       public Collection<Pet> getPetsByStatus(PetStatus[] status) {
+               return getEntityManager()
+                       .createQuery("select X from PetstorePet X where 
X.status in :status", Pet.class)
+                       .setParameter("status", status)
+                       .getResultList();
+       }
+
+       /**
+        * Returns all pets with the specified tags.
+        *
+        * @param tags Pet tags.
+        * @return Pets with the specified tags.
+        * @throws InvalidTag Tag name was invalid.
+        */
+       public Collection<Pet> getPetsByTags(String[] tags) throws InvalidTag {
+               return getEntityManager()
+                       .createQuery("select X from PetstorePet X where X.tags 
in :tags", Pet.class)
+                       .setParameter("tags", tags)
+                       .getResultList();
+       }
+
+       /**
+        * Returns a summary of pet statuses and counts.
+        *
+        * @return A summary of pet statuses and counts.
+        */
+       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;
+       }
+
+       /**
+        * Returns <jk>true</jk> if the specified username and password is 
valid.
+        *
+        * @param username The username.
+        * @param password The password.
+        * @return <jk>true</jk> if the specified username and password is 
valid.
+        */
+       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 InputStream getStream(String fileName) {
+               return getClass().getResourceAsStream(fileName);
+       }
+}
\ No newline at end of file

Reply via email to