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 5e5494b  PetStore enhancements.
5e5494b is described below

commit 5e5494bc66757b602724c9a4530a716d37754739
Author: JamesBognar <jamesbog...@apache.org>
AuthorDate: Fri Oct 19 16:44:04 2018 -0400

    PetStore enhancements.
---
 juneau-doc/docs.txt                                |   1 +
 juneau-doc/src/main/javadoc/overview.html          | 105 +++++++++
 .../src/main/resources/ReleaseNotes/7.2.1.html     |   4 +
 .../01.RestProxies/10.DualPurposeInterfaces.html   | 188 +++++++--------
 .../rest/petstore/AbstractPersistenceService.java  | 256 +++++++++++++++++++++
 .../examples/rest/petstore/AddPetMenuItem.java     |   2 +-
 .../examples/rest/petstore/PetStoreService.java    |  95 ++++----
 .../examples/rest/petstore/dto/CreateOrder.java    |   1 -
 8 files changed, 496 insertions(+), 156 deletions(-)

diff --git a/juneau-doc/docs.txt b/juneau-doc/docs.txt
index db309de..fc3b2d0 100644
--- a/juneau-doc/docs.txt
+++ b/juneau-doc/docs.txt
@@ -228,6 +228,7 @@ juneau-rest-client.PipingOutput = 
#juneau-rest-client.PipingOutput, Overview > j
 juneau-rest-client.ResponsePatterns = #juneau-rest-client.ResponsePatterns, 
Overview > juneau-rest-client > Using Response Patterns
 juneau-rest-client.RestProxies = #juneau-rest-client.RestProxies, Overview > 
juneau-rest-client > REST Proxies
 juneau-rest-client.RestProxies.Body = #juneau-rest-client.RestProxies.Body, 
Overview > juneau-rest-client > REST Proxies > @Body
+juneau-rest-client.RestProxies.DualPurposeInterfaces = 
#juneau-rest-client.RestProxies.DualPurposeInterfaces, Overview > 
juneau-rest-client > REST Proxies > Dual-purpose (end-to-end) interfaces
 juneau-rest-client.RestProxies.FormData = 
#juneau-rest-client.RestProxies.FormData, Overview > juneau-rest-client > REST 
Proxies > @FormData
 juneau-rest-client.RestProxies.Header = 
#juneau-rest-client.RestProxies.Header, Overview > juneau-rest-client > REST 
Proxies > @Header
 juneau-rest-client.RestProxies.Path = #juneau-rest-client.RestProxies.Path, 
Overview > juneau-rest-client > REST Proxies > @Path
diff --git a/juneau-doc/src/main/javadoc/overview.html 
b/juneau-doc/src/main/javadoc/overview.html
index ded7e4e..976bc6b 100644
--- a/juneau-doc/src/main/javadoc/overview.html
+++ b/juneau-doc/src/main/javadoc/overview.html
@@ -384,6 +384,7 @@
                        <li><p class=''><a class='doclink' 
href='#juneau-rest-client.RestProxies.Path'>@Path</a></p>
                        <li><p class=''><a class='doclink' 
href='#juneau-rest-client.RestProxies.Request'>@Request</a></p>
                        <li><p class=''><a class='doclink' 
href='#juneau-rest-client.RestProxies.Response'>@Response</a></p>
+                       <li><p class='new'><a class='doclink' 
href='#juneau-rest-client.RestProxies.DualPurposeInterfaces'>Dual-purpose 
(end-to-end) interfaces</a></p>
                </ol>
                <li><p class=''><a class='doclink' 
href='#juneau-rest-client.SSL'>SSL Support</a></p>
                <li><p class=''><a class='doclink' 
href='#juneau-rest-client.Authentication'>Authentication</a></p>
@@ -22452,6 +22453,106 @@
        The behavior and functionality of all of the annotations are the same 
as if they were used on method arguments directly. This means full support for 
OpenAPI serialization and validation.
 </p>
 </div><!-- END: 9.1.9 - juneau-rest-client.RestProxies.Response -->
+
+<!-- 
====================================================================================================
 -->
+
+<h4 class='topic new' onclick='toggle(this)'><a 
href='#juneau-rest-client.RestProxies.DualPurposeInterfaces' 
id='juneau-rest-client.RestProxies.DualPurposeInterfaces'>9.1.10 - Dual-purpose 
(end-to-end) interfaces</a></h4>
+<div class='topic'><!-- START: 9.1.10 - 
juneau-rest-client.RestProxies.DualPurposeInterfaces -->
+<p>
+       A common coding practice is to use the same Java interface to define 
both your server and client side REST interfaces.
+       The advantage to this approach is that changes that you make to your 
REST interface can be reflected in both places
+       at the same time, reducing the chances for compatibility mistakes.
+</p>
+<p>
+       What makes this possible is that method-level annotations such as 
<ja>@RestMethod</ja> and parameter-level annotations
+       such as <ja>@Query</ja> are inherited from parent classes.  
+       This normally isn't possible, but the framework will spider up the 
parent hierarchy of classes to find method and parameter level
+       annotations defined on overridden methods.
+</p>
+<p>
+       The general approach is to define your {@link 
org.apache.juneau.rest.client.remote.RemoteResource @RemoteResource}-annotated 
interface first.
+       The following example is pulled from the PetStore app:
+</p>
+<p class='bpcode w800'>
+       <ja>@RemoteResource</ja>(path=<js>"/petstore"</js>)
+       <jk>public interface</jk> PetStore {
+       
+               <ja>@RemoteMethod</ja>(method=<jsf>GET</jsf>, 
path=<js>"/pet"</js>)
+               <jk>public</jk> Collection&lt;Pet&gt; getPets() <jk>throws</jk> 
NotAcceptable;
+       
+               <ja>@RemoteMethod</ja>(method=<jsf>DELETE</jsf>, 
path=<js>"/pet/{petId}"</js>)
+               <jk>public</jk> Ok deletePet(
+                       <ja>@Header</ja>(
+                               name=<js>"api_key"</js>,
+                               description=<js>"Security API key"</js>,
+                               required=<jk>true</jk>,
+                               example=<js>"foobar"</js>
+                       )
+                       String apiKey,
+                       <ja>@Path</ja>(
+                               name=<js>"petId"</js>,
+                               description=<js>"Pet id to delete"</js>,
+                               example=<js>"123"</js>
+                       )
+                       <jk>long</jk> petId
+               ) <jk>throws</jk> IdNotFound, NotAcceptable;
+               
+               ...
+</p>
+<p>
+       Next you define the implementation of your interface as a normal Juneau 
REST resource:
+</p>
+<p class='bpcode w800'>
+       <ja>@RestResource</ja>(
+               path=<js>"/petstore"</js>,
+               title=<js>"Petstore application"</js>,
+               ...
+       )
+       <jk>public class</jk> PetStoreResource <jk>extends</jk> 
BasicRestServletJena <jk>implements</jk> PetStore {
+       
+               ...
+       
+               <ja>@Override</ja> <jc>/* PetStore */</jc>
+               <ja>@RestMethod</ja>(
+                       name=<jsm>GET</jsm>,
+                       path=<js>"/pet"</js>,
+                       summary=<js>"All pets in the store"</js>,
+                       ...
+               )
+               <jk>public</jk> Collection&lt;Pet&gt; getPets() <jk>throws</jk> 
NotAcceptable {
+                       <jk>return</jk> <jsf>store</jsf>.getPets();
+               }
+       
+               <ja>@Override</ja> <jc>/* PetStore */</jc>
+               <ja>@RestMethod</ja>(
+                       name=<jsf>DELETE</jsf>,
+                       path=<js>"/pet/{petId}"</js>,
+                       summary=<js>"Deletes a pet"</js>,
+                       ...
+               )
+               <jk>public</jk> Ok deletePet(String apiKey, long petId) 
<jk>throws</jk> IdNotFound, NotAcceptable {
+                       <jsf>store</jsf>.removePet(petId);
+                       <jk>return</jk> <jsf>OK</jsf>;
+               }
+</p>
+<p>
+       Then use the interface as a remote resource like so:
+</p>
+<p class='bpcode w800'>
+       <jk>try</jk> (RestClient rc = 
RestClient.<jsm>create</jsm>().json().rootUrl(<js>"http://localhost:10000";</js>).build())
 {
+               PetStore ps = rc.getRemoteResource(PetStore.<jk>class</jk>);
+
+               <jk>for</jk> (Pet x : ps.getPets()) {
+                       ps.deletePet(<js>"my-special-key"</js>, x.getId());
+                       System.<jsf>err</jsf>.println(<js>"Deleted pet:  
id="</js> + x.getId());
+               }
+       }
+</p>   
+<p>
+       In the example above, we chose to add the <ja>@RestMethod</ja> 
annotation to the implementation class.  
+       However, they could have been added to the interface instead.
+</p>
+</div><!-- END: 9.1.10 - juneau-rest-client.RestProxies.DualPurposeInterfaces 
-->
 </div><!-- END: 9.1 - juneau-rest-client.RestProxies -->
 
 <!-- 
====================================================================================================
 -->
@@ -34309,6 +34410,10 @@
 <h5 class='topic w800'>juneau-rest-server</h5>
 <ul class='spaced-list'>
        <li>
+               Method-level annotations (e.g. <ja>@RestMethod</ja>) and 
parameter-level annotations (e.g. <ja>@Query</ja>) are now inheritable
+               from parent classes and interfaces. 
+               <br>This allows you to define {@doc 
juneau-rest-client.RestProxies.DualPurposeInterfaces}.
+       <li>
                The <code>ReaderResource</code> and <code>StreamResource</code> 
classes have been moved to the <code>org.apache.juneau.http</code>
                package in <code>juneau-marshall</code>.  This allows them to 
be used as return types in remote REST interfaces.
                <br>A new {@link 
org.apache.juneau.rest.helper.ResolvingReaderResource} class has been added 
that includes the variable-resolving support since
diff --git a/juneau-doc/src/main/resources/ReleaseNotes/7.2.1.html 
b/juneau-doc/src/main/resources/ReleaseNotes/7.2.1.html
index 6c2f248..d2916b2 100644
--- a/juneau-doc/src/main/resources/ReleaseNotes/7.2.1.html
+++ b/juneau-doc/src/main/resources/ReleaseNotes/7.2.1.html
@@ -32,6 +32,10 @@
 <h5 class='topic w800'>juneau-rest-server</h5>
 <ul class='spaced-list'>
        <li>
+               Method-level annotations (e.g. <ja>@RestMethod</ja>) and 
parameter-level annotations (e.g. <ja>@Query</ja>) are now inheritable
+               from parent classes and interfaces. 
+               <br>This allows you to define {@doc 
juneau-rest-client.RestProxies.DualPurposeInterfaces}.
+       <li>
                The <code>ReaderResource</code> and <code>StreamResource</code> 
classes have been moved to the <code>org.apache.juneau.http</code>
                package in <code>juneau-marshall</code>.  This allows them to 
be used as return types in remote REST interfaces.
                <br>A new {@link oajr.helper.ResolvingReaderResource} class has 
been added that includes the variable-resolving support since
diff --git 
a/juneau-doc/src/main/resources/Topics/09.juneau-rest-client/01.RestProxies/10.DualPurposeInterfaces.html
 
b/juneau-doc/src/main/resources/Topics/09.juneau-rest-client/01.RestProxies/10.DualPurposeInterfaces.html
index ac0c021..7ee18c0 100644
--- 
a/juneau-doc/src/main/resources/Topics/09.juneau-rest-client/01.RestProxies/10.DualPurposeInterfaces.html
+++ 
b/juneau-doc/src/main/resources/Topics/09.juneau-rest-client/01.RestProxies/10.DualPurposeInterfaces.html
@@ -16,110 +16,100 @@
 {new} Dual-purpose (end-to-end) interfaces
 
 <p>
-       
+       A common coding practice is to use the same Java interface to define 
both your server and client side REST interfaces.
+       The advantage to this approach is that changes that you make to your 
REST interface can be reflected in both places
+       at the same time, reducing the chances for compatibility mistakes.
 </p>
-
-
-@RemoteResource(path="/petstore")
-public interface PetStore {
-
-       @RemoteMethod(method=GET, path="/pet")
-       public Collection<Pet> getPets() throws NotAcceptable;
-
-       @RemoteMethod(path="/pet/{petId}") /* method inferred from method name 
*/
-       public Pet getPet(
-               @Path(
-                       name="petId",
-                       description="ID of pet to return",
-                       example="123"
-               )
-               long petId
-       ) throws IdNotFound, NotAcceptable;
-
-       
+<p>
+       What makes this possible is that method-level annotations such as 
<ja>@RestMethod</ja> and parameter-level annotations
+       such as <ja>@Query</ja> are inherited from parent classes.  
+       This normally isn't possible, but the framework will spider up the 
parent hierarchy of classes to find method and parameter level
+       annotations defined on overridden methods.
+</p>
+<p>
+       The general approach is to define your {@link 
oajr.client.remote.RemoteResource @RemoteResource}-annotated interface first.
+       The following example is pulled from the PetStore app:
+</p>
+<p class='bpcode w800'>
+       <ja>@RemoteResource</ja>(path=<js>"/petstore"</js>)
+       <jk>public interface</jk> PetStore {
        
+               <ja>@RemoteMethod</ja>(method=<jsf>GET</jsf>, 
path=<js>"/pet"</js>)
+               <jk>public</jk> Collection&lt;Pet&gt; getPets() <jk>throws</jk> 
NotAcceptable;
        
-public class PetStoreResource extends BasicRestServletJena implements PetStore 
{
-       private static final long serialVersionUID = 1L;
-
-       private PetStoreService store;
-
-
-       @Override /* PetStore */
-       @RestMethod(
-               name=GET,
-               path="/pet",
-               summary="All pets in the store",
-               swagger=@MethodSwagger(
-                       tags="pet",
-                       parameters={
-                               Queryable.SWAGGER_PARAMS
-                       }
-               ),
-               bpx="Pet: tags",
-               htmldoc=@HtmlDoc(
-                       widgets={
-                               QueryMenuItem.class,
-                               AddPetMenuItem.class
-                       },
-                       navlinks={
-                               "INHERIT",                // Inherit links from 
class.
-                               "[2]:$W{QueryMenuItem}",  // Insert QUERY link 
in position 2.
-                               "[3]:$W{AddPetMenuItem}"  // Insert ADD link in 
position 3.
-                       }
-               ),
-               converters={Queryable.class}
-       )
-       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:[] } ]"
-                       }
-               )
+               <ja>@RemoteMethod</ja>(method=<jsf>DELETE</jsf>, 
path=<js>"/pet/{petId}"</js>)
+               <jk>public</jk> Ok deletePet(
+                       <ja>@Header</ja>(
+                               name=<js>"api_key"</js>,
+                               description=<js>"Security API key"</js>,
+                               required=<jk>true</jk>,
+                               example=<js>"foobar"</js>
+                       )
+                       String apiKey,
+                       <ja>@Path</ja>(
+                               name=<js>"petId"</js>,
+                               description=<js>"Pet id to delete"</js>,
+                               example=<js>"123"</js>
+                       )
+                       <jk>long</jk> petId
+               ) <jk>throws</jk> IdNotFound, NotAcceptable;
+               
+               ...
+</p>
+<p>
+       Next you define the implementation of your interface as a normal Juneau 
REST resource:
+</p>
+<p class='bpcode w800'>
+       <ja>@RestResource</ja>(
+               path=<js>"/petstore"</js>,
+               title=<js>"Petstore application"</js>,
+               ...
        )
-       public Pet getPet(long petId) throws IdNotFound, NotAcceptable {
-               return store.getPet(petId);
-       }
+       <jk>public class</jk> PetStoreResource <jk>extends</jk> 
BasicRestServletJena <jk>implements</jk> PetStore {
        
+               ...
        
-               try (RestClient rc = 
RestClient.create().json().rootUrl("http://localhost:10000";).build()) {
-                       PetStore ps = rc.getRemoteResource(PetStore.class);
-
-                       for (Pet x : ps.getPets()) {
-                               ps.deletePet("apiKey", x.getId());
-                               w.println(format("Deleted pet:  id={0}", 
x.getId()));
-                       }
-                       for (Order x : ps.getOrders()) {
-                               ps.deleteOrder(x.getId());
-                               w.println(format("Deleted order:  id={0}", 
x.getId()));
-                       }
-                       for (User x : ps.getUsers()) {
-                               ps.deleteUser(x.getUsername());
-                               w.println(format("Deleted user:  username={0}", 
x.getUsername()));
-                       }
-                       for (CreatePet x : 
parser.parse(getStream("init/Pets.json"), CreatePet[].class)) {
-                               long id = ps.postPet(x);
-                               w.println(format("Created pet:  id={0}, 
name={1}", id, x.getName()));
-                       }
-                       for (Order x : 
parser.parse(getStream("init/Orders.json"), Order[].class)) {
-                               long id = ps.placeOrder(x.getPetId(), 
x.getUsername());
-                               w.println(format("Created order:  id={0}", id));
-                       }
-                       for (User x: parser.parse(getStream("init/Users.json"), 
User[].class)) {
-                               ps.postUser(x);
-                               w.println(format("Created user:  username={0}", 
x.getUsername()));
-                       }
+               <ja>@Override</ja> <jc>/* PetStore */</jc>
+               <ja>@RestMethod</ja>(
+                       name=<jsm>GET</jsm>,
+                       path=<js>"/pet"</js>,
+                       summary=<js>"All pets in the store"</js>,
+                       ...
+               )
+               <jk>public</jk> Collection&lt;Pet&gt; getPets() <jk>throws</jk> 
NotAcceptable {
+                       <jk>return</jk> <jsf>store</jsf>.getPets();
                }
        
-       
-       
\ No newline at end of file
+               <ja>@Override</ja> <jc>/* PetStore */</jc>
+               <ja>@RestMethod</ja>(
+                       name=<jsf>DELETE</jsf>,
+                       path=<js>"/pet/{petId}"</js>,
+                       summary=<js>"Deletes a pet"</js>,
+                       ...
+               )
+               <jk>public</jk> Ok deletePet(String apiKey, long petId) 
<jk>throws</jk> IdNotFound, NotAcceptable {
+                       <jsf>store</jsf>.removePet(petId);
+                       <jk>return</jk> <jsf>OK</jsf>;
+               }
+</p>
+<p>
+       Then use the interface as a remote resource like so:
+</p>
+<p class='bpcode w800'>
+       <jk>try</jk> (RestClient rc = 
RestClient.<jsm>create</jsm>().json().rootUrl(<js>"http://localhost:10000";</js>).build())
 {
+               PetStore ps = rc.getRemoteResource(PetStore.<jk>class</jk>);
+
+               <jk>for</jk> (Pet x : ps.getPets()) {
+                       ps.deletePet(<js>"my-special-key"</js>, x.getId());
+                       System.<jsf>err</jsf>.println(<js>"Deleted pet:  
id="</js> + x.getId());
+               }
+       }
+</p>   
+<p>
+       In the example above, we chose to add the <ja>@RestMethod</ja> 
annotation to the implementation class.  
+       However, they could have been added to the interface instead.
+</p>
+<p>
+       Note how we didn't need to use the <ja>@Header</ja> and <ja>@Path</ja> 
annotations in our implementation since
+       the annotations were inherited from the interface.
+</p>
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AbstractPersistenceService.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AbstractPersistenceService.java
new file mode 100644
index 0000000..92b178c
--- /dev/null
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AbstractPersistenceService.java
@@ -0,0 +1,256 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau.examples.rest.petstore;
+
+import java.util.*;
+
+import javax.persistence.*;
+
+import org.apache.juneau.utils.*;
+
+/**
+ * Superclass for DAOs that use the JPA entity manager.
+ */
+public class AbstractPersistenceService {
+
+       private final EntityManagerFactory entityManagerFactory;
+
+       public AbstractPersistenceService() {
+               entityManagerFactory = 
Persistence.createEntityManagerFactory("test");
+       }
+
+       /**
+        * Retrieves an 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 em The entity manager to use to retrieve the beans.
+        * @param query The JPA query.
+        * @param t The bean type.
+        */
+       protected <T> List<T> query(EntityManager em, String query, Class<T> t, 
SearchArgs searchArgs) {
+               TypedQuery<T> q = em.createQuery(query, t);
+               if (searchArgs != null) {
+                       q.setMaxResults(searchArgs.getLimit() == 0 ? 100 : 
searchArgs.getLimit());
+                       q.setFirstResult(searchArgs.getPosition());
+               }
+               return em.createQuery(query, t).getResultList();
+       }
+
+       /**
+        * Same as {@link #query(EntityManager,String,Class,SearchArgs)} but 
uses a new entity manager.
+        *
+        * @param query The JPA query.
+        * @param t The bean type.
+        */
+       protected <T> List<T> query(String query, Class<T> t, SearchArgs 
searchArgs) {
+               return query(getEntityManager(), query, t, searchArgs);
+       }
+
+       /**
+        * 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.
+        */
+       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.
+        */
+       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-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AddPetMenuItem.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AddPetMenuItem.java
index 10aba70..3e5eb88 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AddPetMenuItem.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/AddPetMenuItem.java
@@ -42,7 +42,7 @@ public class AddPetMenuItem extends MenuItemWidget {
                                                th("Species:"),
                                                td(
                                                        
select().name("species").children(
-                                                               option("cat"), 
option("dog"), option("bird"), option("fish"), option("mouse"), 
option("rabbit"), option("snake")
+                                                               option("CAT"), 
option("DOG"), option("BIRD"), option("FISH"), option("MOUSE"), 
option("RABBIT"), option("SNAKE")
                                                        )
                                                ),
                                                td(new Tooltip("&#x2753;", "The 
kind of animal."))
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java
index 817271d..063e1bd 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/PetStoreService.java
@@ -22,18 +22,27 @@ import javax.persistence.*;
 import org.apache.juneau.examples.rest.petstore.dto.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.rest.client.*;
+import org.apache.juneau.utils.*;
 
 /**
  * Pet store database application.
+ * <p>
+ * Uses JPA persistence to store and retrieve PetStore DTOs.
+ * JPA beans are defined in <code>META-INF/persistence.xml</code>.
  */
-public class PetStoreService {
+public class PetStoreService extends AbstractPersistenceService {
 
-       private final EntityManagerFactory entityManagerFactory;
-
-       public PetStoreService() {
-               entityManagerFactory = 
Persistence.createEntityManagerFactory("test");
-       }
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Initialization methods.
+       
//-----------------------------------------------------------------------------------------------------------------
 
+       /**
+        * Initialize the petstore database using JPA.
+        *
+        * @param w Console output.
+        * @return This object (for method chaining).
+        * @throws Exception
+        */
        public PetStoreService initDirect(PrintWriter w) throws Exception {
 
                EntityManager em = getEntityManager();
@@ -76,6 +85,13 @@ public class PetStoreService {
                return this;
        }
 
+       /**
+        * Initialize the petstore database by using a remote resource 
interface against our REST.
+        *
+        * @param w Console output.
+        * @return This object (for method chaining).
+        * @throws Exception
+        */
        public PetStoreService initViaRest(PrintWriter w) throws Exception {
                JsonParser parser = 
JsonParser.create().ignoreUnknownBeanProperties().build();
 
@@ -111,6 +127,9 @@ public class PetStoreService {
                return this;
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Service methods.
+       
//-----------------------------------------------------------------------------------------------------------------
 
        public Pet getPet(long id) throws IdNotFound {
                return find(Pet.class, id);
@@ -126,15 +145,15 @@ public class PetStoreService {
        }
 
        public List<Pet> getPets() {
-               return query("select X from Pet X", Pet.class);
+               return query("select X from Pet X", Pet.class, 
(SearchArgs)null);
        }
 
        public List<Order> getOrders() {
-               return query("select X from PetstoreOrder X", Order.class);
+               return query("select X from PetstoreOrder X", Order.class, 
(SearchArgs)null);
        }
 
        public List<User> getUsers() {
-               return query("select X from PetstoreUser X", User.class);
+               return query("select X from PetstoreUser X", User.class, 
(SearchArgs)null);
        }
 
        public Pet create(CreatePet c) {
@@ -150,28 +169,34 @@ public class PetStoreService {
        }
 
        public Pet update(UpdatePet u) throws IdNotFound {
-               return merge(getPet(u.getId()).apply(u));
+               EntityManager em = getEntityManager();
+               return merge(em, find(em, Pet.class, u.getId()).apply(u));
        }
 
        public Order update(Order o) throws IdNotFound {
-               return merge(getOrder(o.getId()).apply(o));
+               EntityManager em = getEntityManager();
+               return merge(em, find(em, Order.class, o.getId()).apply(o));
        }
 
        public User update(User u) throws IdNotFound, InvalidUsername {
                assertValidUsername(u.getUsername());
-               return merge(getUser(u.getUsername()).apply(u));
+               EntityManager em = getEntityManager();
+               return merge(em, find(em, User.class, 
u.getUsername()).apply(u));
        }
 
        public void removePet(long id) throws IdNotFound {
-               remove(getPet(id));
+               EntityManager em = getEntityManager();
+               remove(em, find(em, Pet.class, id));
        }
 
        public void removeOrder(long id) throws IdNotFound {
-               remove(getOrder(id));
+               EntityManager em = getEntityManager();
+               remove(em, find(em, Order.class, id));
        }
 
        public void removeUser(String username) throws IdNotFound {
-               remove(getUser(username));
+               EntityManager em = getEntityManager();
+               remove(em, find(em, User.class, username));
        }
 
        public Collection<Pet> getPetsByStatus(PetStatus[] status) {
@@ -213,46 +238,6 @@ public class PetStoreService {
                        throw new InvalidUsername();
        }
 
-       private EntityManager getEntityManager() {
-               return entityManagerFactory.createEntityManager();
-       }
-
-       private <T> T merge(T t) {
-               EntityManager em = getEntityManager();
-               try {
-                       EntityTransaction et = em.getTransaction();
-                       et.begin();
-                       t = em.merge(t);
-                       et.commit();
-                       return t;
-               } finally {
-                       em.close();
-               }
-       }
-
-       private <T> void remove(T t) {
-               EntityManager em = getEntityManager();
-               try {
-                       EntityTransaction et = em.getTransaction();
-                       et.begin();
-                       em.remove(t);
-                       et.commit();
-               } finally {
-                       em.close();
-               }
-       }
-
-       private <T> List<T> query(String query, Class<T> t) {
-               return getEntityManager().createQuery(query, t).getResultList();
-       }
-
-       private <T> T find(Class<T> t, Object id) throws IdNotFound {
-               T o = getEntityManager().find(t, id);
-               if (o == null)
-                       throw new IdNotFound(id, t);
-               return o;
-       }
-
        private InputStream getStream(String fileName) {
                return getClass().getResourceAsStream(fileName);
        }
diff --git 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
index 2b42888..d26a270 100644
--- 
a/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
+++ 
b/juneau-examples/juneau-examples-rest/src/main/java/org/apache/juneau/examples/rest/petstore/dto/CreateOrder.java
@@ -16,7 +16,6 @@ import org.apache.juneau.annotation.*;
 
 /**
  * Bean for creating {@link Order} objects.
- * Also serves as the superclass for the {@link Order} object.
  */
 @Bean(fluentSetters=true, properties="petId,username")
 public class CreateOrder {

Reply via email to