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<Pet> 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<Pet> 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<Pet> 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<Pet> 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("❓", "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 {