Skip to main content

juneau-petstore-server

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 App class:

App.java

@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<RootResources> 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:

RootResources.java

@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={
"",
" This is an example of a 'router' page that serves as a jumping-off point to child resources.",
" Resources can be nested arbitrarily deep through router pages.",
" Note the options link provided that lets you see the generated swagger doc for this page.",
" Also note the sources link on these pages to view the source code for the page.",
" 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'.",
" Other features (such as this aside) are added through annotations.",
""
}
)
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.

PetStoreResource.java

@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={
"# $R{resourceTitle}",
"## $R{methodSummary}",
"$C{PetStore/headerImage}"
},
aside={
"",
" This page shows a standard nested REST resource.",
" It shows how different properties can be rendered on the same bean in different views.",
" It also shows examples of HtmlRender classes and @BeanProperty(format) annotations.",
" It also shows how the Queryable converter and query widget can be used to create searchable interfaces.",
""
},
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:

PetStoreResource.java

@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:

Pet.java

@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: