The juneau-petstore-server 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:
| @SpringBootApplication
| @EnableJpaRepositories(basePackages="org.apache.juneau.petstore")
| @EnableCaching
| @Controller
| public class App {
|
| //-----------------------------------------------------------------------------------------------------------------
| // App
| //-----------------------------------------------------------------------------------------------------------------
|
| public static void main(String[] args) {
| try {
| new SpringApplicationBuilder(App.class).run(args);
| } catch (Exception e) {
| e.printStackTrace();
| }
| }
|
| //-----------------------------------------------------------------------------------------------------------------
| // Beans
| //-----------------------------------------------------------------------------------------------------------------
|
| @Bean
| public PetStoreService petStoreService() {
| return new PetStoreService();
| }
|
| @Bean
| public RootResources rootResources() {
| return new RootResources();
| }
|
| @Bean
| public PetStoreResource petStoreResource() {
| return new PetStoreResource();
| }
|
| @Bean
| public ServletRegistrationBean getRootServlet(RootResources rootResources) {
| return new ServletRegistrationBean<>(rootResources, "/*");
| }
| }
Notice how cleanly Juneau servlets fit into Spring Boot. No special initializers are required
to integrate Juneau with Spring Boot.
The RootResources 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...PetStoreResource:
| @Rest(
| path="/*",
| title="Root resources",
| description="Example of a router resource page.",
| children={
| PetStoreResource.class
| }
| )
| @HtmlDocConfig(
| widgets={
| ContentTypeMenuItem.class
| },
| navlinks={
| "options: ?method=OPTIONS",
| "$W{ContentTypeMenuItem}",
| "source: $C{Source/gitHub}/org/apache/juneau/petstore/rest/$R{servletClassSimple}.java"
| },
| aside={
| "<div style='max-width:400px' class='text'>",
| " <p>This is an example of a 'router' page that serves as a jumping-off point to child resources.</p>",
| " <p>Resources can be nested arbitrarily deep through router pages.</p>",
| " <p>Note the <span class='link'>options</span> link provided that lets you see the generated swagger doc for this page.</p>",
| " <p>Also note the <span class='link'>sources</span> link on these pages to view the source code for the page.</p>",
| " <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>",
| " <p>Other features (such as this aside) are added through annotations.</p>",
| "</div>"
| }
| )
| public class RootResources extends BasicSpringRestServletGroup {
| private static final long serialVersionUID = 1L;
| }
By extending from BasicSpringRestServletGroup, the root servlet hooks into the injection framework of Spring
to resolve spring beans such as the child resource PetStoreResource.
This page renders as follows:
| http://localhost:5000
The PetStoreResource class is the REST implementation of our PetStore interface.
| @Rest(
| path="/petstore",
| title="Petstore application",
| description={
| "This is a sample server Petstore server based on the Petstore sample at Swagger.io.",
| "You can find out more about Swagger at http://swagger.io.",
| },
| swagger=@Swagger(
| version="1.0.0",
| title="Swagger Petstore",
| termsOfService="You are on your own.",
| contact=@Contact(
| name="Juneau Development Team",
| email="dev@juneau.apache.org",
| url="http://juneau.apache.org"
| ),
| license=@License(
| name="Apache 2.0",
| url="http://www.apache.org/licenses/LICENSE-2.0.html"
| ),
| externalDocs=@ExternalDocs(
| description="Find out more about Juneau",
| url="http://juneau.apache.org"
| ),
| tags={
| @Tag(
| name="pet",
| description="Everything about your Pets",
| externalDocs=@ExternalDocs(
| description="Find out more",
| url="http://juneau.apache.org"
| )
| ),
| @Tag(
| name="store",
| description="Access to Petstore orders"
| ),
| @Tag(
| name="user",
| description="Operations about user",
| externalDocs=@ExternalDocs(
| description="Find out more about our store",
| url="http://juneau.apache.org"
| )
| )
| }
| ),
| staticFiles={"htdocs:/htdocs"}
| )
| @HtmlDocConfig(
| widgets={
| ContentTypeMenuItem.class,
| },
| navlinks={
| "up: request:/..",
| "options: servlet:/?method=OPTIONS",
| "$W{ContentTypeMenuItem}",
| "source: $C{Source/gitHub}/org/apache/juneau/petstore/rest/$R{servletClassSimple}.java"
| },
| head={
| "<link rel='icon' href='$U{servlet:/htdocs/cat.png}'/>" // Add a cat icon to the page.
| },
| header={
| "<h1>$R{resourceTitle}</h1>",
| "<h2>$R{methodSummary}</h2>",
| "$C{PetStore/headerImage}"
| },
| aside={
| "<div style='max-width:400px' class='text'>",
| " <p>This page shows a standard nested REST resource.</p>",
| " <p>It shows how different properties can be rendered on the same bean in different views.</p>",
| " <p>It also shows examples of HtmlRender classes and @BeanProperty(format) annotations.</p>",
| " <p>It also shows how the Queryable converter and query widget can be used to create searchable interfaces.</p>",
| "</div>"
| },
| stylesheet="servlet:/htdocs/themes/dark.css" // Use dark theme by default.
| )
| public class PetStoreResource extends BasicRestObject implements PetStore {
|
| @Autowired
| private PetStoreService store;
|
| /**
| * Navigation page
| *
| * @return Navigation page contents.
| */
| @RestGet(
| path="/",
| summary="Navigation page"
| )
| @HtmlDocConfig(
| style={
| "INHERIT", // Flag for inheriting resource-level CSS.
| "body { ",
| "background-image: url('petstore/htdocs/background.jpg'); ",
| "background-color: black; ",
| "background-size: cover; ",
| "background-attachment: fixed; ",
| "}"
| }
| )
| public ResourceDescriptions getTopPage() {
| return new ResourceDescriptions()
| .append("pet", "All pets in the store")
| .append("store", "Orders and inventory")
| .append("user", "Petstore users")
| ;
| }
|
| ...
Clicking the petstore link on the root page takes you to our PetStore resource:
| http://localhost:5000/petstore
The methods defined in our PetStore interface are implemented like so:
| @Override /* PetStore */
| @RestGet(
| path="/pet",
| summary="All pets in the store",
| swagger=@OpSwagger(
| tags="pet",
| parameters={
| Queryable.SWAGGER_PARAMS // Documents searching.
| }
| ),
| converters={Queryable.class} // Searching support.
| )
| @Bean(on="Pet", excludeProperties="tags,photo") // In this view, don't serialize tags/photos properties.
| public Collection<Pet> getPets() throws NotAcceptable {
| return store.getPets();
| }
|
| @Override /* PetStore */
| @RestGet(
| path="/pet/{petId}",
| summary="Find pet by ID",
| description="Returns a single pet",
| swagger=@OpSwagger(
| tags="pet"
| )
| )
| public Pet getPet(long petId) throws IdNotFound, NotAcceptable {
| return store.getPet(petId);
| }
|
| @Override /* PetStore */
| @RestPost(
| path="/pet",
| summary="Add a new pet to the store",
| swagger=@OpSwagger(
| tags="pet"
| ),
| roleGuard="ROLE_ADMIN || (ROLE_USER && ROLE_WRITABLE)" // Restrict access to this method.
| )
| public long createPet(CreatePet pet) throws IdConflict, NotAcceptable, UnsupportedMediaType {
| return store.create(pet).getId();
| }
|
| ...
After running the Main method in the client code to populate the database, the page renders as follows:
| http://localhost:5000/petstore/pet
The OPTIONS menu items takes you to the auto-generated Swagger UI for the application:
| http://localhost:10000/petstore/pet?method=OPTIONS
Since we've defined tags on our annotations, the pet-related operations are all grouped under the pet tag:
Information for all HTTP parts is automatically generated:
The schema models for POJO models is available in the Responses section of an operation:
Auto-generated examples are available for all supported languages:
For example, application/json5:
Examples can be derived in a number of ways. In our case, we've defined a static method on our Pet
class annotated with @Example:
| @Example
| public static Pet example() {
| return new Pet()
| .id(123)
| .species(Species.DOG)
| .name("Doggie")
| .tags("friendly","smart")
| .status(PetStatus.AVAILABLE);
| }
Similar functionality exists for request bodies as well:
At the bottom of the page is a listing of the POJO models in the app: