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 2022/06/24 12:18:44 UTC

[juneau] 01/02: Merge branch 'master' of https://gitbox.apache.org/repos/asf/juneau.git

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

commit 2c37b62920592722825cecc1803c305a4321fa84
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Fri Jun 24 08:18:02 2022 -0400

    Merge branch 'master' of https://gitbox.apache.org/repos/asf/juneau.git
---
 juneau-doc/docs/Topics/16.juneau-petstore.html     |  21 +-
 .../01.jp.RunningTheApp.html}                      |  27 +-
 .../02.jp.juneau-petstore-api.html                 | 219 +++++++++++++
 .../03.jp.juneau-petstore-client.html              |  47 +++
 .../04.jp.juneau-petstore-server.html              | 363 +++++++++++++++++++++
 .../Topics/16.juneau-petstore/doc-files/9a.png     | Bin 0 -> 221670 bytes
 .../Topics/16.juneau-petstore/doc-files/9b.png     | Bin 0 -> 146792 bytes
 .../Topics/16.juneau-petstore/doc-files/9c.png     | Bin 0 -> 226731 bytes
 .../Topics/16.juneau-petstore/doc-files/9d.png     | Bin 0 -> 234305 bytes
 .../Topics/16.juneau-petstore/doc-files/9e.png     | Bin 0 -> 110349 bytes
 .../Topics/16.juneau-petstore/doc-files/9f.png     | Bin 0 -> 185344 bytes
 .../Topics/16.juneau-petstore/doc-files/9g.png     | Bin 0 -> 140337 bytes
 .../Topics/16.juneau-petstore/doc-files/9h.png     | Bin 0 -> 116711 bytes
 .../16.juneau-petstore/doc-files/PetStore.png      | Bin 0 -> 838280 bytes
 .../16.juneau-petstore/doc-files/PetStore_pets.png | Bin 0 -> 233639 bytes
 .../16.juneau-petstore/doc-files/RootResources.png | Bin 0 -> 184725 bytes
 juneau-doc/docs/overview_template.html             |  26 +-
 17 files changed, 686 insertions(+), 17 deletions(-)

diff --git a/juneau-doc/docs/Topics/16.juneau-petstore.html b/juneau-doc/docs/Topics/16.juneau-petstore.html
index 57002a5db..4615ad4d2 100644
--- a/juneau-doc/docs/Topics/16.juneau-petstore.html
+++ b/juneau-doc/docs/Topics/16.juneau-petstore.html
@@ -13,10 +13,27 @@
  ***************************************************************************************************************************/
  -->
 
-{title:'juneau-pestore', created:'8.2.0', flags:'todo'}
+{title:'juneau-petstore', created:'8.2.0', updated:'9.0.0'}
 
 <div class='topic'>
 	<p>
-		TODO
+		The <c>PetStore</c> application is an functional application meant to demonstrate using Juneau with Spring Boot
+		to create an end-to-end client/server REST API. 
 	</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>
 </div>
\ No newline at end of file
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore.html b/juneau-doc/docs/Topics/16.juneau-petstore/01.jp.RunningTheApp.html
similarity index 56%
copy from juneau-doc/docs/Topics/16.juneau-petstore.html
copy to juneau-doc/docs/Topics/16.juneau-petstore/01.jp.RunningTheApp.html
index 57002a5db..677477704 100644
--- a/juneau-doc/docs/Topics/16.juneau-petstore.html
+++ b/juneau-doc/docs/Topics/16.juneau-petstore/01.jp.RunningTheApp.html
@@ -13,10 +13,33 @@
  ***************************************************************************************************************************/
  -->
 
-{title:'juneau-pestore', created:'8.2.0', flags:'todo'}
+{title:'Running the Pet Store App', created:'9.0.0'}
 
 <div class='topic'>
 	<p>
-		TODO
+		The Pet Store app is a Spring Boot application that can be started up by running the <c>App</c> class.
+		This starts the application on port 5000.  It can also be started in a docker container using the following
+		instructions:
 	</p>
+	<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>
\ No newline at end of file
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/02.jp.juneau-petstore-api.html b/juneau-doc/docs/Topics/16.juneau-petstore/02.jp.juneau-petstore-api.html
new file mode 100644
index 000000000..b3b4e6e5e
--- /dev/null
+++ b/juneau-doc/docs/Topics/16.juneau-petstore/02.jp.juneau-petstore-api.html
@@ -0,0 +1,219 @@
+<!--
+/***************************************************************************************************************************
+ * 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.
+ ***************************************************************************************************************************/
+ -->
+
+{title:'juneau-petstore-api', created:'9.0.0'}
+
+<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 {
+		|	
+		|		<jc>//------------------------------------------------------------------------------------------------------------------
+		|		// Pets
+		|		//------------------------------------------------------------------------------------------------------------------</jc>
+		|	
+		|		<jd>/**
+		|		 * Returns all pets in the database.
+		|		 *
+		|		 * @return All pets in the database.
+		|		 * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+		|		 */</jd>
+		|		<ja>@RemoteGet</ja>(<js>"/pet"</js>)
+		|		<jk>public</jk> Collection<Pet> getPets() <jk>throws</jk> NotAcceptable;
+		|	
+		|		<jd>/**
+		|		 * Returns a pet from the database.
+		|		 *
+		|		 * @param petId The ID of the pet to retrieve.
+		|		 * @return The pet.
+		|		 * @throws IdNotFound Pet was not found.
+		|		 * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+		|		 */</jd>
+		|		<ja>@RemoteGet</ja>(<js>"/pet/{petId}"</js>)
+		|		<jk>public</jk> Pet getPet(
+		|			<ja>@Path</ja>(
+		|				name=<js>"petId"</js>,
+		|				schema=<ja>@Schema</ja>(description=<js>"ID of pet to return"</js>)
+		|			)
+		|			<jk>long</jk> <jv>petId</jv>
+		|		) <jk>throws</jk> IdNotFound, NotAcceptable;
+		|	
+		|		<jd>/**
+		|		 * Adds a pet to the database.
+		|		 *
+		|		 * @param pet The pet data to add to the database.
+		|		 * @return {@link Ok} if successful.
+		|		 * @throws IdConflict ID already in use.
+		|		 * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+		|		 * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header specified.
+		|		 */</jd>
+		|		<ja>@RemotePost</ja>(<js>"/pet"</js>)
+		|		<jk>public long</jk> createPet(
+		|			<ja>@Content</ja>(
+		|				schema=<ja>@Schema</ja>(description=<js>"Pet object to add to the store"</js>)
+		|			)
+		|			CreatePet <jv>pet</jv>
+		|		) <jk>throws</jk> IdConflict, NotAcceptable, UnsupportedMediaType;
+		|	
+		|		<jd>/**
+		|		 * Updates a pet in the database.
+		|		 *
+		|		 * @param pet The pet data to add to the database.
+		|		 * @return {@link Ok} if successful.
+		|		 * @throws IdNotFound ID not found.
+		|		 * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+		|		 * @throws UnsupportedMediaType Unsupported <c>Content-Type</c> header specified.
+		|		 */</jd>
+		|		<ja>@RemotePut</ja>(<js>"/pet/{petId}"</js>)
+		|		<jk>public</jk> Ok updatePet(
+		|			<ja>@Content</ja>(
+		|				schema=<ja>@Schema</ja>(description=<js>"Pet object that needs to be added to the store"</js>)
+		|			)
+		|			UpdatePet <jv>pet</jv>
+		|		) <jk>throws</jk> IdNotFound, NotAcceptable, UnsupportedMediaType;
+		|	
+		|		<jd>/**
+		|		 * Find all pets with the matching statuses.
+		|		 *
+		|		 * @param status The statuses to match against.
+		|		 * @return The pets that match the specified statuses.
+		|		 * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+		|		 */</jd>
+		|		<ja>@RemoteGet</ja>(<js>"/pet/findByStatus"</js>)
+		|		<jk>public</jk> Collection&lt;Pet&gt; findPetsByStatus(
+		|			<ja>@Query</ja>(
+		|				name=<js>"status"</js>,
+		|				schema=<ja>@Schema</ja>(
+		|					description=<js>"Status values that need to be considered for filter."</js>,
+		|					required=<jk>true</jk>,
+		|					type=<js>"array"</js>,
+		|					collectionFormat=<js>"csv"</js>,
+		|					items=<ja>@Items</ja>(
+		|						type=<js>"string"</js>,
+		|						_enum=<js>"AVAILABLE,PENDING,SOLD"</js>,
+		|						_default=<js>"AVAILABLE"</js>
+		|					)
+		|				)
+		|			)
+		|			PetStatus[] <jv>status</jv>
+		|		) <jk>throws</jk> NotAcceptable;
+		|	
+		|		<jd>/**
+		|		 * Deletes the specified pet.
+		|		 *
+		|		 * @param apiKey Security key.
+		|		 * @param petId ID of pet to delete.
+		|		 * @return {@link Ok} if successful.
+		|		 * @throws IdNotFound Pet not found.
+		|		 * @throws NotAcceptable Unsupported <c>Accept</c> header specified.
+		|		 */</jd>
+		|		<ja>@RemoteDelete</ja>(<js>"/pet/{petId}"</js>)
+		|		<jk>public</jk> Ok deletePet(
+		|			<ja>@Header</ja>(
+		|				name=<js>"api_key"</js>,
+		|				schema=<ja>@Schema</ja>(
+		|					description=<js>"Security API key"</js>,
+		|					required=<jk>true</jk>
+		|				)
+		|			)
+		|			String <jv>apiKey</jv>,
+		|			<ja>@Path</ja>(
+		|				name=<js>"petId"</js>,
+		|				schema=<ja>@Schema(description=<js>"Pet id to delete"</js>)
+		|			)
+		|			<jk>long</jk> <jv>petId</jv>
+		|		) <jk>throws</jk> IdNotFound, NotAcceptable;
+		|	
+		|		<jd>/**
+		|		 * Deletes all pets in the database.
+		|		 *
+		|		 * @return {@link Ok} if successful.
+		|		 */</jd>
+		|		<ja>@RemoteDelete</ja>(<js>"/pet"</js>)
+		|		<jk>public</jk> Ok deleteAllPets();
+		|	
+		|		<jc>//------------------------------------------------------------------------------------------------------------------
+		|		// Orders
+		|		//------------------------------------------------------------------------------------------------------------------</jc>
+		|	
+		|		...
+		|	}
+	</p>
+
+	<p>
+		<ja>@Remote</ja> and <ja>@RemoteX</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>9.x.x<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>, 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>
\ No newline at end of file
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/03.jp.juneau-petstore-client.html b/juneau-doc/docs/Topics/16.juneau-petstore/03.jp.juneau-petstore-client.html
new file mode 100644
index 000000000..6cc8b6014
--- /dev/null
+++ b/juneau-doc/docs/Topics/16.juneau-petstore/03.jp.juneau-petstore-client.html
@@ -0,0 +1,47 @@
+<!--
+/***************************************************************************************************************************
+ * 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.
+ ***************************************************************************************************************************/
+ -->
+
+{title:'juneau-petstore-client', created:'9.0.0'}
+
+<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[] <jv>args</jv>) {
+		|	
+		|			<jc>// Create a RestClient with JSON serialization support.</jc>
+		|			RestClient <jv>restClient</jv> = RestClient.<jsm>create</jsm>().simpleJson().build();
+		|	
+		|			<jc>// Instantiate our proxy.</jc>
+		|			PetStore <jv>petStore</jv> = <jv>restClient</jv>.getRemote(PetStore.<jk>class</jk>, <js>"http://localhost:5000"</js>);
+		|	
+		|			<jc>// Print out the pets in the store.</jc>
+		|			Collection&lt;Pet&gt; <jv>pets</jv> = <jv>petStore</jv>.getPets();
+		|	
+		|			<jc>// Pretty-print them to STDOUT.</jc>
+		|			SimpleJson.<jsf>DEFAULT_READABLE</jsf>.println(<jv>pets</jv>);
+		|		}
+		|	}
+	</p>
+	<p>
+		Notice how little code is necessary to construct a remote proxy.
+	</p>
+</div>
\ No newline at end of file
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/04.jp.juneau-petstore-server.html b/juneau-doc/docs/Topics/16.juneau-petstore/04.jp.juneau-petstore-server.html
new file mode 100644
index 000000000..99f51c13c
--- /dev/null
+++ b/juneau-doc/docs/Topics/16.juneau-petstore/04.jp.juneau-petstore-server.html
@@ -0,0 +1,363 @@
+<!--
+/***************************************************************************************************************************
+ * 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.
+ ***************************************************************************************************************************/
+ -->
+
+{title:'juneau-petstore-server', created:'9.0.0'}
+
+<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.  For brevity, the app and configuration classes are combined into
+		the following:
+	</p>
+	
+	<h5 class='figure'>App.java</h5>
+	<p class='bcode w800'>
+		|	<ja>@SpringBootApplication</ja>
+		|	<ja>@EnableJpaRepositories</ja>(basePackages=<js>"org.apache.juneau.petstore"</js>)
+		|	<ja>@EnableCaching</ja>
+		|	<ja>@Controller</ja>
+		|	<jk>public class</jk> App {
+		|	
+		|		<jc>//-----------------------------------------------------------------------------------------------------------------
+		|		// App
+		|		//-----------------------------------------------------------------------------------------------------------------</jc>
+		|	
+		|		<jk>public static void</jk> main(String[] <jv>args</jv>) {
+		|			<jk>try</jk> {
+		|				<jk>new</jk> SpringApplicationBuilder(App.<jk>class</jk>).run(<jv>args</jv>);
+		|			} <jk>catch</jk> (Exception <jv>e</jv>) {
+		|				<jv>e</jv>.printStackTrace();
+		|			}
+		|		}
+		|	
+		|		<jc>//-----------------------------------------------------------------------------------------------------------------
+		|		// Beans
+		|		//-----------------------------------------------------------------------------------------------------------------</jc>
+		|	
+		|		<ja>@Bean</ja>
+		|		<jk>public</jk> PetStoreService petStoreService() {
+		|			<jk>return new</jk> PetStoreService();
+		|		}
+		|	
+		|		<ja>@Bean</ja>
+		|		<jk>public</jk> RootResources rootResources() {
+		|			<jk>return new</jk> RootResources();
+		|		}
+		|	
+		|		<ja>@Bean</ja>
+		|		<jk>public</jk> PetStoreResource petStoreResource() {
+		|			<jk>return new</jk> PetStoreResource();
+		|		}
+		|	
+		|		<ja>@Bean</ja>
+		|		<jk>public</jk> ServletRegistrationBean<Servlet> getRootServlet(RootResources <jv>rootResources</jv>) {
+		|			<jk>return new</jk> ServletRegistrationBean<>(<jv>rootResources</jv>, <js>"/*"</js>);
+		|		}
+		|	}
+	</p>
+	<p>
+		Notice how cleanly Juneau servlets fit into Spring Boot.  No special initializers are required
+		to integrate Juneau with Spring Boot.
+	</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>
+		|		},
+		|		navlinks={
+		|			<js>"options: ?method=OPTIONS"</js>,
+		|			<js>"$W{ContentTypeMenuItem}"</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>
+		|		}
+		|	)
+		|	<jk>public class</jk> RootResources <jk>extends</jk> BasicSpringRestServletGroup <jk>implements</jk> BasicUniversalConfig {
+		|		<jk>private static final long</jk> <jsf>serialVersionUID</jsf> = 1L;
+		|	}
+	</p>
+	<p>
+		By extending from <c>BasicSpringRestServletGroup</c>, the root servlet hooks into the injection framework of Spring
+		to resolve spring beans such as the child resource <c>PetStoreResource</c>.
+	</p>
+	<p>
+		This page renders as follows:
+	</p>
+	
+	<p class='bcode w800'>
+		|	http://localhost:5000
+	</p>			
+	<img class='bordered w800' src='doc-files/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>@Swagger</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>,
+		|		},
+		|		navlinks={
+		|			<js>"up: request:/.."</js>,
+		|			<js>"options: servlet:/?method=OPTIONS"</js>,
+		|			<js>"$W{ContentTypeMenuItem}"</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> BasicRestObject <jk>implements</jk> BasicUniversalConfig, PetStore {
+		|	
+		|		<ja>@Inject</ja>
+		|		<jk>private</jk> PetStoreService <jf>store</jf>;
+		|	
+		|		<jd>/**
+		|		 * Navigation page
+		|		 *
+		|		 * @return Navigation page contents.
+		|		 */</jd>
+		|		<ja>@RestGet</ja>(
+		|			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='doc-files/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>@RestGet</ja>(
+		|		path=<js>"/pet"</js>,
+		|		summary=<js>"All pets in the store"</js>,
+		|		swagger=<ja>@OpSwagger</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>@Bean</ja>(on=<js>"Pet"</js>, excludeProperties=<js>"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>@RestGet</ja>(
+		|		path=<js>"/pet/{petId}"</js>,
+		|		summary=<js>"Find pet by ID"</js>,
+		|		description=<js>"Returns a single pet"</js>,
+		|		swagger=<ja>@OpSwagger</ja>(
+		|			tags=<js>"pet"</js>
+		|		)
+		|	)
+		|	<jk>public</jk> Pet getPet(<jk>long</jk> <jv>petId</jv>) <jk>throws</jk> IdNotFound, NotAcceptable {
+		|		<jk>return</jk> <jf>store</jf>.getPet(<jv>petId</jv>);
+		|	}
+		|	
+		|	<ja>@Override</ja> <jc>/* PetStore */</jc>
+		|	<ja>@RestPost</ja>(
+		|		path=<js>"/pet"</js>,
+		|		summary=<js>"Add a new pet to the store"</js>,
+		|		swagger=<ja>@OpSwagger</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 <jv>pet</jv>) <jk>throws</jk> IdConflict, NotAcceptable, UnsupportedMediaType {
+		|		<jk>return</jk> <jf>store</jf>.create(<jv>pet</jv>).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/pet
+	</p>			
+	<img class='bordered w800' src='doc-files/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='doc-files/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='doc-files/9b.png'>
+	<p>
+		Information for all HTTP parts is automatically generated:
+	</p>
+	<img class='bordered w900' src='doc-files/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='doc-files/9c.png'>
+	<p>
+		Auto-generated examples are available for all supported languages:
+	</p>
+	<img class='bordered w900' src='doc-files/9d.png'>
+	<p>
+		For example, <l>application/json+simple</l>:
+	</p>
+	<img class='bordered w900' src='doc-files/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='doc-files/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='doc-files/9g.png'>
+	
+</div>
\ No newline at end of file
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9a.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9a.png
new file mode 100644
index 000000000..33a1791d8
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9a.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9b.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9b.png
new file mode 100644
index 000000000..85720f204
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9b.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9c.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9c.png
new file mode 100644
index 000000000..23a3e7ff9
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9c.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9d.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9d.png
new file mode 100644
index 000000000..f183863a5
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9d.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9e.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9e.png
new file mode 100644
index 000000000..286a78ffe
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9e.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9f.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9f.png
new file mode 100644
index 000000000..dd0013c9e
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9f.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9g.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9g.png
new file mode 100644
index 000000000..a5c0b6c14
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9g.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9h.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9h.png
new file mode 100644
index 000000000..084e7ce49
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/9h.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/PetStore.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/PetStore.png
new file mode 100644
index 000000000..78b98c896
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/PetStore.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/PetStore_pets.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/PetStore_pets.png
new file mode 100644
index 000000000..e5e5c722f
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/PetStore_pets.png differ
diff --git a/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/RootResources.png b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/RootResources.png
new file mode 100644
index 000000000..a4e5d96e4
Binary files /dev/null and b/juneau-doc/docs/Topics/16.juneau-petstore/doc-files/RootResources.png differ
diff --git a/juneau-doc/docs/overview_template.html b/juneau-doc/docs/overview_template.html
index 7d458fe27..a41e70145 100644
--- a/juneau-doc/docs/overview_template.html
+++ b/juneau-doc/docs/overview_template.html
@@ -73,25 +73,25 @@
 	<tr class='dark bb'>
 		<td rowspan="4" style='text-align:center;font-weight:bold;padding:20px;'>Juneau Core</td>
 		<td class='code'><a class='doclink' href='#juneau-marshall'>juneau-marshall</a></td>
-		<td>
+		<td style='padding:10px'>
 			A universal toolkit for marshalling POJOs to a wide variety of content types using a common framework with no external library dependencies.
 		</td>
 	</tr>
 	<tr class='dark bb'>
 		<td class='code'><a class='doclink' href='#juneau-marshall-rdf'>juneau-marshall-rdf</a></td>
-		<td>
+		<td style='padding:10px'>
 			Extended marshalling support for RDF languages.				
 		</td>
 	</tr>
 	<tr class='dark bb'>
 		<td class='code'><a class='doclink' href='#juneau-dto'>juneau-dto</a></td>
-		<td>
+		<td style='padding:10px'>
 			A variety of predefined DTOs for serializing and parsing languages such as HTML5, Swagger and ATOM.
 		</td>
 	</tr>
 	<tr class='dark bb'>
 		<td class='code'><a class='doclink' href='#juneau-config'>juneau-config</a></td>
-		<td>
+		<td style='padding:10px'>
 			A sophisticated configuration file API.
 		</td>
 	</tr>
@@ -99,32 +99,32 @@
 	<tr class='light bb'>
 		<td rowspan="5" style='text-align:center;font-weight:bold;padding:20px;'>Juneau REST</td>
 		<td class='code'><a class='doclink' href='#juneau-rest-server'>juneau-rest-server</a></td>
-		<td>
+		<td style='padding:10px'>
 			A universal REST server API for creating Swagger-based self-documenting REST interfaces using POJOs, simply deployed as 
 			one or more top-level servlets in any Servlet 3.1.0+ container.
 		</td>
 	</tr>
 	<tr class='light bb'>
 		<td class='code'><a class='doclink' href='#juneau-rest-server-springboot'>juneau-rest-server-springboot</a></td>
-		<td>
+		<td style='padding:10px'>
 			Spring boot integration support.
 		</td>
 	</tr>
 	<tr class='light bb'>
 		<td class='code'><a class='doclink' href='#juneau-rest-server-jaxrs'>juneau-rest-server-jaxrs</a></td>
-		<td>
+		<td style='padding:10px'>
 			JAX/RS integration support.
 		</td>
 	</tr>
 	<tr class='light bb'>
 		<td class='code'><a class='doclink' href='#juneau-rest-client'>juneau-rest-client</a></td>
-		<td>
+		<td style='padding:10px'>
 			A universal REST client API for interacting with Juneau or 3rd-party REST interfaces using POJOs and proxy interfaces.
 		</td>
 	</tr>
 	<tr class='light bb'>
 		<td class='code'><a class='doclink' href='#juneau-rest-mock'>juneau-rest-mock</a></td>
-		<td>
+		<td style='padding:10px'>
 			Mocking APIs for server-less end-to-end testing of REST server and client APIs.
 		</td>
 	</tr>
@@ -132,25 +132,25 @@
 	<tr class='dark bb'>
 		<td rowspan="4" style='text-align:center;font-weight:bold;padding:20px;'>Examples</td>
 		<td class='code'>juneau-examples-core</td>
-		<td>
+		<td style='padding:10px'>
 			Juneau Core API examples.
 		</td>
 	</tr>
 	<tr class='dark bb'>
 		<td class='code'>juneau-examples-rest</td>
-		<td>
+		<td style='padding:10px'>
 			Juneau REST API examples.
 		</td>
 	</tr>
 	<tr class='dark bb'>
 		<td class='code'>juneau-examples-rest-jetty</td>
-		<td>
+		<td style='padding:10px'>
 			Juneau REST API examples using Jetty deployment.
 		</td>
 	</tr>
 	<tr class='dark bb'>
 		<td class='code'>juneau-examples-rest-springboot</td>
-		<td>
+		<td style='padding:10px'>
 			Juneau REST API examples using Spring Boot deployment.
 		</td>
 	</tr>