001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.examples.rest.petstore;
014
015import static java.text.MessageFormat.*;
016
017import java.io.*;
018import java.util.*;
019
020import javax.persistence.*;
021
022import org.apache.juneau.examples.rest.petstore.dto.*;
023import org.apache.juneau.examples.rest.petstore.rest.*;
024import org.apache.juneau.json.*;
025import org.apache.juneau.parser.ParseException;
026import org.apache.juneau.rest.client.*;
027import org.apache.juneau.utils.*;
028
029/**
030 * Pet store database application.
031 * <p>
032 * Uses JPA persistence to store and retrieve PetStore DTOs.
033 * JPA beans are defined in <c>META-INF/persistence.xml</c>.
034 *
035 * <ul class='seealso'>
036 *    <li class='extlink'>{@source}
037 * </ul>
038 */
039public class PetStoreService extends AbstractPersistenceService {
040
041   //-----------------------------------------------------------------------------------------------------------------
042   // Initialization methods.
043   //-----------------------------------------------------------------------------------------------------------------
044
045   /**
046    * Initialize the petstore database using JPA.
047    *
048    * @param w Console output.
049    * @return This object (for method chaining).
050    * @throws ParseException Malformed input encountered.
051    * @throws IOException File could not be read from file system.
052    */
053   public PetStoreService initDirect(PrintWriter w) throws ParseException, IOException {
054
055      EntityManager em = getEntityManager();
056      EntityTransaction et = em.getTransaction();
057      JsonParser parser = JsonParser.create().build();
058
059      et.begin();
060
061      for (Pet x : em.createQuery("select X from PetstorePet X", Pet.class).getResultList()) {
062         em.remove(x);
063         w.println(format("Deleted pet:  id={0}", x.getId()));
064      }
065      for (Order x : em.createQuery("select X from PetstoreOrder X", Order.class).getResultList()) {
066         em.remove(x);
067         w.println(format("Deleted order:  id={0}", x.getId()));
068      }
069      for (User x : em.createQuery("select X from PetstoreUser X", User.class).getResultList()) {
070         em.remove(x);
071         w.println(format("Deleted user:  username={0}", x.getUsername()));
072      }
073
074      et.commit();
075      et.begin();
076
077      for (Pet x : parser.parse(getStream("init/Pets.json"), Pet[].class)) {
078         x = em.merge(x);
079         w.println(format("Created pet:  id={0}, name={1}", x.getId(), x.getName()));
080      }
081      for (Order x : parser.parse(getStream("init/Orders.json"), Order[].class)) {
082         x = em.merge(x);
083         w.println(format("Created order:  id={0}", x.getId()));
084      }
085      for (User x: parser.parse(getStream("init/Users.json"), User[].class)) {
086         x = em.merge(x);
087         w.println(format("Created user:  username={0}", x.getUsername()));
088      }
089
090      et.commit();
091
092      return this;
093   }
094
095   /**
096    * Initialize the petstore database by using a remote resource interface against our REST.
097    *
098    * @param w Console output.
099    * @return This object (for method chaining).
100    * @throws ParseException Malformed input encountered.
101    * @throws IOException Thrown by client stream.
102    */
103   public PetStoreService initViaRest(PrintWriter w) throws ParseException, IOException {
104      JsonParser parser = JsonParser.create().ignoreUnknownBeanProperties().build();
105
106      String port = System.getProperty("juneau.serverPort", "8000");
107
108      try (RestClient rc = RestClient.create().json().rootUrl("http://localhost:" + port).build()) {
109         PetStore ps = rc.getRemoteResource(PetStore.class);
110
111         for (Pet x : ps.getPets()) {
112            ps.deletePet("apiKey", x.getId());
113            w.println(format("Deleted pet:  id={0}", x.getId()));
114         }
115         for (Order x : ps.getOrders()) {
116            ps.deleteOrder(x.getId());
117            w.println(format("Deleted order:  id={0}", x.getId()));
118         }
119         for (User x : ps.getUsers()) {
120            ps.deleteUser(x.getUsername());
121            w.println(format("Deleted user:  username={0}", x.getUsername()));
122         }
123         for (CreatePet x : parser.parse(getStream("init/Pets.json"), CreatePet[].class)) {
124            long id = ps.postPet(x);
125            w.println(format("Created pet:  id={0}, name={1}", id, x.getName()));
126         }
127         for (Order x : parser.parse(getStream("init/Orders.json"), Order[].class)) {
128            long id = ps.placeOrder(x.getPetId(), x.getUsername());
129            w.println(format("Created order:  id={0}", id));
130         }
131         for (User x: parser.parse(getStream("init/Users.json"), User[].class)) {
132            ps.postUser(x);
133            w.println(format("Created user:  username={0}", x.getUsername()));
134         }
135      }
136
137      return this;
138   }
139
140   //-----------------------------------------------------------------------------------------------------------------
141   // Service methods.
142   //-----------------------------------------------------------------------------------------------------------------
143
144   /**
145    * Returns the pet with the specified ID.
146    *
147    * @param id The pet ID.
148    * @return The pet with the specified ID.  Never <jk>null</jk>.
149    * @throws IdNotFound If pet was not found.
150    */
151   public Pet getPet(long id) throws IdNotFound {
152      return find(Pet.class, id);
153   }
154
155   /**
156    * Returns the order with the specified ID.
157    *
158    * @param id The order ID.
159    * @return The order with the specified ID.  Never <jk>null</jk>.
160    * @throws IdNotFound If order was not found.
161    */
162   public Order getOrder(long id) throws IdNotFound {
163      return find(Order.class, id);
164   }
165
166   /**
167    * Returns the user with the specified username.
168    *
169    * @param username The username.
170    * @return The user with the specified username.  Never <jk>null</jk>.
171    * @throws InvalidUsername Username was not valid.
172    * @throws IdNotFound If order was not found.
173    */
174   public User getUser(String username) throws InvalidUsername, IdNotFound  {
175      assertValidUsername(username);
176      return find(User.class, username);
177   }
178
179   /**
180    * Returns all pets in the database.
181    *
182    * @return All pets in the database.
183    */
184   public List<Pet> getPets() {
185      return query("select X from PetstorePet X", Pet.class, (SearchArgs)null);
186   }
187
188   /**
189    * Returns all orders in the database.
190    *
191    * @return All orders in the database.
192    */
193   public List<Order> getOrders() {
194      return query("select X from PetstoreOrder X", Order.class, (SearchArgs)null);
195   }
196
197   /**
198    * Returns all users in the database.
199    *
200    * @return All users in the database.
201    */
202   public List<User> getUsers() {
203      return query("select X from PetstoreUser X", User.class, (SearchArgs)null);
204   }
205
206   /**
207    * Creates a new pet in the database.
208    *
209    * @param c The pet input data.
210    * @return a new {@link Pet} object.
211    */
212   public Pet create(CreatePet c) {
213      return merge(new Pet().status(PetStatus.AVAILABLE).apply(c));
214   }
215
216   /**
217    * Creates a new order in the database.
218    *
219    * @param c The order input data.
220    * @return a new {@link Order} object.
221    */
222   public Order create(CreateOrder c) {
223      return merge(new Order().status(OrderStatus.PLACED).apply(c));
224   }
225
226   /**
227    * Creates a new user in the database.
228    *
229    * @param c The user input data.
230    * @return a new {@link User} object.
231    */
232   public User create(User c) {
233      return merge(new User().apply(c));
234   }
235
236   /**
237    * Updates a pet in the database.
238    *
239    * @param u The update information.
240    * @return The updated {@link Pet} object.
241    * @throws IdNotFound Pet was not found.
242    */
243   public Pet update(UpdatePet u) throws IdNotFound {
244      EntityManager em = getEntityManager();
245      return merge(em, find(em, Pet.class, u.getId()).apply(u));
246   }
247
248   /**
249    * Updates an order in the database.
250    *
251    * @param o The update information.
252    * @return The updated {@link Order} object.
253    * @throws IdNotFound Order was not found.
254    */
255   public Order update(Order o) throws IdNotFound {
256      EntityManager em = getEntityManager();
257      return merge(em, find(em, Order.class, o.getId()).apply(o));
258   }
259
260   /**
261    * Updates a user in the database.
262    *
263    * @param u The update information.
264    * @return The updated {@link User} object.
265    * @throws IdNotFound User was not found.
266    * @throws InvalidUsername The username was not valid.
267    */
268   public User update(User u) throws IdNotFound, InvalidUsername {
269      assertValidUsername(u.getUsername());
270      EntityManager em = getEntityManager();
271      return merge(em, find(em, User.class, u.getUsername()).apply(u));
272   }
273
274   /**
275    * Removes a pet from the database.
276    *
277    * @param id The pet ID.
278    * @throws IdNotFound Pet was not found.
279    */
280   public void removePet(long id) throws IdNotFound {
281      EntityManager em = getEntityManager();
282      remove(em, find(em, Pet.class, id));
283   }
284
285   /**
286    * Removes an order from the database.
287    *
288    * @param id The order ID.
289    * @throws IdNotFound Order was not found.
290    */
291   public void removeOrder(long id) throws IdNotFound {
292      EntityManager em = getEntityManager();
293      remove(em, find(em, Order.class, id));
294   }
295
296   /**
297    * Removes a user from the database.
298    *
299    * @param username The username.
300    * @throws IdNotFound User was not found.
301    */
302   public void removeUser(String username) throws IdNotFound {
303      EntityManager em = getEntityManager();
304      remove(em, find(em, User.class, username));
305   }
306
307   /**
308    * Returns all pets with the specified statuses.
309    *
310    * @param status Pet statuses.
311    * @return Pets with the specified statuses.
312    */
313   public Collection<Pet> getPetsByStatus(PetStatus[] status) {
314      return getEntityManager()
315         .createQuery("select X from PetstorePet X where X.status in :status", Pet.class)
316         .setParameter("status", status)
317         .getResultList();
318   }
319
320   /**
321    * Returns all pets with the specified tags.
322    *
323    * @param tags Pet tags.
324    * @return Pets with the specified tags.
325    * @throws InvalidTag Tag name was invalid.
326    */
327   public Collection<Pet> getPetsByTags(String[] tags) throws InvalidTag {
328      return getEntityManager()
329         .createQuery("select X from PetstorePet X where X.tags in :tags", Pet.class)
330         .setParameter("tags", tags)
331         .getResultList();
332   }
333
334   /**
335    * Returns a summary of pet statuses and counts.
336    *
337    * @return A summary of pet statuses and counts.
338    */
339   public Map<PetStatus,Integer> getInventory() {
340      Map<PetStatus,Integer> m = new LinkedHashMap<>();
341      for (Pet p : getPets()) {
342         PetStatus ps = p.getStatus();
343         if (! m.containsKey(ps))
344            m.put(ps, 1);
345         else
346            m.put(ps, m.get(ps) + 1);
347      }
348      return m;
349   }
350
351   /**
352    * Returns <jk>true</jk> if the specified username and password is valid.
353    *
354    * @param username The username.
355    * @param password The password.
356    * @return <jk>true</jk> if the specified username and password is valid.
357    */
358   public boolean isValid(String username, String password) {
359      return getUser(username).getPassword().equals(password);
360   }
361
362   //-----------------------------------------------------------------------------------------------------------------
363   // Helper methods
364   //-----------------------------------------------------------------------------------------------------------------
365
366   private void assertValidUsername(String username) throws InvalidUsername {
367      if (username == null || ! username.matches("[\\w\\d]{3,8}"))
368         throw new InvalidUsername();
369   }
370
371   private InputStream getStream(String fileName) {
372      return getClass().getResourceAsStream(fileName);
373   }
374}