PetStore Application

The PetStore 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.

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.

The GitHub project hosting the application can be found here.

The project is broken down into the following subprojects:

juneau-petstore-api

The juneau-petstore-api 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.

The PetStore 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.

PetStore.java

@RemoteResource(path="/petstore") public interface PetStore { @RemoteMethod(method=GET, path="/pet") public Collection<Pet> getPets() throws NotAcceptable; @RemoteMethod(path="/pet/{petId}") public Pet getPet( @Path( name="petId", description="ID of pet to return", example="123" ) long petId ) throws IdNotFound, NotAcceptable; @RemoteMethod(method=POST, path="/pet") public long createPet( @Body( description="Pet object to add to the store" ) CreatePet pet ) throws IdConflict, NotAcceptable, UnsupportedMediaType; ... }

@RemoteResource and @RemoteMethod are client-side annotations used to map the method calls to REST and will be describe in the client code section.

@Path and @Body are used by both the client and server side code to map to REST artifacts on both sides.

Both sets of annotations are provided by pulling in the Juneau dependency below:

Maven Dependency

<dependency> <groupId>org.apache.juneau</groupId> <artifactId>juneau-marshall</artifactId> <version>8.1.0</version> </dependency>

The Pet 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.

Pet.java

@Bean(typeName="Pet", fluentSetters=true, properties="id,species,name,tags,price,status") @Entity(name="PetstorePet") public class Pet { @Column @Id @GeneratedValue @Schema(description="Unique identifier for this pet.") @Html(link="servlet:/pet/{id}") private long id; @Column(length=50) @Schema(description="Pet name.", minLength=3, maxLength=50) private String name; @Column @Schema(description="Price of pet.", maximum="999.99") @Html(render=PriceRender.class) private float price; ... }

The annotations here are a combination of Juneau annotations for controlling marshalling (@Bean, @Html) and documentation/validation (@Schema), and JPA annoations for database persistence (@Entity, @Column).

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.

juneau-petstore-client

The juneau-petstore-client module contains a single Main class used to instantiate the proxy against our remote REST interface using the Java interface described above.

Main.java

public class Main { public static void main(String[] args) { // Create a RestClient with JSON serialization support. try (RestClient rc = RestClient.create(SimpleJsonSerializer.class JsonParser.class).build()) { // Instantiate our proxy. PetStore petStore = rc.getRemoteResource(PetStore.class, "http://localhost:5000"); // Print out the pets in the store. Collection<Pet> pets = petStore.getPets(); // Pretty-print them to STDOUT. SimpleJson.DEFAULT_READABLE.println(pets); } catch (Exception e) { e.printStackTrace(); } } }

Notice how little code is necessary to construct a remote proxy.

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.

App.java

@SpringBootApplication public class App { public static void main(String[] args) { new App().start(args); } protected void start(String[] args) { new SpringApplicationBuilder(App.class) .initializers(new JuneauRestInitializer(App.class)) .run(args); } }

AppConfiguration.java

@Configuration public class AppConfiguration { @Bean public PetStoreService petStoreService() { return new PetStoreService(); } @Bean @JuneauRestRoot public RootResources rootResources() { return new RootResources(); } @Bean public PetStoreResource petStoreResource() { return new PetStoreResource(); } }

The JuneauRestInitializer 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.

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

@RestResource( path="/*", title="Root resources", description="Example of a router resource page.", children={ PetStoreResource.class } ) @HtmlDocConfig( widgets={ ContentTypeMenuItem.class, ThemeMenuItem.class }, navlinks={ "options: ?method=OPTIONS", "$W{ContentTypeMenuItem}", "$W{ThemeMenuItem}", "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>" } ) @SerializerConfig( // 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. quoteChar="'" ) public class RootResources extends BasicRestServletGroup { private static final long serialVersionUID = 1L; }

This page renders as follows:

http://localhost:5000

The PetStoreResource class is the REST implementation of our PetStore interface.

PetStoreResource.java

@RestResource( 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=@ResourceSwagger( 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, ThemeMenuItem.class, }, navlinks={ "up: request:/..", "options: servlet:/?method=OPTIONS", "$W{ContentTypeMenuItem}", "$W{ThemeMenuItem}", "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 BasicRest implements PetStore { @Inject private PetStoreService store; /** * Navigation page * * @return Navigation page contents. */ @RestMethod( name=GET, 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 */ @RestMethod( name=GET, path="/pet", summary="All pets in the store", swagger=@MethodSwagger( tags="pet", parameters={ Queryable.SWAGGER_PARAMS // Documents searching. } ), converters={Queryable.class} // Searching support. ) @BeanConfig( bpx="Pet: tags,photo" // In this view, don't serialize tags/photos properties. ) public Collection<Pet> getPets() throws NotAcceptable { return store.getPets(); } @Override /* PetStore */ @RestMethod( name=GET, path="/pet/{petId}", summary="Find pet by ID", description="Returns a single pet", swagger=@MethodSwagger( tags="pet" ) ) public Pet getPet(long petId) throws IdNotFound, NotAcceptable { return store.getPet(petId); } @Override /* PetStore */ @RestMethod( name=POST, path="/pet", summary="Add a new pet to the store", swagger=@MethodSwagger( 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/pets

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/json+simple:

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: