| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <!-- Google Tag Manager --> |
| <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': |
| new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], |
| j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= |
| 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); |
| })(window,document,'script','dataLayer','GTM-MT3PBTF');</script> |
| <!-- End Google Tag Manager --> |
| |
| <style> |
| @import url("styles/juneau-code.css"); |
| @import url("styles/juneau-doc.css"); |
| @import url("../content/styles/juneau-code.css"); |
| @import url("../content/styles/juneau-doc.css"); |
| p { max-width: 800px;} |
| </style> |
| </head> |
| <body> |
| <!-- Google Tag Manager (noscript) --> |
| <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-MT3PBTF" |
| height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> |
| <!-- End Google Tag Manager (noscript) --> |
| |
| <h2 class='topic'>PetStore Application</h2> |
| <div class='topic'> |
| <p> |
| The <c>PetStore</c> application is an functional application meant to demonstrate using Juneau with other technologies |
| such as Spring Boot, Spring Data, Bootstrap, and Datatables to create fully-functional applications with end-to-end |
| REST integration support. |
| </p> |
| <p> |
| What makes Juneau unique is the ability to create Java interfaces that behave just like RPC, but using REST |
| as the underlying protocol. And the technology it not tied to any platform so it can be used in any environment |
| by simply pulling in Maven dependencies. The server-side need only provide the ability to host a servlet. |
| </p> |
| <p> |
| Visit the <a class='doclink' href='https://github.com/apache/juneau-petstore'>GitHub project</a> hosting the application. |
| </p> |
| <p> |
| The project is broken down into the following subprojects: |
| </p> |
| <ul class='spaced-list'> |
| <li><c>juneau-petstore-api</c> - Contains the Java interface and DTOs for the petstore application. |
| <li><c>juneau-petstore-server</c> - Contains the server-side Java implementation of the petstore Java interface as a REST resource. |
| <li><c>juneau-petstore-client</c> - Contains the client-side Java proxy of the petstore Java interface. |
| </ul> |
| |
| <h3 class='topic'>Running the Petstore App</h3> |
| <div class='topic'> |
| <ul class='spaced-list'> |
| <li> |
| Install <a class='doclink' href='https://docs.docker.com/install/'>docker</a> on your machine. |
| </li> |
| <li> |
| <p> |
| Clone the Petstore project on your machine. |
| </p> |
| <p class='bcode w800'> |
| git clone https://github.com/apache/juneau-petstore.git |
| </p> |
| </li> |
| <li> |
| <p> |
| Open terminal inside the project directory and run the below command to start the app. |
| </p> |
| <p class='bcode w800'> |
| docker build . -t petstore && docker run -p 5000:5000 petstore |
| </p> |
| </li> |
| </ul> |
| </div> |
| |
| <h3 class='topic'>juneau-petstore-api</h3> |
| <div class='topic'> |
| <p> |
| The <c>juneau-petstore-api</c> module contains the Java interface of our application and the DTOs that go along |
| with it. These classes are meant to be shared between the server and client side code. |
| </p> |
| |
| <p> |
| The <c>PetStore</c> class is our primary class for defining our application. It's a standard Java interface with |
| annotations used to describe how the methods map to REST calls. |
| </p> |
| <h5 class='figure'>PetStore.java</h5> |
| <p class='bcode w800'> |
| <ja>@Remote</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>(path=<js>"/pet/{petId}"</js>) |
| <jk>public</jk> Pet getPet( |
| <ja>@Path</ja>( |
| name=<js>"petId"</js>, |
| description=<js>"ID of pet to return"</js>, |
| example=<js>"123"</js> |
| ) |
| <jk>long</jk> petId |
| ) <jk>throws</jk> IdNotFound, NotAcceptable; |
| |
| <ja>@RemoteMethod</ja>(method=<jsf>POST</jsf>, path=<js>"/pet"</js>) |
| <jk>public long</jk> createPet( |
| <ja>@Body</ja>( |
| description=<js>"Pet object to add to the store"</js> |
| ) CreatePet pet |
| ) <jk>throws</jk> IdConflict, NotAcceptable, UnsupportedMediaType; |
| |
| ... |
| } |
| </p> |
| <p> |
| <ja>@Remote</ja> and <ja>@RemoteMethod</ja> are client-side annotations used to map the method calls to REST |
| and will be describe in the client code section. |
| </p> |
| <p> |
| <ja>@Path</ja> and <ja>@Body</ja> are used by both the client and server side code to map to REST artifacts on both |
| sides. |
| </p> |
| <p> |
| Both sets of annotations are provided by pulling in the Juneau dependency below: |
| |
| <h5 class='figure'>Maven Dependency</h5> |
| <p class='bcode w500'> |
| <xt><dependency></xt> |
| <xt><groupId></xt>org.apache.juneau<xt></groupId></xt> |
| <xt><artifactId></xt>juneau-marshall<xt></artifactId></xt> |
| <xt><version></xt>8.1.0<xt></version></xt> |
| <xt></dependency></xt> |
| </p> |
| <p> |
| The <c>Pet</c> class is a DTO that gets serialized over the REST connection. It is also annotated with JPA annotations |
| so that they can easily be stored in a JPA datastore on the server side. |
| </p> |
| <h5 class='figure'>Pet.java</h5> |
| <p class='bcode w800'> |
| <ja>@Bean</ja>(typeName=<js>"Pet"</js>, fluentSetters=<jk>true</jk>, properties=<js>"id,species,name,tags,price,status"</js>) |
| <ja>@Entity</ja>(name=<js>"PetstorePet"</js>) |
| <jk>public class</jk> Pet { |
| |
| <ja>@Column @Id @GeneratedValue</ja> |
| <ja>@Schema</ja>(description=<js>"Unique identifier for this pet."</js>) |
| <ja>@Html</ja>(link=<js>"servlet:/pet/{id}"</js>) |
| <jk>private long</jk> <jf>id</jf>; |
| |
| <ja>@Column</ja>(length=50) |
| <ja>@Schema</ja>(description=<js>"Pet name."</js>, minLength=3, maxLength=50) |
| <jk>private</jk> String <jf>name</jf>; |
| |
| <ja>@Column</ja> |
| <ja>@Schema</ja>(description=<js>"Price of pet."</js>, maximum=<js>"999.99"</js>) |
| <ja>@Html</ja>(render=PriceRender.<jk>class</jk>) |
| <jk>private float</jk> <jf>price</jf>; |
| |
| ... |
| } |
| </p> |
| <p> |
| The annotations here are a combination of Juneau annotations for controlling marshalling (<ja>@Bean</ja>, <ja>@Html</ja>) |
| and documentation/validation (<ja>@Schema</ja>), and JPA annoations for database persistence (<ja>@Entity</ja>, <ja>@Column</ja>). |
| </p> |
| <div class='info'> |
| Most applications may choose to have separate classes for DTOs and JPA beans since you typically are not going to want |
| to expose server-side details to client-side code. In these examples however they were combined into the same classes for brevity. |
| </div> |
| </div> |
| |
| <h3 class='topic'>juneau-petstore-client</h3> |
| <div class='topic'> |
| <p> |
| The <c>juneau-petstore-client</c> module contains a single <c>Main</c> class used to instantiate the proxy against |
| our remote REST interface using the Java interface described above. |
| </p> |
| |
| <h5 class='figure'>Main.java</h5> |
| <p class='bcode w800'> |
| <jk>public class</jk> Main { |
| |
| <jk>public static void</jk> main(String[] args) { |
| |
| <jc>// Create a RestClient with JSON serialization support.</jc> |
| <jk>try</jk> (RestClient rc = RestClient.create(SimpleJsonSerializer.<jk>class</jk> JsonParser.<jk>class</jk>).build()) { |
| |
| <jc>// Instantiate our proxy.</jc> |
| PetStore petStore = rc.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:5000"</js>); |
| |
| <jc>// Print out the pets in the store.</jc> |
| Collection<Pet> pets = petStore.getPets(); |
| |
| <jc>// Pretty-print them to STDOUT.</jc> |
| SimpleJson.<jsf>DEFAULT_READABLE</jsf>.println(pets); |
| |
| } <jk>catch</jk> (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| </p> |
| <p> |
| Notice how little code is necessary to construct a remote proxy. |
| </p> |
| </div> |
| |
| <h3 class='topic'>juneau-petstore-server</h3> |
| <div class='topic'> |
| <p> |
| The <c>juneau-petstore-server</c> module contains all of the guts of the application. It's a standard Spring Boot |
| application with Juneau integration support. |
| </p> |
| |
| <h5 class='figure'>App.java</h5> |
| <p class='bcode w800'> |
| <ja>@SpringBootApplication</ja> |
| <jk>public class</jk> App { |
| |
| <jk>public static void</jk> main(String[] args) { |
| <jk>new</jk> App().start(args); |
| } |
| |
| <jk>protected void</jk> start(String[] args) { |
| <jk>new</jk> SpringApplicationBuilder(App.<jk>class</jk>) |
| .initializers(<jk>new</jk> JuneauRestInitializer(App.<jk>class</jk>)) |
| .run(args); |
| } |
| } |
| </p> |
| |
| <h5 class='figure'>AppConfiguration.java</h5> |
| <p class='bcode w800'> |
| <ja>@Configuration</ja> |
| <jk>public class</jk> AppConfiguration { |
| |
| <ja>@Bean</ja> |
| <jk>public</jk> PetStoreService petStoreService() { |
| <jk>return new</jk> PetStoreService(); |
| } |
| |
| <ja>@Bean</ja> |
| <ja>@JuneauRestRoot</ja> |
| <jk>public</jk> RootResources rootResources() { |
| <jk>return new</jk> RootResources(); |
| } |
| |
| <ja>@Bean</ja> |
| <jk>public</jk> PetStoreResource petStoreResource() { |
| <jk>return new</jk> PetStoreResource(); |
| } |
| } |
| </p> |
| <p> |
| The <c>JuneauRestInitializer</c> is used to allow Juneau resource classes to reference child Spring beans. |
| It is only required if you wish to use injection on your child resources. |
| </p> |
| |
| <p> |
| The <c>RootResources</c> class is the top-level entry point into the REST API. It allows us to group |
| child resources. In our case though we only have one child resource...<c>PetStoreResource</c>: |
| </p> |
| |
| <h5 class='figure'>RootResources.java</h5> |
| <p class='bcode w800'> |
| <ja>@Rest</ja>( |
| path=<js>"/*"</js>, |
| title=<js>"Root resources"</js>, |
| description=<js>"Example of a router resource page."</js>, |
| children={ |
| PetStoreResource.<jk>class</jk> |
| } |
| ) |
| <ja>@HtmlDocConfig</ja>( |
| widgets={ |
| ContentTypeMenuItem.<jk>class</jk>, |
| ThemeMenuItem.<jk>class</jk> |
| }, |
| navlinks={ |
| <js>"options: ?method=OPTIONS"</js>, |
| <js>"$W{ContentTypeMenuItem}"</js>, |
| <js>"$W{ThemeMenuItem}"</js>, |
| <js>"source: $C{Source/gitHub}/org/apache/juneau/petstore/rest/$R{servletClassSimple}.java"</js> |
| }, |
| aside={ |
| <js>"<div style='max-width:400px' class='text'>"</js>, |
| <js>" <p>This is an example of a 'router' page that serves as a jumping-off point to child resources.</p>"</js>, |
| <js>" <p>Resources can be nested arbitrarily deep through router pages.</p>"</js>, |
| <js>" <p>Note the <span class='link'>options</span> link provided that lets you see the generated swagger doc for this page.</p>"</js>, |
| <js>" <p>Also note the <span class='link'>sources</span> link on these pages to view the source code for the page.</p>"</js>, |
| <js>" <p>All content on pages in the UI are serialized POJOs. In this case, it's a serialized array of beans with 2 properties, 'name' and 'description'.</p>"</js>, |
| <js>" <p>Other features (such as this aside) are added through annotations.</p>"</js>, |
| <js>"</div>"</js> |
| } |
| ) |
| <ja>@SerializerConfig</ja>( |
| <jc>// For testing purposes, we want to use single quotes in all the serializers so it's easier to do simple |
| // String comparisons. |
| // You can apply any of the Serializer/Parser/BeanContext settings this way.</jc> |
| quoteChar=<js>"'"</js> |
| ) |
| <jk>public class</jk> RootResources <jk>extends</jk> BasicRestServletGroup { |
| <jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L; |
| } |
| </p> |
| <p> |
| This page renders as follows: |
| </p> |
| |
| <p class='bcode w800'> |
| http://localhost:5000 |
| </p> |
| <img class='bordered w800' src='petstore/RootResources.png'> |
| |
| <p> |
| The <c>PetStoreResource</c> class is the REST implementation of our <c>PetStore</c> interface. |
| </p> |
| |
| <h5 class='figure'>PetStoreResource.java</h5> |
| <p class='bcode w800'> |
| <ja>@Rest</ja>( |
| path=<js>"/petstore"</js>, |
| title=<js>"Petstore application"</js>, |
| description={ |
| <js>"This is a sample server Petstore server based on the Petstore sample at Swagger.io."</js>, |
| <js>"You can find out more about Swagger at http://swagger.io."</js>, |
| }, |
| swagger=<ja>@ResourceSwagger</ja>( |
| version=<js>"1.0.0"</js>, |
| title=<js>"Swagger Petstore"</js>, |
| termsOfService=<js>"You are on your own."</js>, |
| contact=<ja>@Contact</ja>( |
| name=<js>"Juneau Development Team"</js>, |
| email=<js>"dev@juneau.apache.org"</js>, |
| url=<js>"http://juneau.apache.org"</js> |
| ), |
| license=<ja>@License</ja>( |
| name=<js>"Apache 2.0"</js>, |
| url=<js>"http://www.apache.org/licenses/LICENSE-2.0.html"</js> |
| ), |
| externalDocs=<ja>@ExternalDocs</ja>( |
| description=<js>"Find out more about Juneau"</js>, |
| url=<js>"http://juneau.apache.org"</js> |
| ), |
| tags={ |
| <ja>@Tag</ja>( |
| name=<js>"pet"</js>, |
| description=<js>"Everything about your Pets"</js>, |
| externalDocs=<ja>@ExternalDocs</ja>( |
| description=<js>"Find out more"</js>, |
| url=<js>"http://juneau.apache.org"</js> |
| ) |
| ), |
| <ja>@Tag</ja>( |
| name=<js>"store"</js>, |
| description=<js>"Access to Petstore orders"</js> |
| ), |
| <ja>@Tag</ja>( |
| name=<js>"user"</js>, |
| description=<js>"Operations about user"</js>, |
| externalDocs=<ja>@ExternalDocs</ja>( |
| description=<js>"Find out more about our store"</js>, |
| url=<js>"http://juneau.apache.org"</js> |
| ) |
| ) |
| } |
| ), |
| staticFiles={<js>"htdocs:/htdocs"</js>} |
| ) |
| <ja>@HtmlDocConfig</ja>( |
| widgets={ |
| ContentTypeMenuItem.<jk>class</jk>, |
| ThemeMenuItem.<jk>class</jk>, |
| }, |
| navlinks={ |
| <js>"up: request:/.."</js>, |
| <js>"options: servlet:/?method=OPTIONS"</js>, |
| <js>"$W{ContentTypeMenuItem}"</js>, |
| <js>"$W{ThemeMenuItem}"</js>, |
| <js>"source: $C{Source/gitHub}/org/apache/juneau/petstore/rest/$R{servletClassSimple}.java"</js> |
| }, |
| head={ |
| <js>"<link rel='icon' href='$U{servlet:/htdocs/cat.png}'/>"</js> <jc>// Add a cat icon to the page.</jc> |
| }, |
| header={ |
| <js>"<h1>$R{resourceTitle}</h1>"</js>, |
| <js>"<h2>$R{methodSummary}</h2>"</js>, |
| <js>"$C{PetStore/headerImage}"</js> |
| }, |
| aside={ |
| <js>"<div style='max-width:400px' class='text'>"</js>, |
| <js>" <p>This page shows a standard nested REST resource.</p>"</js>, |
| <js>" <p>It shows how different properties can be rendered on the same bean in different views.</p>"</js>, |
| <js>" <p>It also shows examples of HtmlRender classes and @BeanProperty(format) annotations.</p>"</js>, |
| <js>" <p>It also shows how the Queryable converter and query widget can be used to create searchable interfaces.</p>"</js>, |
| <js>"</div>"</js> |
| }, |
| stylesheet=<js>"servlet:/htdocs/themes/dark.css"</js> <jc>// Use dark theme by default.</jc> |
| ) |
| <jk>public class</jk> PetStoreResource <jk>extends</jk> BasicRest <jk>implements</jk> PetStore { |
| |
| <ja>@Inject</ja> |
| <jk>private</jk> PetStoreService <jf>store</jf>; |
| |
| <jd>/** |
| * Navigation page |
| * |
| * @return Navigation page contents. |
| */</jd> |
| <ja>@RestMethod</ja>( |
| name=<jsf>GET</jsf>, |
| path=<js>"/"</js>, |
| summary=<js>"Navigation page"</js> |
| ) |
| <ja>@HtmlDocConfig</ja>( |
| style={ |
| <js>"INHERIT"</js>, <jc>// Flag for inheriting resource-level CSS.</jc> |
| <js>"body { "</js>, |
| <js>"background-image: url('petstore/htdocs/background.jpg'); "</js>, |
| <js>"background-color: black; "</js>, |
| <js>"background-size: cover; "</js>, |
| <js>"background-attachment: fixed; "</js>, |
| <js>"}"</js> |
| } |
| ) |
| <jk>public</jk> ResourceDescriptions getTopPage() { |
| <jk>return new</jk> ResourceDescriptions() |
| .append(<js>"pet"</js>, <js>"All pets in the store"</js>) |
| .append(<js>"store"</js>, <js>"Orders and inventory"</js>) |
| .append(<js>"user"</js>, <js>"Petstore users"</js>) |
| ; |
| } |
| |
| ... |
| </p> |
| <p> |
| Clicking the <c>petstore</c> link on the root page takes you to our PetStore resource: |
| </p> |
| <p class='bcode w800'> |
| http://localhost:5000/petstore |
| </p> |
| <img class='bordered w800' src='petstore/PetStore.png'> |
| |
| <p> |
| The methods defined in our <c>PetStore</c> interface are implemented like so: |
| </p> |
| <h5 class='figure'>PetStoreResource.java</h5> |
| <p class='bcode w800'> |
| <ja>@Override</ja> <jc>/* PetStore */</jc> |
| <ja>@RestMethod</ja>( |
| name=<jsf>GET</jsf>, |
| path=<js>"/pet"</js>, |
| summary=<js>"All pets in the store"</js>, |
| swagger=<ja>@MethodSwagger</ja>( |
| tags=<js>"pet"</js>, |
| parameters={ |
| Queryable.<jsf>SWAGGER_PARAMS</jsf> <jc>// Documents searching.</jc> |
| } |
| ), |
| converters={Queryable.<jk>class</jk>} <jc>// Searching support.</jc> |
| ) |
| <ja>@BeanConfig</ja>( |
| bpx=<js>"Pet: tags,photo"</js> <jc>// In this view, don't serialize tags/photos properties.</jc> |
| ) |
| <jk>public</jk> Collection<Pet> getPets() <jk>throws</jk> NotAcceptable { |
| <jk>return</jk> <jf>store</jf>.getPets(); |
| } |
| |
| <ja>@Override</ja> <jc>/* PetStore */</jc> |
| <ja>@RestMethod</ja>( |
| name=<jsf>GET</jsf>, |
| path=<js>"/pet/{petId}"</js>, |
| summary=<js>"Find pet by ID"</js>, |
| description=<js>"Returns a single pet"</js>, |
| swagger=<ja>@MethodSwagger</ja>( |
| tags=<js>"pet"</js> |
| ) |
| ) |
| <jk>public</jk> Pet getPet(long petId) <jk>throws</jk> IdNotFound, NotAcceptable { |
| <jk>return</jk> <jf>store</jf>.getPet(petId); |
| } |
| |
| <ja>@Override</ja> <jc>/* PetStore */</jc> |
| <ja>@RestMethod</ja>( |
| name=<jsf>POST</jsf>, |
| path=<js>"/pet"</js>, |
| summary=<js>"Add a new pet to the store"</js>, |
| swagger=<ja>@MethodSwagger</ja>( |
| tags=<js>"pet"</js> |
| ), |
| roleGuard=<js>"ROLE_ADMIN || (ROLE_USER && ROLE_WRITABLE)"</js> <jc>// Restrict access to this method.</jc> |
| ) |
| <jk>public long</jk> createPet(CreatePet pet) <jk>throws</jk> IdConflict, NotAcceptable, UnsupportedMediaType { |
| <jk>return</jk> <jf>store</jf>.create(pet).getId(); |
| } |
| |
| ... |
| </p> |
| <p> |
| After running the <c>Main</c> method in the client code to populate the database, the page renders as follows: |
| </p> |
| <p class='bcode w800'> |
| http://localhost:5000/petstore/pets |
| </p> |
| <img class='bordered w800' src='petstore/PetStore_pets.png'> |
| |
| <p> |
| The <l>OPTIONS</l> menu items takes you to the auto-generated Swagger UI for the application: |
| </p> |
| <p class='bcode w900'> |
| http://localhost:10000/petstore/pet?method=OPTIONS |
| </p> |
| <img class='bordered w900' src='petstore/9a.png'> |
| <p> |
| Since we've defined tags on our annotations, the pet-related operations are all grouped under the <l>pet</l> tag: |
| </p> |
| <img class='bordered w900' src='petstore/9b.png'> |
| <p> |
| Information for all HTTP parts is automatically generated: |
| </p> |
| <img class='bordered w900' src='petstore/9h.png'> |
| <p> |
| The schema models for POJO models is available in the <l>Responses</l> section of an operation: |
| </p> |
| <img class='bordered w900' src='petstore/9c.png'> |
| <p> |
| Auto-generated examples are available for all supported languages: |
| </p> |
| <img class='bordered w900' src='petstore/9d.png'> |
| <p> |
| For example, <l>application/json+simple</l>: |
| </p> |
| <img class='bordered w900' src='petstore/9e.png'> |
| <p> |
| Examples can be derived in a number of ways. In our case, we've defined a static method on our <l>Pet</l> |
| class annotated with <ja>@Example</ja>: |
| </p> |
| <h5 class='figure'>Pet.java</h5> |
| <p class='bcode w800'> |
| <ja>@Example</ja> |
| <jk>public static</jk> Pet example() { |
| <jk>return new</jk> Pet() |
| .id(123) |
| .species(Species.<jsf>DOG</jsf>) |
| .name(<js>"Doggie"</js>) |
| .tags(<js>"friendly"</js>,<js>"smart"</js>) |
| .status(PetStatus.<jsf>AVAILABLE</jsf>); |
| } |
| </p> |
| <p> |
| Similar functionality exists for request bodies as well: |
| </p> |
| <img class='bordered w900' src='petstore/9f.png'> |
| <p> |
| At the bottom of the page is a listing of the POJO models in the app: |
| </p> |
| <img class='bordered w900' src='petstore/9g.png'> |
| |
| </div> |
| </div> |
| </body> |