blob: f82811f18abaefb53e162ad453bac7d6afca3823 [file] [log] [blame]
<!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 &amp;&amp; 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&lt;Pet&gt; 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>&lt;dependency&gt;</xt>
<xt>&lt;groupId&gt;</xt>org.apache.juneau<xt>&lt;/groupId&gt;</xt>
<xt>&lt;artifactId&gt;</xt>juneau-marshall<xt>&lt;/artifactId&gt;</xt>
<xt>&lt;version&gt;</xt>8.1.0<xt>&lt;/version&gt;</xt>
<xt>&lt;/dependency&gt;</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&lt;Pet&gt; 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>"&lt;div style='max-width:400px' class='text'&gt;"</js>,
<js>" &lt;p&gt;This is an example of a 'router' page that serves as a jumping-off point to child resources.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;Resources can be nested arbitrarily deep through router pages.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;Note the &lt;span class='link'&gt;options&lt;/span&gt; link provided that lets you see the generated swagger doc for this page.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;Also note the &lt;span class='link'&gt;sources&lt;/span&gt; link on these pages to view the source code for the page.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;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'.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;Other features (such as this aside) are added through annotations.&lt;/p&gt;"</js>,
<js>"&lt;/div&gt;"</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>"&lt;link rel='icon' href='$U{servlet:/htdocs/cat.png}'/&gt;"</js> <jc>// Add a cat icon to the page.</jc>
},
header={
<js>"&lt;h1&gt;$R{resourceTitle}&lt;/h1&gt;"</js>,
<js>"&lt;h2&gt;$R{methodSummary}&lt;/h2&gt;"</js>,
<js>"$C{PetStore/headerImage}"</js>
},
aside={
<js>"&lt;div style='max-width:400px' class='text'&gt;"</js>,
<js>" &lt;p&gt;This page shows a standard nested REST resource.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;It shows how different properties can be rendered on the same bean in different views.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;It also shows examples of HtmlRender classes and @BeanProperty(format) annotations.&lt;/p&gt;"</js>,
<js>" &lt;p&gt;It also shows how the Queryable converter and query widget can be used to create searchable interfaces.&lt;/p&gt;"</js>,
<js>"&lt;/div&gt;"</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&lt;Pet&gt; 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 &amp;&amp; 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>