You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/10/19 20:44:16 UTC
[juneau] branch master updated: PetStore enhancements.
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 <ja...@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 {