Marshallingcreated: 9.0.0
End-to-End RESTcreated: 9.0.0
REST Clientcreated: 9.0.0
DTOscreated: 9.0.0
Config Filescreated: 9.0.0
Fluent Assertionscreated: 9.0.0
General Designcreated: 9.0.0
Serializers and Parsersupdated: 9.0.0
Bean Contextsupdated: 9.0.0
Java Beans Supportcreated: 8.2.0, updated: 9.0.0
@Bean Annotationupdated: 8.2.0,9.0.0
@Beanp Annotationupdated: 8.1.0,8.1.2,9.0.0
@Beanc Annotationupdated: 8.1.0,8.1.2
POJO Buildersupdated: 9.0.0
HTTP Part Serializers and Parsersupdated: 8.2.0,9.0.0
Context Settingsupdated: 8.1.3,9.0.0
Context Annotationscreated: 8.1.0, updated: 8.1.3,8.2.0,9.0.0
JsonMap and JsonListupdated: 8.2.0
Complex Data Typescreated: 9.0.0
SerializerSets and ParserSetsupdated: 9.0.0
Swapsupdated: 9.0.0
Auto-detected swapscreated: 8.1.0
Per-media-type Swapsupdated: 8.1.0,8.2.0
@Swap Annotationupdated: 8.0.0,9.0.0
Dynamically Applied Annotationscreated: 8.1.3, updated: 9.0.0
Bean Names and Dictionariesupdated: 9.0.0
Virtual Beansupdated: 9.0.0
Non-Tree Models and Recursion Detectionupdated: 9.0.0
Parsing into Generic Modelsupdated: 8.2.0
Reading Continuous Streamsupdated: 9.0.0
URIsupdated: 9.0.0
Comparison with Jacksonupdated: 9.0.0
SVL Variablesupdated: 8.0.0,8.1.0
VarResolvers and VarResolverSessionsupdated: 9.0.0
VarResolver.DEFAULTcreated: 8.1.0
Encoderscreated: 9.0.0
Object Toolscreated: 9.0.0
JSON Serializersupdated: 9.0.0
JSON 5updated: 9.0.0
JSON Parsersupdated: 9.0.0
XML Serializersupdated: 9.0.0
XML Parsersupdated: 9.0.0
HTML Serializersupdated: 9.0.0
HTML Parsersupdated: 9.0.0
HtmlDocSerializerupdated: 9.0.0
UON Serializersupdated: 9.0.0
UON Parsersupdated: 9.0.0
URL-Encoding Serializersupdated: 9.0.0
URL-Encoding Parsersupdated: 9.0.0
MessagePack Serializersupdated: 9.0.0
MessagePack Parsersupdated: 9.0.0
OpenAPI Detailsupdated: 8.2.0
OpenAPI Methodologyupdated: 8.2.0
OpenAPI Serializersupdated: 8.2.0,9.0.0
OpenAPI Parsersupdated: 8.2.0,9.0.0
Overviewupdated: 9.0.0
Reading Entriesupdated: 9.0.0
POJOsupdated: 9.0.0
Java Collection Framework Objectsupdated: 9.0.0
Binary Dataupdated: 9.0.0
Variablesupdated: 9.0.0
Modded/Encoded Entriesupdated: 9.0.0
Sectionsupdated: 9.0.0
Serializingupdated: 9.0.0
Importsupdated: 8.1.0
Config Storesupdated: 9.0.0
FileStoreupdated: 9.0.0
Custom ConfigStoresupdated: 9.0.0
Read-only Configsupdated: 9.0.0
System Default Configcreated: 8.0.0, updated: 8.1.0
juneau-assertionscreated: 9.0.0
Overviewcreated: 9.0.0
juneau-rest-commoncreated: 9.0.0
Helper Classescreated: 9.0.0
Annotationscreated: 9.0.0
HTTP Headerscreated: 9.0.0
HTTP Partscreated: 9.0.0
HTTP Entities and Resourcescreated: 9.0.0
HTTP Responsescreated: 9.0.0
Remote Proxy Interfacescreated: 9.0.0
juneau-rest-serverupdated: 9.0.0
Overviewcreated: 9.0.0
@Rest-Annotated Classesupdated: 8.1.2,9.0.0
Predefined Classesupdated: 9.0.0
Child Resourcesupdated: 9.0.0
Path Variablesupdated: 9.0.0
Deploymentupdated: 9.0.0
Lifecycle Hooksupdated: 9.0.0
@RestOp-Annotated Methodsupdated: 9.0.0
Inferred HTTP Methods and Pathsupdated: 9.0.0
Java Method Parametersupdated: 9.0.0
Java Method Return Typesupdated: 9.0.0
Java Method Throwable Typesupdated: 9.0.0
Path Patternsupdated: 9.0.0
Matchersupdated: 9.0.0
Additional Informationupdated: 9.0.0
HTTP Partsupdated: 9.0.0
Part Marshallersupdated: 8.1.0,9.0.0
HTTP Part Annotationsupdated: 8.1.0,9.0.0
Default Partscreated: 9.0.0
@Request Beansupdated: 8.1.0,9.0.0
@Response Beansupdated: 8.1.0,9.0.0
HTTP Part APIscreated: 9.0.0
Marshallingupdated: 9.0.0
Form Postsupdated: 9.0.0
Guardsupdated: 9.0.0
Convertersupdated: 9.0.0
Localized Messagesupdated: 8.2.0,9.0.0
Encodersupdated: 9.0.0
Configuration Filesupdated: 9.0.0
SVL Variablesupdated: 9.0.0
Static filesupdated: 9.0.0
Client Versioningupdated: 9.0.0
BasicRestServlet/BasicRestObjectupdated: 8.1.0,9.0.0
Basic Swagger Infoupdated: 9.0.0
Parametersupdated: 9.0.0
Modelsupdated: 9.0.0
REST method execution statisticscreated: 8.1.3, updated: 9.0.0
@HtmlDocConfigupdated: 8.1.0,9.0.0
Widgetsupdated: 9.0.0
Predefined Widgetsupdated: 9.0.0
UI Customizationupdated: 9.0.0
Stylesheetsupdated: 8.1.0,9.0.0
Logging / Debuggingcreated: 9.0.0
HTTP Status Codesupdated: 9.0.0
Built-in Parametersupdated: 9.0.0
RestContextcreated: 9.0.0
RestOpContextcreated: 9.0.0
Response Processorscreated: 9.0.0
REST/RPCupdated: 8.0.0,9.0.0
Serializing URIsupdated: 9.0.0
Utility Beanscreated: 9.0.0
Using with HTML Beanscreated: 9.0.0
juneau-rest-server-springbootcreated: 8.0.0, updated: 9.0.0
Overviewcreated: 8.0.0, updated: 9.0.0
juneau-rest-clientupdated: 9.0.0
POJO Marshallingcreated: 8.2.0, updated: 9.0.0
Request Partscreated: 8.2.0, updated: 9.0.0
Request Contentcreated: 8.2.0, updated: 9.0.0
Response Statuscreated: 8.1.0, updated: 9.0.0
Response Headerscreated: 8.2.0, updated: 9.0.0
Response Contentcreated: 8.2.0, updated: 9.0.0
Custom Call Handlerscreated: 8.2.0, updated: 9.0.0
Interceptorscreated: 8.2.0
REST Proxiescreated: 8.2.0, updated: 9.0.0
Logging and Debuggingcreated: 8.2.0, updated: 9.0.0
Customizing HttpClientcreated: 8.2.0, updated: 9.0.0
Extending RestClientcreated: 8.2.0
Authenticationupdated: 8.2.0
juneau-rest-mockcreated: 8.1.0, updated: 8.2.0
MockRestClientcreated: 8.2.0, updated: 9.0.0
juneau-microservice-corecreated: 8.1.0
Microservice Overviewcreated: 8.0.0
Lifecycle Methodscreated: 8.0.0
Argscreated: 8.0.0
Manifestcreated: 8.0.0
Configcreated: 8.0.0
System propertiescreated: 8.0.0
VarResolvercreated: 8.0.0
Console Commandscreated: 8.0.0
Listenerscreated: 8.0.0
juneau-microservice-jettycreated: 8.1.0
Overviewcreated: 8.0.0
Lifecycle Methodscreated: 8.0.0
Resource Classescreated: 8.0.0
Predefined Resource Classescreated: 8.0.0
Configcreated: 8.0.0
Jetty.xml filecreated: 8.0.0
UI Customizationcreated: 8.0.0
Extending JettyMicroservicecreated: 8.0.0
my-jetty-microservicecreated: 8.1.0
Installing in Eclipsecreated: 8.0.0
Running in Eclipsecreated: 8.0.0
Building and Running from Command-Linecreated: 8.0.0
my-springboot-microservicecreated: 8.0.0
Installing in Eclipsecreated: 8.0.0
Running in Eclipsecreated: 8.0.0
Building and Running from Command-Linecreated: 8.0.0
juneau-petstorecreated: 8.2.0, updated: 9.0.0
Running the Pet Store Appcreated: 9.0.0
juneau-petstore-apicreated: 9.0.0
juneau-petstore-clientcreated: 9.0.0
juneau-petstore-servercreated: 9.0.0
juneau-marshallcreated: 8.2.0
juneau-svlcreated: 8.2.0
juneau-rest-servercreated: 8.2.0
Apache Juneau™ is a single cohesive Java ecosystem for marshalling Java objects to a wide variety of language types and creating annotation-based REST end-to-end server and client APIs.
The Juneau ecosystem consists of the following parts:
Category | Maven Artifacts | Description | Prereqs |
---|---|---|---|
juneau-core | juneau-marshall |
|
|
juneau-marshall-rdf |
|
|
|
juneau-dto |
|
|
|
juneau-config |
|
|
|
juneau-assertions |
|
|
|
juneau-rest | juneau-rest-common |
|
|
juneau-rest-server |
|
|
|
juneau-rest-server-springboot |
|
|
|
juneau-rest-client |
|
|
|
juneau-rest-mock |
|
|
|
my-springboot-microservice |
|
|
|
juneau-examples | juneau-examples-core |
|
|
juneau-examples-rest |
|
||
juneau-all |
Combination of the following:
|
|
The current version of Juneau is
If you would like to work with the bleeding-edge code, you can access the
Each of the components are also packaged as stand-alone OSGi modules.
The juneau-marshall library includes easy-to-use and highly customizable serializers and parsers based around a common API. It allows you to marshall Java POJOs directly to and from a wide variety of language types without the need for intermediate Document Object Models making them extremely efficient.
Supported languages include:
The default serializers can often be used to serialize POJOs in a single line of code:
Parsing back into POJOs is equally simple for any of the supported languages. Language fragments are also supported.
Marshalling support is provided for a wide variety of POJO types including:
Marshallers like the one shown above are pairings of serializers and parsers. Serializers and parsers are builder-based using fluent methods allowing you to quickly create, clone, and modify them in single lines of code.
Many POJOs such as primitives, beans, collections, arrays, and classes with various known constructors and methods are serializable out-of-the-box.
Swaps allow you to replace non-serializable objects with serializable equivalents. The {@link org.apache.juneau.swaps} package contains a variety of predefined swaps.
Any POJO that doesn't fit into the category of a bean/collection/array/primitive and doesn't have a swap associated with it is converted to simple strings. By default, various instance and static methods and constructors on POJO classes are automatically detected and supported for marshalling a POJO to and from a string.
Beans and POJO classes, methods, fields, and constructors can also be annotated with a variety of annotations to customize how they are marshalled:
As a general rule, any capabilities provided by bean annotations can be programmatically specified via the builder APIs. This allows the marshallers to be used equivalently on either your own code that you have access to, or external code where you only have access to binaries.
Serializers and parsers can also be configured using annotations.
Config annotations are extensively used in the REST Servlet APIs to configure how POJOs are marshalled through REST interfaces.
Config variables also support embedded variables for resolving settings at runtime.
Default values for config settings can be overridden via system properties or environment variables. For example, the system property "BeanContext.sortProperties" or environment variable "BEANCONTEXT_SORTPROPERTIES" can be used to set the default value for the sort properties setting.
Bean annotations can also be programmatically attached to POJOs using config annototations like so:
The {@link org.apache.juneau.json.Json5Serializer} class can be used to serialized POJOs into JSON 5 notation.
JSON 5 is similar to JSON except for the following:
JSON 5 is still valid Javascript. The advantage to simplified JSON is you can represent it in a Java String in minimal form with minimal escaping. This is particularly useful in cases such as unit testing where you can easily validate POJOs by simplifying them to JSON 5 and do a simple string comparison.
WriterSerializer
The Marshalling API also supports UON (URL-Encoded Object Notation). It allows JSON-like data structures (OBJECT, ARRAY, NUMBER, BOOLEAN, STRING, NULL) in HTTP constructs (query parameters, form parameters, headers, URL parts) without violating RFC2396. This allows POJOs to be converted directly into these HTTP constructs which is not possible in other languages such as JSON.
(
id=1,
name=
The Marshalling API also supports schema-based OpenAPI serialization. It allows HTTP parts to be marshalled to-and-from POJOs based on OpenAPI schema definitions.
Schema-based serialization and parsing is used heavily in both the server and client REST APIs with built-in schema validations support in various HTTP part annotations.
The {@link org.apache.juneau.collections.JsonMap} and {@link org.apache.juneau.collections.JsonList} collections classes allow you
to programmatically build generic JSON data structures. They are similar in concept to
These classes provide lots of convenience methods including:
{@link org.apache.juneau.serializer.SerializerSet} and {@link org.apache.juneau.parser.ParserSet} classes allow serializers and parsers
to be retrieved by W3C-compliant HTTP Accept
and Content-Type
values:
The {@link org.apache.juneau.svl} package defines an API for a language called "Simple Variable Language".
In a nutshell, Simple Variable Language (or SVL) is text that contains variables of the form
Variables can be recursively nested within the varKey (e.g.
The SVL variables are used widely throughout various annotations defined in Juneau allowing many features to be configured via external sources such as configuration files or environment variables/system properties. The SVL APIs are extensible allowing for the addition of new types of variables.
The juneau-rest-server and juneau-rest-client libraries provide server and client side REST capabilities that can be used by themselves, or together to create simplified yet sophisticated Java-based REST communications layers that completely hide away the complexities of the REST protocol.
A typical pattern is to define a REST API on the server side:
Then define a Java interface that can be provided to consumers of your API to access your REST API:
Note that you may choose to have your service class implement your interface. The REST libraries will happily look for annotations defined on methods of parent classes and interfaces. It's up to you how you want to design it.
Finally, the {@link org.apache.juneau.rest.client.RestClient} class is used to construct a remote proxy to our REST service:
The call above translates to the following REST call:
POST http://localhost:10000/petstore/pets?debug=true HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== E-Tag: 475588d4-0b27-4f56-9296-cc683251d314 { name: 'Fluffy', price: 9.99 }
It looks simplistic but the server and client APIs are highly sophisticated libraries that allow you to perform complex tasks using very little code.
A REST resource is simply a Java class annotated with {@link org.apache.juneau.rest.annotation.Rest}. The most common case is a class that extends {@link org.apache.juneau.rest.servlet.BasicRestServlet}, which itself is simply an extension of {@link jakarta.servlet.http.HttpServlet} which allows it to be deployed as a servlet.
This is what it looks like in a browser.
http://localhost:10000/helloWorld
Child Resources are REST servlets or objects that are linked to parent resources through the {@link org.apache.juneau.rest.annotation.Rest#children() @Rest(children)} annotation.
The path of the child resource gets appended to the path of the parent resource.
So in the example above, the child resource is accessed through the URL
The advantage of using child resources is that they do not need to be declared in the JEE
The servlets in the previous section implemented the {@link org.apache.juneau.rest.config.BasicUniversalConfig} which simply defines a preconfigured set of annotations that get inherited by the child classes:
The {@link org.apache.juneau.rest.config} package contains other basic configurations for use. Annotations are aggregated from child-to-parent order allowing for these basic configurations to be extended and modified, or you can create your own annotations from scratch.
The {@link org.apache.juneau.rest.servlet.BasicRestServletGroup} class provides a default "router" page for child resources when a parent resource is nothing more than a grouping of child resources.
The
When you bring up this resource in a browser, you see the following that provides a list of navigable links to your child resources:
http://localhost:10000
The real power behind the REST server API is the ability to define Java methods as REST endpoints.
Java methods on {@link org.apache.juneau.rest.annotation.Rest @Rest}-annotated classes have the following format:
The various parts require their own topics to fully appreciate the scope of abilities but the following is a summary:
The {@link org.apache.juneau.rest.servlet.BasicRestServlet} class is the entry point for your REST resources.
It extends directly from
When the servlet
Most developers are not going to be using the
The {@link org.apache.juneau.rest.springboot.BasicSpringRestServlet} class is typically entry point for your REST resources when working within a Spring Boot environment. It extends from {@link org.apache.juneau.rest.springboot.SpringRestServlet} which provides additional capabilities including:
Most developers are not going to be using the
Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style REST calls and the ability to perform marshalling of POJOs to and from HTTP parts.
The {@link org.apache.juneau.rest.mock.MockRestClient} class is used for performing serverless unit testing of {@link org.apache.juneau.rest.annotation.Rest @Rest}-annotated and {@link org.apache.juneau.http.remote.Remote @Remote}-annotated classes. It perform full serialization and parsing of the HTTP request and responses, but bypasses the network layer to significantly improve speed while still performing real testing.
The juneau-dto library contains several predefined POJOs for generating commonly-used document types that are designed to be used with the Juneau Marshaller APIs for both serializing and parsing.
The Juneau HTML5 DTOs are simply beans with fluent-style setters that allow you to quickly construct HTML fragments as Java objects. These object can then be serialized to HTML using one of the existing HTML serializers, or to other languages such as JSON using the JSON serializers.
The {@link org.apache.juneau.dto.html5.HtmlBuilder} class is a utility class with predefined static methods that allow you to easily construct DTO instances in a minimal amount of code.
The Juneau ATOM feed DTOs are simply beans with fluent-style setters. The following code shows a feed being created programmatically using the {@link org.apache.juneau.dto.atom.AtomBuilder} class.
The Juneau Swagger DTOs are simply beans with fluent-style setters that allow you to quickly construct Swagger documents as Java objects. These object can then be serialized to JSON using one of the existing JSON serializers, or to other languages such as XML or HTML using the other serializers.
The {@link org.apache.juneau.dto.swagger.ui.SwaggerUI} class is a DTO class for generating Swagger user interfaces from {@link org.apache.juneau.dto.swagger.Swagger} beans.
The
Using {@link org.apache.juneau.dto.swagger.ui.SwaggerUI}, we're able to render that JSON as a Swagger user interface when the request is asking for HTML:
The juneau-config library contains a powerful API for creating and using INI-style config files.
Config files are accessed through the {@link org.apache.juneau.config.Config} class which
are created through the {@link org.apache.juneau.config.Config.Builder} class.
Builder creator methods are provided on the
The config language may look simple but it is a very powerful feature with many capabilities.
The juneau-assertions module in Juneau is a powerful API for performing fluent style assertions.
Fluent assertions have two types of methods:
Multiple transformations and assertions can be performed per statement.
The Assertions APIs are used throughout the REST client and server APIs for performing inline assertions on REST requests and responses.
The Juneau framework uses the design pattern of builders, context, and session objects:
This is a general design pattern used throughout the framework including the REST client and server APIs.
The following shows the general pattern for creating sessions:
Typically developers will not deal with session objects and will just use convenience methods on the context classes themselves that handle creation of sessions:
Most context objects also have static default instances that can be used in leu of creating new contexts as well:
Most context classes also have the ability to clone and modify existing context objects:
The default values of many context settings can also be set via system properties and environment variables. The javadocs on these settings will identify when this is possible.
The framework makes heavy use of caching of existing context objects with the same builder settings. This is a critical reason why Juneau achieve impressive performance. Using Java reflection to find out all information about a bean type is expensive. By caching context objects, we only need to reflect that bean type once and store that information in the context for reuse by all serializers and parsers that share the same bean context configuration settings.
juneau-marshall-
org.apache.juneau.marshall_
The
{@link org.apache.juneau.marshaller.Marshaller Marshallers} are simple pairings of a {@link org.apache.juneau.serializer.Serializer} and {@link org.apache.juneau.parser.Parser} with convenience methods for serializing and parsing POJOs.
Juneau comes with the following predefined marshallers:
Each predefined marshaller also includes static convenience from/to methods to make it even easier to perform marshalling on POJOs:
One of the goals of Juneau was to make serialization as simple as possible. In a single line of code, you should be able to serialize and parse most POJOs. Despite this simplicity, Juneau provides lots of extensibility and configuration properties for tailoring how POJOs are serialized and parsed.
The built-in serializers in Juneau are fast, efficient, and highly configurable. They work by serializing POJOs directly to streams instead of using intermediate Document Object Model objects.
In most cases, you can serialize objects in one line of code by using one of the default serializers:
In addition to the default serializers, customized serializers can be created using various built-in options:
Default serialization support is provided for Java primitives,
Parsers work by parsing input directly into POJOs instead of having to create intermediate Document Object Models. This allows them to parse input with minimal object creation.
Like the serializers, you can often parse objects in one line of code by using one of the default parsers:
The parsers can also be used to populating existing bean and collection objects:
At the heart of the marshalling APIs is the {@link org.apache.juneau.BeanContext Bean Context} API that provides a common framework for marshalling beans and POJOs across all serializers and parsers. All serializers and parsers (and their builders) extend from the bean context API classes.
One important feature of the bean context API is the ability to wrap Java beans inside maps to allow properties to be accessed through a Map layer. Although this is used internally by all the serializers and parsers, it's often useful to use this feature by itself.
The bean context API provides many settings that fine-tune how POJOs should be handled during marshalling.
Out-of-the-box, Juneau supports marshalling of Java beans with standard public getters and setters, public
fields, and fluent setters (e.g.
Several settings exist to allow you to customize how bean properties are handled by serializers and parsers:
Settings and equivalent annotations are also available to control which properties are marshalled and how they are ordered.
It's common to use the {@link org.apache.juneau.annotation.Bean#properties @Bean(properties|p)} annotation to force the ordering of properties during marshalling. IBM JVMs keep the ordering of fields and methods in the compiled bytecodebut Oracle JVMs do not and return fields/methods in random order. The {@link org.apache.juneau.annotation.Bean#properties @Bean(properties|p)} annotation was added to help with this limitation.
The {@link org.apache.juneau.annotation.Bean @Bean} annotation is used to tailor how beans are interpreted by the framework.
Bean property inclusion and ordering on a bean class can be done using the {@link org.apache.juneau.annotation.Bean#properties() @Bean(properties|p)} annotation.
Bean properties can be excluded using the {@link org.apache.juneau.annotation.Bean#excludeProperties() @Bean(excludeProperties|xp)} annotation.
Bean properties can be sorted alphabetically using {@link org.apache.juneau.annotation.Bean#sort() @Bean(sort)}
The {@link org.apache.juneau.annotation.Bean#propertyNamer() @Bean(propertyNamer)} annotation is used to provide customized naming of properties.
Property namers are used to transform bean property names from standard form to some other form. For example, the {@link org.apache.juneau.PropertyNamerDLC} will convert property names to dashed-lowercase, and these will be used as attribute names in JSON and element names in XML.
The {@link org.apache.juneau.annotation.Bean#interfaceClass @Bean(interfaceClass)} annotation is used to limit properties on beans to specific interface classes. When specified, only the list of properties defined on the interface class will be used during serialization. Additional properties on subclasses will be ignored.
Note that this annotation can be used on the parent class so that it filters to all child classes. Or can be set individually on the child classes.
The {@link org.apache.juneau.annotation.Bean#stopClass @Bean(stopClass)} annotation is another way to limit which properties are serialized (except from the opposite direction). It's identical in purpose to the stop class specified by {@link java.beans.Introspector#getBeanInfo(Class, Class)}. Any properties in the stop class or in its base classes will be ignored during analysis.
For example, in the following class hierarchy, instances of
The {@link org.apache.juneau.annotation.Bean#interceptor() @Bean(interceptor)} annotation and {@link org.apache.juneau.swap.BeanInterceptor} class can be used to perform interception and inline handling of bean getter and setter calls.
The {@link org.apache.juneau.annotation.Bean#on() @Bean(on)} and {@link org.apache.juneau.annotation.Bean#onClass() @Bean(onClass)}
annotations can be used to programmatically attach
The {@link org.apache.juneau.annotation.Beanp @Beanp} annotation is used to tailor how individual bean properties are interpreted by the framework.
The {@link org.apache.juneau.annotation.Beanp#name() @Beanp(name)} annotation is used to override the name of the bean property.
The {@link org.apache.juneau.annotation.Name @Name} annotation is a shortcut for specifying a bean property name:
If the {@link org.apache.juneau.BeanContext.Builder#beanFieldVisibility(Visibility) beanFieldVisibility} setting on the bean context excludes this field (e.g. the visibility is set to the default of PUBLIC but the field is PROTECTED), this annotation can be used to force the field to be identified as a property.
The bean property named
The following shows various ways of using dynamic bean properties.
Similar rules apply for value types and swaps. The property values optionally can be any serializable type or use swaps.
The {@link org.apache.juneau.annotation.Beanp#value() @Beanp(value)} annotation is a synonym for {@link org.apache.juneau.annotation.Beanp#name() @Beanp(name)}. Use it in cases where you're only specifying a name so that you can shorten your annotation.
The following annotations are equivalent:
The {@link org.apache.juneau.annotation.Beanp#type() @Beanp(type)} annotation is used to identify a specialized class type for a generalized property. Normally the type is inferred through reflection of the field type or getter return type. However, you'll want to specify this value if you're parsing beans where the bean property class is an interface or abstract class to identify the bean type to instantiate. Otherwise, you may cause an {@link java.lang.InstantiationException} when trying to set these fields.
This property must denote a concrete class with a no-arg constructor.
The {@link org.apache.juneau.annotation.Beanp#params() @Beanp(params)} annotation is for bean properties of type map or collection. It's used to identify the class types of the contents of the bean property object when the general parameter types are interfaces or abstract classes.
The {@link org.apache.juneau.annotation.Beanp#properties() @Beanp(properties)} annotation is used to limit which child properties are rendered by the serializers. It can be used on any of the following bean property types:
The {@link org.apache.juneau.annotation.Beanp#format() @Beanp(format)} annotation specifies a String format for converting a bean property value to a formatted string.
The {@link org.apache.juneau.annotation.Beanc @Beanc} annotation is used to map constructor arguments to property names on bean with read-only properties. Since method parameter names are lost during compilation, this annotation essentially redefines them so that they are available at runtime.
The definition of a read-only bean is a bean with properties with only getters, like shown below:
Beans can also be defined with a combination of read-only and read-write properties.
The {@link org.apache.juneau.annotation.Name @Name} annotation can also be used instead of
If neither
The {@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore} annotation is used to ignore classes, fields, and methods from being interpreted as beans or bean components.
When applied to classes, objects will be converted to strings even though they look like beans.
When applied to fields and getters/setters, they will be ignored as bean properties.
The {@link org.apache.juneau.annotation.NameProperty @NameProperty} annotation is used to identify a setter as a method for setting the name of a POJO as it's known by its parent object.
A commonly-used case is when you're parsing a JSON map containing beans where one of the bean properties is the key used in the map.
The {@link org.apache.juneau.annotation.ParentProperty @ParentProperty} annotation is used to identify a setter as a method for adding a parent reference to a child object.
A commonly-used case is when you're parsing beans and a child bean has a reference to a parent bean.
Parsers will automatically set this field for you in the child beans.
Juneau parsers can use builders to instantiate POJOs. This is useful in cases where you want to create beans with read-only properties. Note that while it's possible to do this using the {@link org.apache.juneau.annotation.Beanc @Beanc} annotation, using builders can often be cleaner.
A typical builder usage is shown below:
MyBean
The typical code for such a builder using a static inner class is shown below:
The POJO class can be any type including beans. Builders MUST be beans with one or more writable properties. The bean properties themselves do not need to be readable (i.e. getters are optional).
Builders require two parts:
The first can be accomplished through any of the following:
The second can be accomplished through any of the following:
Juneau serializers treat instances of
Note that if you're serializing Readers and InputStreams, it's up to you to make sure you're producing valid output (in this case JSON).
A more typical scenario where this is useful is by using swaps to convert POJOs to Readers whose contents are determined via the {@link org.apache.juneau.BeanSession#getMediaType()} method. In the following example, we're customizing the JSON output for a particular bean type but leaving all other renditions as-is:
There is a separate set of serializers and parsers for marshalling HTTP parts (query, form-data, headers, path variables, and plain-text request bodies). The distinction is that these are designed to marshall directly to-and-from strings based on Open-API schema information.
The {@link org.apache.juneau.httppart.HttpPartSchema} class also provides convenience static methods for creation of custom schemas. The equivalent to the schema above can be structured like so:
The class hierarchy for the part marshallers are:
Serializers and parsers have a wide variety of configurable settings. Their builders all extend from the {@link org.apache.juneau.BeanContext.Builder} class that allows you to easily construct new instances from scratch or build upon existing instances. For example, the following code shows how to configure a JSON serializer:
WriterSerializer
However, each of the serializers and parsers already contain reusable instances with common configurations. For example, JSON has the following predefined reusable serializers and parsers:
These can be used directly, as follows:
For performance reasons, serializers and parsers are immutable.
However, they can be 'copied' and modified using the
Default values for configurable settings can be set globally using either system properties or environment variables.
For example, the default
All configurable properties described in the previous section have annotation equivalents that can be applied on classes or methods.
In the section on the REST server API, we describe how to configure serializers and parsers using
Config annotations defined on classes and methods can be applied to serializers and parsers using the following methods:
The following example shows how annotations defined on a dummy class can be applied to a serializer:
Config annotations are provided for all serializers and parsers:
Annotations normally applied to bean classes/methods/fields/parameters
can also be programmatically attatched to beans by using the
Annotations can also be applied directly to serializers and parsers using the following method:
The following example shows a concrete implementation of an annotation can be applied to a serializer:
Concrete annotation implementations are provided for all annotations.
Any number of matching config or concrete annotations can be applied. They are applied in the order they are provided to the context. Therefore any values can be overridden. Config and concrete annotations also override any class or method level annotations
The {@link org.apache.juneau.collections.JsonMap} and {@link org.apache.juneau.collections.JsonList} classes are generic Java representations of JSON objects and arrays. These classes can be used to create "unstructured" models for serialization (as opposed to "structured" models consisting of beans). If you want to quickly generate JSON/XML/HTML from generic maps/collections, or parse JSON/XML/HTML into generic maps/collections, these classes work well.
These classes extend directly from the following JCF classes:
The
These object can be serialized in one of two ways:
Any valid JSON can be parsed into an unstructured model consisting of generic {@link org.apache.juneau.collections.JsonMap} and {@link org.apache.juneau.collections.JsonList} objects. (Any valid XML can also be parsed into an unstructured model)
The
The Juneau parsers have the ability to parse into complex data types that consist of multidimensional arrays and nested maps and collections using the methods below:
Arrays are simple enough and can be constructed using the first method:
String
For data types consisting of nested collections an maps such as
String
The arguments can be arbitrarily long to indicate arbitrarily complex data structures.
Similar methods for converting to complex types can be found on the {@link org.apache.juneau.rest.httppart.RequestContent} and {@link org.apache.juneau.rest.httppart.RequestHttpPart} classes, and the {@link org.apache.juneau.BeanSession#convertToType(Object,Type,Type...)} method.
On top of the serializers and parsers are the {@link org.apache.juneau.serializer.SerializerSet} and
{@link org.apache.juneau.parser.ParserSet} classes.
These classes allow serializers and parsers to be grouped and retrieved by W3C-compliant HTTP
The REST servlet API builds upon the
{@link org.apache.juneau.swap.ObjectSwap Swaps} are a critical component of Juneau. They allow the serializers and parsers to handle Java objects that wouldn't normally be serializable.
Swaps are, simply put, 'object swappers' that swap in serializable objects for
non-serializable ones during serialization, and vis-versa during parsing.
Some examples of non-serializable objects are
In the following example, we introduce a
The swap can then be associated with serializers and parsers like so:
Another example of a
The following example shows the BASE64 swap in use:
The {@link org.apache.juneau.BeanContextable.Builder#swap(Class,Class,ThrowingFunction)} and {@link org.apache.juneau.BeanContextable.Builder#swap(Class,Class,ThrowingFunction,ThrowingFunction)} methods are another way to define swaps by using functions.
By default, all serializers and parsers have built in
Various other swaps are provided in the {@link org.apache.juneau.swaps} package.
Various methods can be defined on a class directly to affect how it gets serialized.
This can often be simpler than using
Objects serialized as
Note that these methods cover conversion from several built-in Java types, meaning the parsers can automatically construct these objects from strings:
If you want to force a bean-like class to be serialized as a string, you can use the
{@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore} annotation on the class to force it to be
serialized to a string using the
Serializing to other intermediate objects can be accomplished by defining a swap method directly on the class:
Serializing to and from Maps can be accomplished by defining any of the following methods:
The
The following example shows how an HTML5 form template object can be created that gets serialized as a populated HTML5 {@link org.apache.juneau.dto.html5.Form} bean.
Swapped objects can be converted back into their original form by the parsers by specifying one of the following methods:
The following shows how our form template class can be modified to allow the parsers to reconstruct our original object:
Swaps can also be defined per-media-type.
The {@link org.apache.juneau.swap.ObjectSwap#forMediaTypes()} method can be overridden to provide a set of media types that the swap is invoked on. It's also possible to define multiple swaps against the same object as long as they're differentiated by media type. When multiple swaps are defined, the best-match media type is used.
In the following example, we define 3 swaps against the same object. One for JSON, one for XML, and one for all other types.
When multiple swaps match the same media type, a best-match algorithm is applied to find the correct swap to use.
In later sections we describe how annotations can be used to shorten this syntax:
In the previous sections, we defined two-way swaps, meaning swaps where the original objects could be reconstructing during parsing. However, there are certain kinds of objects that we may want to support for serializing but that are not possible to reconstruct during parsing. For these, we can use one-way object swaps.
A one-way swap is simply an object transform that only implements the {@code swap()} method. The {@code unswap()} method is simply left unimplemented.
An example of a one-way swaps would be one that allows {@code Iterators} to be serialized as JSON arrays. It can make sense to be able to render {@code Iterators} as arrays but in general it's not possible to reconstruct an {@code Iterator} during parsing.
Here is an example of our one-way swap being used. Note that trying to parse the original object will cause a {@link org.apache.juneau.parser.ParseException} to be thrown.
{@link org.apache.juneau.annotation.Swap @Swap} can be used to associate a swap class using an
annotation.
This is often cleaner than using the builder
Multiple swaps can be associated with a class using multiple {@link org.apache.juneau.annotation.Swap @Swap} annotations:
The
When applied to bean properties, the swap annotation need only be applied to either the getter, setter, or field.
The swap annotation can also be applied to the private field of a bean property, like so:
The {@link org.apache.juneau.annotation.Swap#template() @Swap(template)} annotation allows you to associate arbitrary contextual strings with swaps. The primary purpose is for providing template names, such as for Apache FreeMarker, therefore the name 'template'. However, the usage of the string is open-ended.
For example, you could pair a template string like so:
The implementation of the FreeMarker swap would look something like this:
Surrogate classes are very similar in concept to
For example, let's say we want to be able to serialize the following class but it's not serializable for some reason (for example, there are no properties exposed):
This could be solved with the following
However, the same can be accomplished by using a surrogate class that simply contains a constructor with the non-serializable class as an argument:
The surrogate class is registered in the same way as a
When the serializer encounters the non-serializable class, it will serialize an instance of the surrogate instead.
In the section Swaps, you were introduced to annotations that can be applied to bean classes, methods, fields, and constructors such as {@link org.apache.juneau.annotation.Bean @Bean}:
An alternate way of applying these annotations is to attach them to unrelated classes and methods and then tell your serializer or parser where to find them.
The advantage to this approach is it allows you to use Juneau annotations on classes/methods/fields/constructors where you might not have access to the source code, or when you only want to selectively apply the annotation under certain scenarios instead of globally.
For example, the following shows the
Any Juneau annotation that has an
The valid pattern matches are:
While parsing into beans, Juneau attempts to determine the class types of bean properties through reflection on the bean property getter or setter. Often this is insufficient if the property type is an interface or abstract class that cannot be instantiated. This is where bean names and dictionaries come into play.
Bean names and dictionaries are used for identifying class types when they cannot be inferred through reflection.
Bean classes are given names through the {@link org.apache.juneau.annotation.Bean#typeName() @Bean(typeName)}
annotation.
These names are then added to the serialized output as virtual
On the parsing side, these type names are resolved to classes through the use of bean dictionaries.
For example, if a bean property is of type
When serialized as JSON,
{
x: [
{_type:
Type names can be represented slightly differently in different languages.
For example, the dictionary name is used as element names when serialized to XML.
This allows the
When serialized as XML, the bean is rendered as:
Bean dictionaries are registered through the following:
The bean dictionary setting can consist of any of the following types:
The
When using the annotation, you'll typically want to define it on an interface class so that it can be inherited by all subclasses.
In addition to the bean type name support described above, simplified support is provided for bean subtypes.
Bean subtypes are similar in concept to bean type names, except for the following differences:
In the following example, the abstract class has two subclasses:
When serialized, the subtype is serialized as a virtual
A1
The following shows what happens when parsing back into the original object.
A
The {@link org.apache.juneau.BeanContext.Builder#disableInterfaceProxies()} setting (enabled by default) allows the Juneau parsers to parse content into virtual beans (bean interfaces without implementation classes).
For example, the following code creates an instance of the specified unimplemented interface:
Getter and setter values can be any parsable values, even other virtual beans.
Under-the-covers, a virtual bean is simply a proxy interface on top of an existing {@link org.apache.juneau.BeanMap} instance. From a programmatic point-of-view, they're indistinguishable from real beans, and can be manipulated and serialized like any other bean.
Virtual beans can also be created programmatically using the {@link org.apache.juneau.BeanContext} class:
Address
The Juneau Serializer API is designed to be used against POJO tree structures.
It expects that there not be loops in the POJO model (e.g. children with references to parents, etc...).
If you try to serialize models with loops, you will usually cause a
If you still want to use the Juneau serializers on such models, Juneau provides the {@link org.apache.juneau.BeanTraverseContext.Builder#detectRecursions()} setting. It tells the serializer to look for instances of an object in the current branch of the tree and skip serialization when a duplicate is encountered.
For example, let's make a POJO model out of the following classes:
Now we create a model with a loop and serialize the results.
What we end up with is the following, which does not serialize the contents of the
{
Without recursion detection enabled, this would cause a stack-overflow error.
Recursion detection introduces a performance penalty of around 20%. For this reason the setting is disabled by default.
The Juneau parsers are not limited to parsing back into the original bean classes.
If the bean classes are not available on the parsing side, the parser can also be used to
parse into a generic model consisting of
You can parse into any
When the map or list type is not specified, or is the abstract
For example, given the following JSON:
{
id:
We can parse this into a generic
What we end up with is the exact same output.
Even the numbers and booleans are preserved because they are parsed into
{
id:
Once parsed into a generic model, various convenience methods are provided on the
As a general rule, parsing into beans is often more efficient than parsing into generic models. And working with beans is often less error prone than working with generic models.
The following parsers can be configured to read continuous streams of objects from the same input stream:
The {@link org.apache.juneau.json.JsonParser} and {@link org.apache.juneau.uon.UonParser} classes can read continuous streams by using the {@link org.apache.juneau.parser.Parser.Builder#unbuffered()} setting. This prevents the parsers from using an internal buffer that would read past the end of the currently parsed POJO.
Note that this isn't perfect in all cases since you can't combine two JSON numbers into a single
reader (e.g.
For obvious reasons, do not use the following properties when reading continuous streams:
The {@link org.apache.juneau.msgpack.MsgPackParser} class doesn't use any internal buffering to begin with, so it can be used with continuous streams without any special properties.
Juneau serializers have sophisticated support for transforming relative URIs to absolute form.
The following example shows a bean containing URIs of various forms and how they end up serialized.
URI resolution is controlled by the following settings:
Juneau automatically interprets any {@link java.net.URL} and {@link java.net.URI} objects as URIs and will resolve them accordingly. The {@link org.apache.juneau.annotation.Uri @Uri} annotation can be used to extend that to other bean properties and class types so that they also get interpreted as URIs. For example:
Juneau was developed independently from Jackson but shares many of the same features and capabilities. Whereas Jackson was created to work primarily with JSON, Juneau was created to work for multiple languages. Therefore, the terminology and annotations in Juneau are similar but language-agnostic.
The following charts describe equivalent features between the two libraries:
Jackson | Juneau |
---|---|
|
{@link org.apache.juneau.annotation.Beanp @Beanp} |
|
{@link org.apache.juneau.annotation.Beanp#name() @Beanp(name="*")} |
|
{@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore} |
{@link org.apache.juneau.annotation.Bean#excludeProperties @Bean(excludeProperties|xp)} | |
No equivalent annotation but can be controlled via:
{@link org.apache.juneau.BeanContext.Builder#beanFieldVisibility(Visibility)} {@link org.apache.juneau.BeanContext.Builder#beanMethodVisibility(Visibility)} |
|
|
{@link org.apache.juneau.annotation.Beanc @Beanc} |
No equivalent. | |
|
Juneau uses swaps to convert non-serializable object to serializable forms:
{@link org.apache.juneau.annotation.Swap @Swap} |
No equivalent annotation but can be controlled via various settings:
{@link org.apache.juneau.BeanContext} {@link org.apache.juneau.serializer.Serializer} |
|
{@link org.apache.juneau.annotation.Bean#properties @Bean(properties="...")}
{@link org.apache.juneau.annotation.Bean#sort @Bean(sort=x)} |
|
|
Can be replicated using swaps with |
In general, Juneau allows for marshalling for a wide variety of POJO types including:
The following chart shows POJOs categorized into groups and whether they can be serialized or parsed:
Group | Description | Examples | Can serialize? | Can parse? |
---|---|---|---|---|
1 | Java primitives and primitive objects |
|
yes | yes |
2 | Java Collections Framework objects, Java arrays, Java Optionals | |||
2a |
With standard keys/values
Map keys are group [1, 4a, 6a] objects. Map, Collection, Optional, and array values are group [1, 2, 3ac, 4a, 6a] objects. |
|
yes | yes |
2b |
With non-standard keys/values
Map keys are group [2, 3, 4b, 5, 6b, 7] objects. Map, Collection, and array values are group [3b, 4b, 5, 6b, 7] objects. |
|
yes | no |
3 | Java Beans | |||
3a |
With standard properties
These are beans that have one or more properties defined by public getter or public fields. Properties can also be defined as final read-only fields and passed in as constructor args. Property values are group [1, 2, 3ac, 4a, 6a] objects. |
yes | yes | |
3b |
With non-standard properties or not true beans
These include true beans that have one or more properties defined by getter and setter methods or properties but property types include group [3b, 4b, 5, 6b, 7] objects. This also includes classes that look like beans but aren't true beans. For example, classes that have getters but not setters, or classes without no-arg constructors. |
yes | no | |
3c |
Virtual beans
These are unimplemented bean interfaces with properties of type [1, 2, 3ac, 4a, 6a] objects. Parsers will automatically create interface proxies on top of BeanMap instances. |
yes | yes | |
3d |
Read-only beans without setters
The same as 3a but without property setters or constructor args. |
yes | no | |
4 |
Swapped objects
These are objects that are not directly serializable but have {@link org.apache.juneau.swap.ObjectSwap ObjectSwaps} associated with them. The purpose of a POJO swap is to convert an object to another object that is easier to serialize and parse. For example, the {@link org.apache.juneau.swaps.TemporalDateSwap.IsoLocalDateTime} class can be used to serialize {@link java.util.Date} objects to ISO8601 strings, and parse them back into {@link java.util.Date} objects. |
|||
4a |
2-way swapped to group [1, 2a, 3ac] objects
For example, a swap that converts a {@code Date} to a {@code String}. |
|
yes | yes |
4b |
1-way swapped to group [1, 2, 3] objects
For example, a swap that converts an {@code Iterator} to a {@code List}. This would be one way, since you cannot reconstruct an {@code Iterator}. |
|
yes | no |
5 |
Readers and InputStreams
Contents are serialized directly to the output stream or writer. Typically used for low-level language-specific replacement of POJOs using per-Media-Type POJO swaps. |
|
yes | no |
6 |
Non-serializable objects with standard methods for converting to a serializable form |
|||
6a |
Classes with a method that converts it to a serializable form:
|
|
yes | yes |
6b |
Classes that only have a method to convert to a serializable form:
|
yes | no | |
7 |
All other objects
Anything that doesn't fall into one of the groups above are simply converted to {@code Strings} using the {@code toString()} method. |
yes | no |
A separate category exists for POJOs that can be converted to and from Strings. These are used in places such as:
As a general rule, all POJOs are converted to Strings using the
POJOs are convertible from Strings using any of the following (matched in the specified order):
Exceptions exist for the following classes:
POJOs are also converted to various other types in places such as the Open-API serializers and parsers.
In this section, the type being converted to will be referred to as
POJOs are considered convertible from X if it has any of the following (matched in the specified order):
POJOs are considered convertible from X if any of the reverse of above are true.
The {@link org.apache.juneau.svl} packages defines an API for a language called "Simple Variable Language".
In a nutshell, Simple Variable Language (or SVL) is text that contains variables of the form
Most variables can be recursively nested within the varKey (e.g.
The {@link org.apache.juneau.svl.VarResolver} class is used to resolve variables. The {@link org.apache.juneau.svl.VarResolver#DEFAULT} resolver is a reusable instance of this class configured with the following basic variables:
The following logic variables are also provided:
The following shows how variables can be arbitrarily nested...
Variables are defined through the {@link org.apache.juneau.svl.Var} API. The API comes with several predefined variables and is easily extensible.
The following is an example of a variable that performs URL-Encoding on strings.
The following shows the class hierarchy of the {@link org.apache.juneau.svl.Var} class:
The following is the list of default variables defined in all modules:
Module | Class | Pattern |
---|---|---|
juneau-svl | {@link org.apache.juneau.svl.vars.EnvVariablesVar} | $E{key[,default]} |
{@link org.apache.juneau.svl.vars.SystemPropertiesVar} | $S{key[,default]} | |
{@link org.apache.juneau.svl.vars.ArgsVar} | $A{key[,default]} | |
{@link org.apache.juneau.svl.vars.ManifestFileVar} | $MF{key[,default]} | |
{@link org.apache.juneau.svl.vars.IfVar} | $IF{arg,then[,else]} | |
{@link org.apache.juneau.svl.vars.SwitchVar} | $SW{arg,pattern1:then1[,pattern2:then2...]} | |
{@link org.apache.juneau.svl.vars.CoalesceVar} | $CO{arg1[,arg2...]} | |
{@link org.apache.juneau.svl.vars.PatternMatchVar} | $PM{arg,pattern} | |
{@link org.apache.juneau.svl.vars.PatternReplaceVar} | $PR{arg,pattern,replace} | |
{@link org.apache.juneau.svl.vars.PatternExtractVar} | $PE{arg,pattern,groupdIndex} | |
{@link org.apache.juneau.svl.vars.NotEmptyVar} | $NE{arg} | |
{@link org.apache.juneau.svl.vars.UpperCaseVar} | $UC{arg} | |
{@link org.apache.juneau.svl.vars.LowerCaseVar} | $LC{arg} | |
{@link org.apache.juneau.svl.vars.LenVar} | $LN{arg[,delimiter]} | |
{@link org.apache.juneau.svl.vars.SubstringVar} | $ST{arg,start[,end]} | |
{@link org.apache.juneau.html.HtmlWidgetVar} | $W{name} | |
juneau-config | {@link org.apache.juneau.config.vars.ConfigVar} | $C{key[,default]} |
juneau-rest-server | {@link org.apache.juneau.rest.vars.FileVar} | $F{path[,default]}} |
{@link org.apache.juneau.rest.vars.ServletInitParamVar} | $I{name[,default]} | |
{@link org.apache.juneau.rest.vars.LocalizationVar} | $L{key[,args...]} | |
{@link org.apache.juneau.rest.vars.RequestAttributeVar} | $RA{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestFormDataVar} | $RF{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestHeaderVar} | $RH{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestPathVar} | $RP{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestQueryVar} | $RQ{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestSwaggerVar} | $RS{key} | |
{@link org.apache.juneau.rest.vars.RequestVar} | $R{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.SerializedRequestAttrVar} | $SA{contentType,key[,default]} | |
{@link org.apache.juneau.rest.vars.SwaggerVar} | $SS{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.UrlVar} | $U{uri} | |
{@link org.apache.juneau.rest.vars.UrlEncodeVar} | $UE{uriPart} |
The main class for performing variable resolution is {@link org.apache.juneau.svl.VarResolver}. Two methods are provided for resolving variables:
Var resolvers can rely on the existence of other objects. For example, {@link org.apache.juneau.config.vars.ConfigVar} relies on the existence of a {@link org.apache.juneau.config.Config}. This is accomplished through the following method:
Beans are accessible through the following method:
Var resolvers can be cloned and extended by using the {@link org.apache.juneau.svl.VarResolver#copy()} method. Cloning a resolver will copy it's {@link org.apache.juneau.svl.Var} class names and context objects.
{@link org.apache.juneau.svl.VarResolver#DEFAULT} is a reusable variable resolver with default support for the following variables:
The {@link org.apache.juneau.encoders} package defines an API for handling encoding-based matching
of
The {@link org.apache.juneau.encoders.EncoderSet} class represents the set of {@link org.apache.juneau.encoders.Encoder encoders} keyed by codings.
It maintains a set of encoders and the codings that they can handle.
The {@link org.apache.juneau.encoders.EncoderSet#getEncoderMatch(String) getEncoderMatch(String)} and {@link org.apache.juneau.encoders.EncoderSet#getEncoder(String)}
methods are then used to find appropriate encoders for specific
Encoders are tried in the order they appear in the set. The {@link org.apache.juneau.encoders.EncoderSet.Builder#add(Class...)} / {@link org.apache.juneau.encoders.EncoderSet.Builder#add(Encoder...)} methods prepend the values to the list to allow them the opportunity to override encoders already in the list.
For example, calling builder.add(E1.
will result in the order
The {@link org.apache.juneau.encoders.Encoder} interface is used for enabling decompression on requests and compression on responses, such as support for GZIP compression. It is used to wrap input and output streams within compression/decompression streams.
Encoders are registered with
The {@link org.apache.juneau.objecttools} package defines convenience utility classes for accessing and manipulating POJOs. It consists of the following classes:
The {@link org.apache.juneau.objecttools.ObjectRest} class provides the ability to perform standard REST operations (GET, PUT, POST, DELETE) against nodes in a POJO model. Nodes in the POJO model are addressed using URLs.
A POJO model is defined as a tree model where nodes consist of consisting of the following:
Leaves of the tree can be any type of object.
Use {@link org.apache.juneau.objecttools.ObjectRest#get(String) get()} to retrieve an element from a JSON tree.
Use {@link org.apache.juneau.objecttools.ObjectRest#put(String,Object) put()} to create (or overwrite) an element in a JSON tree.
Use {@link org.apache.juneau.objecttools.ObjectRest#post(String,Object) post()} to add an element to a list in a JSON tree.
Use {@link org.apache.juneau.objecttools.ObjectRest#delete(String) delete()} to remove an element from a JSON tree.
In the special case of collections/arrays of maps/beans, a special XPath-like selector notation can be used in lieu
of index numbers on GET requests to return a map/bean with a specified attribute value.
The syntax is {@code @attr=val}, where attr is the attribute name on the child map, and val is the matching value.
The {@link org.apache.juneau.objecttools.ObjectSearcher} class is designed to provide searches across arrays and collections of maps or beans. It allows you to quickly filter beans and maps using simple yet sophisticated search arguments.
MyBean[]
The tool can be used against the following data types:
The default searcher is configured with the following matcher factories that provides the capabilities of matching against various data types. This list is extensible:
The {@link org.apache.juneau.objecttools.StringMatcherFactory} class provides searching based on the following patterns:
The {@link org.apache.juneau.objecttools.NumberMatcherFactory} class provides searching based on the following patterns:
The {@link org.apache.juneau.objecttools.TimeMatcherFactory} class provides searching based on the following patterns:
The {@link org.apache.juneau.objecttools.ObjectSorter} class is designed to sort arrays and collections of maps or beans.
MyBean[]
The tool can be used against the following data types:
The arguments are a simple comma-delimited list of property names optionally suffixed with
The {@link org.apache.juneau.objecttools.ObjectViewer} class is designed to extract properties from collections of maps or beans.
MyBean[]
The tool can be used against the following data types:
The {@link org.apache.juneau.objecttools.ObjectPaginator} class is designed to extract sublists from arrays/collections of maps or beans.
MyBean[]
The tool can be used against the following data types:
The {@link org.apache.juneau.objecttools.ObjectIntrospector} class is used to invoke methods on {@code Objects} using arguments in serialized form.
String
The arguments passed to the identified method are POJOs serialized in JSON format. Arbitrarily complex arguments can be passed in as arguments.
The {@link org.apache.juneau.objecttools.ObjectMerger} class is used for merging POJOs behind a single interface. This is particularly useful in cases where you want to define beans with 'default' values.
For example, given the following bean classes:
The getters will be called in order until the first non-null value is returned:
Juneau supports converting arbitrary POJOs to and from JSON using ultra-efficient serializers and parsers. The JSON serializer converts POJOs directly to JSON without the need for intermediate DOM objects using a highly-efficient state machine. Likewise, the JSON parser creates POJOs directly from JSON without the need for intermediate DOM objects.
The following example shows JSON for a typical bean:
Person
{
{
name:
The JSON data type produced depends on the Java object type being serialized.
POJO type | JSON type | Example | Serialized form |
---|---|---|---|
String | String | ||
Number | Number | ||
Boolean | Boolean | ||
Null | Null | ||
Beans with properties of any type on this list | Object | ||
Maps with values of any type on this list | Object | ||
Collections and arrays of any type on this list | Array |
In addition, swaps can be used to convert non-serializable POJOs into serializable forms, such as converting
The {@link org.apache.juneau.json.JsonSerializer} class is used to serialize POJOs into JSON.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.json.Json5Serializer} class can be used to serialized POJOs into JSON 5 notation.
JSON 5 is similar to JSON except for the following:
The advantage to JSON 5 is you can represent it in a Java String in minimal form with minimal escaping. This is particularly useful in cases such as unit testing where you can easily validate POJOs by simplifying them to JSON 5 and do a simple string comparison.
WriterSerializer
The {@link org.apache.juneau.json.JsonParser} class is used to parse JSON into POJOs.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following pre-configured parsers are provided for convenience:
The JSON parser supports ALL valid JSON, including:
The {@link org.apache.juneau.json.annotation.Json @Json} annotation is used to override the behavior of {@link org.apache.juneau.json.JsonSerializer} on individual bean classes or properties.
The annotation can be applied to beans as well as other objects serialized to other types (e.g. strings).
The {@link org.apache.juneau.json.annotation.Json#wrapperAttr() @Json(wrapperAttr)} annotation can be used to wrap beans inside a JSON object with a specified attribute name.
The following shows the JSON representation with and without the annotation present:
Without annotation | With annotation |
---|---|
{
|
{
|
Juneau provides the {@link org.apache.juneau.json.JsonSchemaSerializer} class for generating JSON-Schema
documents that describe the output generated by the {@link org.apache.juneau.json.JsonSerializer} class.
This class shares the same properties as
The code for creating our POJO model and generating JSON-Schema is shown below:
{
Juneau supports converting arbitrary POJOs to and from XML using ultra-efficient serializers and parsers. The XML serializer converts POJOs directly to XML without the need for intermediate DOM objects. Likewise, the XML parser uses a STaX parser and creates POJOs directly without intermediate DOM objects.
Unlike frameworks such as JAXB, Juneau does not require POJO classes to be annotated to produce and consume XML. However, several XML annotations are provided for handling namespaces and fine-tuning the format of the XML produced.
The following example shows XML for a typical bean:
Person
Juneau produces JSON-equivalent XML, meaning any valid JSON document can be losslessly converted into an XML equivalent. In fact, all of the Juneau serializers and parsers are built upon this JSON-equivalence.
The following examples show how different data types are represented in XML. They mirror how the data structures are represented in JSON.
The representation of loose (not a direct bean property value) simple types are shown below:
Data type | JSON example | XML |
---|---|---|
string | ||
boolean | ||
integer | 123 | |
float | 1.23 | |
null |
Loose maps and beans use the element
Data type | JSON example | XML |
---|---|---|
Map<String,String> |
{
k1: |
|
Map<String,Number> |
{
k1: 123,
k2: 1.23,
k3: |
|
Map<String,Object> |
{
k1: |
Loose collections and arrays use the element
Data type | JSON example | XML |
---|---|---|
String[] |
[
|
|
Number[] |
[
123,
1.23,
|
|
Object[] |
[
|
|
String[][] |
[
[ |
|
|
[ 123 ] | |
|
[
|
|
List<String> |
[
|
|
List<Number> |
[
123,
1.23,
|
|
List<Object> |
[
|
Data type | JSON example | XML |
---|---|---|
|
{
|
Data type | JSON example | XML |
---|---|---|
|
{
|
The {@link org.apache.juneau.xml.XmlSerializer} class is used to serialize POJOs into XML.
The {@link org.apache.juneau.xml.XmlDocSerializer} class is the same but serializes a
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.xml.XmlParser} class is used to parse XML into POJOs.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following pre-configured parsers are provided for convenience:
The {@link org.apache.juneau.annotation.Bean#typeName() @Bean(typeName)} annotation can be used to override the Juneau default name on bean elements. Types names serve two distinct purposes:
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
On bean properties, a
In the following example, a type attribute is used on property 'b' but not property 'a' since
'b' is of type
Java | Without annotation | With annotation |
---|---|---|
|
Beans with type names are often used in conjunction with the {@link org.apache.juneau.annotation.Bean#dictionary() @Bean(dictionary)} and {@link org.apache.juneau.annotation.Beanp#dictionary() @Beanp(dictionary)} annotations so that the beans can be resolved at parse time. These annotations are not necessary during serialization but are needed during parsing in order to resolve the bean types.
The following examples show how type names are used under various circumstances.
Pay special attention to when
Java | XML |
---|---|
|
|
|
|
|
Bean type names are also used for resolution when abstract fields are used. The following examples show how they are used in a variety of circumstances.
Java | XML |
---|---|
|
|
|
|
|
|
|
On a side note, characters that cannot be represented in XML 1.0 are encoded using a simple encoding.
Note in the examples below, some characters such as
Java | XML |
---|---|
|
|
|
While it's true that these characters CAN be represented in XML 1.1, it's impossible to parse XML 1.1 text in Java without the XML containing an XML declaration. Unfortunately, this, and the uselessness of the {@link javax.xml.stream.XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES} setting in Java forced us to make some hard design decisions that may not be the most elegant.
The {@link org.apache.juneau.xml.annotation.Xml#childName() @Xml(childName)} annotation can be used to specify the name of XML child elements for bean properties of type collection or array.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: [ |
||
|
{ a: [123,456] } |
The {@link org.apache.juneau.xml.annotation.Xml#format() @Xml(format)} annotation can be used to tweak the XML format of a POJO. The value is set to an enum value of type {@link org.apache.juneau.xml.annotation.XmlFormat}. This annotation can be applied to both classes and bean properties.
The {@link org.apache.juneau.xml.annotation.XmlFormat#ATTR} format can be applied to bean properties to serialize them as XML attributes instead of elements. Note that this only supports properties of simple types (e.g. strings, numbers, booleans).
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ATTRS} format can be applied to bean classes to force all bean properties to be serialized as XML attributes instead of child elements.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ELEMENT} format can be applied to bean properties to override the {@link org.apache.juneau.xml.annotation.XmlFormat#ATTRS} format applied on the bean class.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ATTRS} format can be applied to a single bean
property of type
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
|
The {@link org.apache.juneau.xml.annotation.XmlFormat#COLLAPSED} format can be applied to bean properties of type array/Collection. This causes the child objects to be serialized directly inside the bean element. This format must be used in conjunction with {@link org.apache.juneau.xml.annotation.Xml#childName() @Xml(childName)} to differentiate which collection the values came from if you plan on parsing the output back into beans. Note that child names must not conflict with other property names.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: [ |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ELEMENTS} format can be applied to a single bean property of either a simple type or array/Collection. It allows free-form child elements to be formed. All other properties on the bean MUST be serialized as attributes.
Data type | JSON example | With annotation |
---|---|---|
|
{
a: |
|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#MIXED} format is similar to {@link org.apache.juneau.xml.annotation.XmlFormat#ELEMENTS} except elements names on primitive types (string/number/boolean/null) are stripped from the output. This format particularly useful when combined with bean dictionaries to produce mixed content. The bean dictionary isn't used during serializationbut it is needed during parsing to resolve bean types.
The {@link org.apache.juneau.xml.annotation.XmlFormat#MIXED_PWS} format identical to {@link org.apache.juneau.xml.annotation.XmlFormat#MIXED} except whitespace characters are preserved in the output.
Data type | JSON example | Without annotations | With annotations |
---|---|---|---|
|
{
a: [
|
Whitespace (tabs and newlines) are not added to MIXED child nodes in readable-output mode. This helps ensures strings in the serialized output can be losslessly parsed back into their original forms when they contain whitespace characters. If the {@link javax.xml.stream.XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES} setting was not useless in Java, we could support lossless readable XML for MIXED content. But as of Java 8, it still does not work.
XML suffers from other deficiencies as well that affect MIXED content.
For example,
The examples below show how whitespace is handled under various circumstances:
Data type | XML |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It should be noted that when using
The {@link org.apache.juneau.xml.annotation.XmlFormat#TEXT} format is similar to
{@link org.apache.juneau.xml.annotation.XmlFormat#MIXED} except it's meant for solitary objects that
get serialized as simple child text nodes.
Any object that can be serialize to a
Data type | JSON example | Without annotations | With annotations |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#XMLTEXT} format is similar to
{@link org.apache.juneau.xml.annotation.XmlFormat#TEXT} except it's meant for strings containing XML
that should be serialized as-is to the document.
Any object that can be serialize to a
Data type | JSON example | With TEXT annotation | With XMLTEXT annotation |
---|---|---|---|
|
{
a: |
Let's go back to the example of our original
The namespace URLs can either be defined as part of the {@link org.apache.juneau.xml.annotation.Xml @Xml} annotation, or can be defined at the package level with the {@link org.apache.juneau.xml.annotation.XmlSchema @XmlSchema} annotation. Below shows it defined at the package level:
Person
Now when we run this code, we'll see namespaces added to our output:
Enabling the XmlSerializer.XML_addNamespaceUrisToRootsetting results
in the namespace URLs being added to the root node:
We can simplify the output by setting the default namespace on the serializer so that all the elements do not need to be prefixed:
This produces the following equivalent where the elements don't need prefixes since they're already in the default document namespace:
By default, the XML serializer class will make a first-pass over the data structure to look for namespaces defined on classes and bean properties. In high-performance environments, you may want to consider disabling auto-detection and providing your own explicit list of namespaces to the serializer to avoid this scanning step using {@link org.apache.juneau.xml.XmlSerializer.Builder#disableAutoDetectNamespaces()}.
The following code will produce the same output as before but will perform slightly better since it avoids this pre-scan step.
Juneau supports converting arbitrary POJOs to and from HTML. Built on top of the existing XML parser, it also uses a STaX parser and creates POJOs directly without intermediate DOM objects.
The primary use case for HTML serialization is rendering POJOs in easy-to-read format in REST interfaces.
The following examples show how different data types are represented in HTML. They mirror how the data structures are represented in JSON.
The representation for simple types mirror those produced by the XML serializer. Tags are added to help differentiate data types when they cannot be inferred through reflection. These tags are ignored by browsers and treated as plain text.
Data type | JSON example | HTML |
---|---|---|
string | ||
boolean | ||
integer | 123 | |
float | 1.23 | |
null |
Maps and beans are represented as tables.
The
Data type | JSON example | HTML |
---|---|---|
Map<String,String> |
{
k1: |
|
Map<String,Number> |
{
k1: 123,
k2: 1.23,
k3: |
|
Map<String,Object> |
{
k1: |
Collections and arrays are represented as ordered lists.
Data type | JSON example | HTML |
---|---|---|
String[] |
[
|
|
Number[] |
[
123,
1.23,
|
|
Object[] |
[
|
|
String[][] |
[
[ |
|
|
[ 123 ] | |
|
[
|
Data type | JSON example | HTML |
---|---|---|
List<String> |
[
|
|
List<Number> |
[
123,
1.23,
|
|
List<Object> |
[
|
Data type | JSON example | HTML |
---|---|---|
|
{
|
Data type | JSON example | HTML |
---|---|---|
|
{
|
The {@link org.apache.juneau.html.HtmlSerializer} class is used to serialize POJOs into HTML.
The {@link org.apache.juneau.html.HtmlDocSerializer} class is the same but wraps the serialized POJO inside a document template consisting of header, nav, aside, and footer sections.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.html.HtmlParser} class is used to parse HTML into POJOs. They can also parse the contents produced by {@link org.apache.juneau.html.HtmlDocSerializer}.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following pre-configured parsers are provided for convenience:
The {@link org.apache.juneau.html.annotation.Html @Html} annotation can be used to customize how POJOs are serialized to HTML on a per-class/field/method basis.
The {@link org.apache.juneau.html.annotation.Html#link @Html(link)} annotation adds a hyperlink to a bean property when rendered as HTML.
The {@link org.apache.juneau.html.annotation.Html#anchorText @Html(anchorText)} annotation is used to specify the anchor text of a hyperlink.
The {@link org.apache.juneau.html.annotation.Html#format @Html(format)} annotation is used to specify what format to use for HTML elements.
For example, the HTML beans defined in the {@link org.apache.juneau.dto.html5} package use
The {@link org.apache.juneau.html.annotation.Html#noTableHeaders @Html(noTableHeaders)} annotation is used to prevent beans from being serialized with table headers.
The {@link org.apache.juneau.html.annotation.Html#noTables @Html(noTables)} annotation is used to force beans to be serialized as trees instead of tables
The {@link org.apache.juneau.html.annotation.Html#render @Html(render)} annotation allows for custom rendering of bean property values when serialized as HTML. Using this class, you can alter the CSS style and HTML content of the bean property.
The following example shows two render classes that customize the appearance of the
{@link org.apache.juneau.html.HtmlDocSerializer} is an extension of {@link org.apache.juneau.html.HtmlSerializer} that wraps serialized POJOs in a complete HTML document.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
This class is used extensively in the creation of POJO-based user interfaces in the REST API.
The {@link org.apache.juneau.html.HtmlDocSerializer.Builder#template(Class)} setting defines a template for the HTML page being generated. The default template is described next.
The {@link org.apache.juneau.html.BasicHtmlDocTemplate} class defines a default template for HTML documents created by {@link org.apache.juneau.html.HtmlDocSerializer}.
The HTML document created by this template consists of the following structure:
Custom page templates can be created by implementing the {@link org.apache.juneau.html.HtmlDocTemplate} interface and associating it with your {@link org.apache.juneau.html.HtmlDocSerializer} using the {@link org.apache.juneau.html.HtmlDocSerializer.Builder#template(Class)} setting.
The interface implementation is open-ended allowing you to define the contents of the page any way you wish.
The {@link org.apache.juneau.html.HtmlSchemaSerializer} class is the HTML-equivalent to the {@link org.apache.juneau.json.JsonSchemaSerializer} class. It's used to generate HTML versions of JSON-Schema documents that describe the output generated by the {@link org.apache.juneau.json.JsonSerializer} class.
The code for creating our POJO model and generating HTML-Schema is shown below:
The result is the HTML table shown below:
type | object | ||||||||||||||||||||||||||||||||||||||||||
properties |
|
Juneau supports converting arbitrary POJOs to and from UON strings using ultra-efficient serializers and parsers. The serializer converts POJOs directly to UON strings without the need for intermediate DOM objects using a highly-efficient state machine. Likewise, the parser creates POJOs directly from UON strings without the need for intermediate DOM objects.
Juneau uses UON (URL-Encoded Object Notation) for representing POJOs. The UON specification can be found here.
The following example shows JSON for a typical bean:
Person
(
Java type | JSON equivalent | UON |
---|---|---|
Maps/beans | OBJECT |
|
Collections/arrays | ARRAY |
|
Booleans | BOOLEAN |
|
int/float/double/... | NUMBER |
|
null | NULL |
|
String | STRING |
|
Refer to the UON specification for a complete set of syntax rules.
The {@link org.apache.juneau.uon.UonSerializer} class is used to serialize POJOs into UON.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.uon.UonParser} class is used to parse UON into POJOs.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following pre-configured parsers are provided for convenience:
Juneau supports converting arbitrary POJOs to and from URL-encoded strings using ultra-efficient serializers and parsers. The serializer converts POJOs directly to URL-encoded strings without the need for intermediate DOM objects using a highly-efficient state machine. Likewise, the parser creates POJOs directly from URL-encoded strings without the need for intermediate DOM objects.
Juneau uses UON (URL-Encoded Object Notation) for representing POJOs as URL-Encoded values in key-value pairs. The UON specification can be found here.
The following example shows JSON for a typical bean:
Person
Java type | JSON equivalent | UON |
---|---|---|
Maps/beans | OBJECT |
|
Collections/arrays | ARRAY |
|
Booleans | BOOLEAN |
|
int/float/double/... | NUMBER |
|
null | NULL |
|
String | STRING |
|
Refer to the UON specification for a complete set of syntax rules.
The {@link org.apache.juneau.urlencoding.UrlEncodingSerializer} class is used to serialize POJOs into URL-Encoding.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.urlencoding.UrlEncodingParser} class is used to parse URL-Encoding into POJOs.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following pre-configured parsers are provided for convenience:
The {@link org.apache.juneau.urlencoding.annotation.UrlEncoding @UrlEncoding} annotation is used to override the behavior of {@link org.apache.juneau.urlencoding.UrlEncodingSerializer} on individual bean classes or properties.
The {@link org.apache.juneau.urlencoding.annotation.UrlEncoding#expandedParams() expandedParams} setting is used to force bean properties of type array or Collection to be expanded into multiple key/value pairings.
Juneau supports converting arbitrary POJOs to and from MessagePack using ultra-efficient serializers and parsers.
MessagePack is a compact binary form of JSON. The serialization support for MessagePack mirrors that of JSON.
The {@link org.apache.juneau.msgpack.MsgPackSerializer} class is used to serialize POJOs into MessagePack.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.msgpack.MsgPackParser} class is used to parse MessagePack into POJOs.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following pre-configured parsers are provided for convenience:
Juneau supports converting arbitrary POJOs to and from strings using OpenAPI-based schema rules.
The relevant classes for using OpenAPI-based serialization are:
The {@link org.apache.juneau.httppart.HttpPartSchema} class is used to define the formatting and validations for a POJO. It's used in conjunction with the serializer and parser to produce and consume HTTP parts based on OpenAPI rules.
Later in the rest-server and rest-client sections, we also describe how the following annotations can be applied to method parameters and class types to define the schema for various HTTP parts:
Unlike the other Juneau serializers and parsers that convert input and output directly to-and-from POJOs,
the OpenAPI serializers and parsers use intermediate objects based on the
The following table shows the "natural" intermediate type of the object based on the
Type | Format | Intermediate Java Type |
---|---|---|
binary binary-spaced |
||
date-time |
{@link java.util.Calendar} | |
No intermediate type. (serialized directly to/from POJO) |
||
empty | {@link java.lang.String} | |
empty | {@link java.lang.Boolean} | |
{@link java.lang.Integer} | ||
{@link java.lang.Long} | ||
{@link java.lang.Float} | ||
{@link java.lang.Double} | ||
empty | Arrays of intermediate types on this list. | |
No intermediate type. (serialized directly to/from POJO) |
||
empty | ||
No intermediate type. (serialized directly to/from POJO) |
The valid POJO types for serializing/parsing are based on the intermediate types above. As a general rule, any POJOs that are the intermediate type or transformable to or from the intermediate type are valid POJO types.
For example, the following POJO type can be transformed to and from a byte array.
This example shows how that POJO can be converted to a BASE64-encoded string.
In addition to defining format, the schema also allows for validations of the serialized form.
It looks simple but the implementation is highly sophisticated being able to serialize and parse and validate using complex schemas.
The next sections go more into depth on serializing and parsing various POJO types.
The {@link org.apache.juneau.oapi.OpenApiSerializer} class is used to convert POJOs to HTTP parts.
The class hierarchy for the builder of this serializer is:
Refer to the builder javadocs for configurable settings.
Later we'll describe how to use HTTP-Part annotations to define OpenAPI schemas for serialization and parsing
of HTTP parts.
The following example is a preview showing an HTTP body defined as pipe-delimited list of comma-delimited numbers (e.g.
Under-the-covers, this gets converted to the following schema object:
HttpPartSchema
Various convenience methods exist for shortening this code.
The following code shows how the schema above can be used to create our pipe+csv list of numbers:
As a general rule, any POJO convertible to the intermediate type for the
Type | Format | Valid parameter types |
---|---|---|
binary binary-spaced |
|
|
date-time |
|
|
|
||
empty |
|
|
empty |
|
|
|
||
|
||
|
||
|
||
empty |
|
|
|
||
empty |
|
|
|
For arrays, an example of "Any POJO transformable to arrays of the default types" is:
In the example above, our POJO class can be used to create our pipe-delimited list of comma-delimited numbers:
The
The following shows an example of a bean with several properties of various types.
We define the following schema:
Then we serialize our bean:
String
The results of this serialization is shown below:
( f1=foo, f2=Zm9v, f3=666F6F, f4='66 6F 6F', f5=2012-12-21T12:34:56Z, f6=foo, f7=1, f8=2, f9=1.0, f10=1.0, f11=true, fExtra=1 )
The following is an example of a bean with various array property types:
For this bean, we define the following schema:
Serializing this bean produces the following output:
( f1=@('a,b',null), f2=@(Zm9v,null), f4=@(2012-12-21T12:34:56Z,null), f5=@(666F6F,null), f6=@('66 6F 6F',null), f7=@(a,b,null), f8=@(1,2,null), f9=@(3,4,null), f10=@(1.0,2.0,null), f11=@(3.0,4.0,null), f12=@(true,false,null), fExtra=@(1,2,null) )
The {@link org.apache.juneau.oapi.OpenApiParser} class is used to convert HTTP parts back into POJOs.
The class hierarchy for the builder of this parser is:
Refer to the builder javadocs for configurable settings.
The following is the previous example of a schema that defines the format of a pipe-delimited list of comma-delimited numbers (e.g.
The following code shows how the schema above can be used to parse our input into a POJO:
As a general rule, any POJO convertible from the intermediate type for the
Type | Format | Valid parameter types |
---|---|---|
binary binary-spaced |
|
|
date-time |
|
|
|
||
empty |
|
|
empty |
|
|
|
||
|
||
|
||
|
||
empty |
|
|
|
||
empty |
|
|
|
Additionally, any of the type above can also be wrapped as {@link java.util.Optional Optionals}.
For arrays, an example of "Any POJO transformable from arrays of the default types" is:
In the example above, our POJO class can be constructed from our pipe-delimited list of comma-delimited numbers:
Just like serialization, the
The following shows an example of a bean with several properties of various types.
We define the following schema again:
Then we parse our input into our POJO:
String
Note that serializing into generic
We can also parse into Maps as well:
String
juneau-marshall-rdf-
org.apache.juneau.marshaller.rdf_
The
juneau-dto-
org.apache.juneau.dto_
The
The Juneau HTML5 DTOs are simply beans with fluent-style setters that allow you to quickly construct HTML fragments as Java objects. These object can then be serialized to HTML using one of the existing HTML serializers, or to other languages such as JSON using the JSON serializers.
The {@link org.apache.juneau.dto.html5.HtmlBuilder} class is a utility class with predefined static methods that allow you to easily construct DTO instances in a minimal amount of code.
The following examples show how to create common HTML DOM objects.
Java code | HTML |
---|---|
|
|
|
|
|
Using the HTML5 DTOs, you should be able to construct any valid HTML5 from full document bodies to any possible fragments.
The {@link org.apache.juneau.html.HtmlParser} class can be used convert these HTML documents back into POJOs.
Other serializers and parsers (e.g. {@link org.apache.juneau.json.JsonSerializer}) can be used to represent these POJOs in languages other than HTML.
The Juneau ATOM feed DTOs are simply beans with fluent-style setters. The following code shows a feed being created programmatically using the {@link org.apache.juneau.dto.atom.AtomBuilder} class.
To serialize this to ATOM, use the {@link org.apache.juneau.xml.XmlSerializer} class:
The {@link org.apache.juneau.xml.XmlParser} class can be used convert these Atom documents back into POJOs.
Other serializers and parsers (e.g. {@link org.apache.juneau.json.JsonSerializer}) can be used to represent these POJOs in languages other than XML.
The Juneau Swagger DTOs are simply beans with fluent-style setters that allow you to quickly construct Swagger documents as Java objects. These object can then be serialized to JSON using one of the existing JSON serializers, or to other languages such as XML or HTML using the other serializers.
The {@link org.apache.juneau.dto.swagger.SwaggerBuilder} class is a utility class with predefined static methods that allow you to easily construct DTO instances in a minimal amount of code.
The following is an example Swagger document from the Swagger website.
{
This document can be generated by the following Java code:
Methods that take in beans and collections of beans can also take in JSON representations of those objects.
Properties can also be accessed via the {@link org.apache.juneau.dto.swagger.SwaggerElement#get(String,Class)}
and {@link org.apache.juneau.dto.swagger.SwaggerElement#set(String,Object)} methods.
These methods can also be used to set and retrieve non-Swagger attributes such as
Swagger docs can be parsed back into Swagger beans using the following code:
Swagger
The {@link org.apache.juneau.dto.swagger.ui.SwaggerUI} class is a DTO class for generating Swagger user interfaces from {@link org.apache.juneau.dto.swagger.Swagger} beans.
The
Using {@link org.apache.juneau.dto.swagger.ui.SwaggerUI}, we're able to render that JSON as a Swagger user interface when the request is asking for HTML:
The class itself is nothing more than a POJO swap that swaps out {@link org.apache.juneau.dto.swagger.Swagger} beans with {@link org.apache.juneau.dto.html5.Div} elements:
The {@link org.apache.juneau.rest.servlet.BasicRestServlet} class (describe later) shows how this swap is used in the REST interface to generate the Swagger UI shown above:
juneau-config-
org.apache.juneau.config_
The
Config files are accessed through the {@link org.apache.juneau.config.Config} class which
are created through the {@link org.apache.juneau.config.Config.Builder} class.
Builder creator methods are provided on the
Once instantiated, reading values from the config are simple:
The config language may look simple but it is a very powerful feature with many capabilities including:
/ \ [ ] = #
Configuration files can contain entries for anything from primitive types up to complex hierarchies of POJOs consisting of maps, collections, and/or beans.
Entries are accessed via the {@link org.apache.juneau.config.Config#get(String)} method which returns the following bean:
The most common case for configuration values are primitives.
On integers and longs,
Numbers can also use hexadecimal and octal notation:
Strings with newlines are treated as multi-line values that get broken into separate lines:
Typically, multi-line values are started on the next line for clarity like so:
Entries can also be read as POJOs. In theory, any parsable POJO type can be represented as a config value. However in practice, we're typically talking about the following:
An example of an object convertible from a String was already shown in an example above. In that case, it was a URL which has a public constructor that takes in a String:
Beans are represented as JSON 5 by default:
The default serializer and parser is registered on the Config through the following methods:
The
String[]
String arrays can also be represented in JSON when the registered parser is a JSON parser:
String[]
Primitive arrays can also be retrieved using the
Arrays of POJOs can also be retrieved using the methods as well:
Address[]
Entries can also be read as Java Collection Framework objects.
The
Examples are shown below:
List<Address>
Oftentimes, it might be useful to parse into the {@link org.apache.juneau.collections.JsonList} and {@link org.apache.juneau.collections.JsonMap} classes that provide the various convenience methods for working with JSON-like data structures:
JsonMap
Entries can also be accessed as binary data. Binary data can be represented in 3 formats:
The binary data format is controlled via the following setting:
For example:
Binary lines can be split up into separate lines for readability:
Binary data line wrapping can be controlled via the following setting:
Config files can contain variables that get resolved dynamically using the previously-described {@link org.apache.juneau.svl.VarResolver} API.
Config
By default,
The variable resolver is controlled via the following setting:
Additionally, the following method can be used to retrieve a
The default variable resolver also provides the following logic variables for performing simple logical operations:
The
The
The
The
The following method can be used to associates entry modifiers to a config:
Mods are used to modify values before being persisted. This can be used to
replace or encode sensitive information. They are denoted by a single
character that gets appended between angle brackets on the property name (e.g.
The framework comes built-in with a simple {@link org.apache.juneau.config.mod.XorEncodeMod xor-encode} mod tied
to the
Custom encoders can be used to provide your own encoding support by implementing the {@link org.apache.juneau.config.mod.Mod} class.
Unmodified values are encoded when the file is saved using the {@link org.apache.juneau.config.Config#commit()} method. They can also be encoded immediately by calling {@link org.apache.juneau.config.Config#applyMods()} which can typically be done during JVM startup to immediately encode any unencoded passwords in the file.
Config sections can be retrieved in-bulk using the {@link org.apache.juneau.config.Config#getSection(String)} method. It returns the following bean:
The {@link org.apache.juneau.config.Section#asMap() asMap()} method allows you to access a section as simple key/value pairs.
Maps created this way are snapshot copies of the section at the time of the method call.
Config files can also be used to directly populate beans using {@link org.apache.juneau.config.Section#asBean(Class) asBean()} or {@link org.apache.juneau.config.Section#writeToBean(Object,boolean) writeToBean()}.
Like maps, beans created this way are snapshot copies of the section at the time of the method call.
Config sections can also be accessed via interface proxies using {@link org.apache.juneau.config.Section#asInterface(Class)}.
While section maps and beans retrieve copies of the configuration data at the time of the method call, section interfaces can also be use to set values in the underlying configuration.
The following methods allow you to add, remove, and modify entries and sections in a config file:
The method {@link org.apache.juneau.config.Config#set(String,Object,Serializer,String,String,List)} can be used for adding comments and pre-lines to entries, and specifying encoded values.
The last 4 arguments in {@link org.apache.juneau.config.Config#set(String,Object,Serializer,String,String,List)}
are optional in that if you pass
Sections can be added with optional pre-lines using the
Changes made to the configuration are transactional in nature. They are kept in memory until you call the {@link org.apache.juneau.config.Config#commit()} method. Until then, you still see the modified values when you call any of the getters but the modified values exist only in memory.
Changes can be rolled back using the {@link org.apache.juneau.config.Config#rollback()} method.
In general, external file modifications will be detected immediately in the
The
If the
If the same entry is both internally and externally modified, the external modification will be overwritten (although both change events will be seen by listeners).
Setter methods that take in a
The value can then be retrieved using the equivalent parser:
Address
The following methods can be used to bulk-load configuration values:
Changes can then be committed using the {@link org.apache.juneau.config.Config#commit()} method.
Configuration change events can be listened for using the following methods:
The {@link org.apache.juneau.config.event.ConfigEventListener} interface consists of the following method:
The {@link org.apache.juneau.config.event.ConfigEvent} class provides access to all the information about the updated entry:
The listener method is triggered:
In both cases, the listener is triggered after the changes have been committed.
The following methods are used for serializing
Both methods are thread safe.
Configurations can import values from other configurations using the following syntax:
A configuration can contain zero or more imports anywhere in the file. However, for clarity, imports should normally be placed in the default section of the configuration file. The resolved configuration is retrieved from the configuration store used for the child configuration.
Configuration imports can be nested arbitrarily deep.
Values can be overridden by child configurations.
Config
Changes made to imported configurations are automatically reflected in the child configuration and
partake in the listener API as if the entries were part of the child configuration.
Only non-overridden values trigger listener events. For example, if an imported configuration
defines a value for
Values can be overwritten in child configurations but the values will only be set in that configuration and not the imported configuration.
Dynamically adding an import will cause change events to be generated for imported values.
Dynamically removing an import has the same effect as removing keys and generates
Note that when dynamically adding or removing imports, overridden keys in the child config will be filtered from the change events.
Configuration files are stored in entities called Stores.
The methods that need to be implemented on a store are:
Read is self-explanatory:
Write is slightly trickier:
The update method is called whenever the stored file gets modified externally:
Two configuration stores are provided by default:
The store is defined on the
The default store used is {@link org.apache.juneau.config.store.FileStore#DEFAULT} which defines the execution directory as the file system directory to store and retrieve files.
The {@link org.apache.juneau.config.store.MemoryStore} class is simply an in-memory storage location for configuration files. There is no hard persistence and is used primarily for testing purposes.
However, the implementation provides a good idea on how stores work (especially the write method):
The {@link org.apache.juneau.config.store.FileStore} is the typical store used for configuration files. It provides the following configurable settings:
The
The example below shows a starting point for an implementation based on polling a relational database. The source can be found here: {@link org.apache.juneau.examples.core.config.store.SqlStore}. Completing it is left as an exercise:
The
Note that this is a different listener than {@link org.apache.juneau.config.event.ConfigEventListener}. In this case, we're just listening for changed files:
This listener is used by the
The following settings can be used to create read-only
This causes all methods that make modifications to throw {@link java.lang.UnsupportedOperationException}.
In general, it's good practice to close Config if you're only creating them temporarily so that their listeners get unregistered from the underlying storage APIs.
Each JVM has a system default config. This is a configuration file that serves as the default configuration for the system. It's accessed using the following static methods:
If you do not specify a system default config, one will be automatically searched for. The search is done in the following order:
Later in the section on REST resources, we describe how to associate configurations with REST resources
using the {@link org.apache.juneau.rest.annotation.Rest#config() @Rest(config)} annotation.
The system default configuration can be referenced with the keyword
By default, all properties in the system default configuration are automatically set as system properties.
This can be disabled by setting the system property
juneau-assertions-
org.apache.juneau.assertions_
The {@link org.apache.juneau.assertions} package in Juneau is a powerful API for performing fluent style assertions. It is used to implement built-in assertion methods on both the server and client side APIs. But it can also be used standalone for testing.
The {@link org.apache.juneau.assertions} package in Juneau is a powerful API for performing fluent style assertions. It is used throughout the REST client and server APIs for performing inline assertions on REST requests and responses.
The assertions API is designed to be used in both code (as it's done in the REST APIs) or for standalone use in unit tests.
The {@link org.apache.juneau.assertions.Assertions} class provides various static methods for invoking assertions on a variety of object types for simplified unit testing.
Assertions have 3 categories of methods:
Testing methods (
Transform methods (
Configuration methods (
The following shows the class hierarchy for the {@link org.apache.juneau.assertions.IntegerAssertion IntegerAssertion} class showing the general design pattern for assertion classes:
In the design, the "Fluent" classes (e.g. {@link org.apache.juneau.assertions.FluentIntegerAssertion}) allow you to specify the object
that gets returned when the test method is executed. When used in the
For more information about the capabilities of the Assertions API, refer to the methods on the {@link org.apache.juneau.assertions.Assertions} methods above.
juneau-rest-common-
org.apache.juneau.rest.common_
The {@link org.apache.juneau.http} package contains a slew of useful extensions to the Apache HttpComponents libraries and define APIs used extensively in the REST server and client APIs.
These APIs extend from the Apache HttpComponents libraries and can be used with libraries based on it such as Apache HttpClient. The REST Client API described later is built on top of Apache HttpClient and many of the classes defined in this package make up integral components of that API. Likewise, the APIs defined here are also used in the REST Server APIs also described later.
The {@link org.apache.juneau.http.header} package contains various convenience classes for creating standard HTTP components using static imports.
The {@link org.apache.juneau.http.HttpHeaders} class contains many convenience static methods and fields for working with standard HTTP request and response headers and header lists.
This class is vast in scope and covers all request and response headers defined in RFC2616.
In addition to the predefined headers, various methods are provided for free-form headers. Each accepts either static values or values from {@link java.util.function.Supplier Suppliers}:
The {@link org.apache.juneau.http.HttpHeaders#serializedHeader(String,Object) serializedHeader} methods allows for headers serialized using schema-based serializers such as the OpenAPI serializer.
Static methods are also provided for instantiating {@link org.apache.juneau.http.annotation.Header}-annotated or other HttpComponent-defined header classes:
Lists of headers can be produced with the following methods:
The capabilities of the {@link org.apache.juneau.http.header.HeaderList} class is described later.
The {@link org.apache.juneau.http.HttpParts} class contains convenience static methods for generating query/form-data/path parts and part lists.
The following methods are provided for creating parts. Each accepts either static values or values from {@link java.util.function.Supplier Suppliers}:
The {@link org.apache.juneau.http.HttpParts#serializedPart(String,Object) serializedPart} methods allows for parts serialized using schema-based serializers such as the OpenAPI serializer.
Lists of parts can be produced with the following methods:
The capabilities of the {@link org.apache.juneau.http.part.PartList} class is described later.
The {@link org.apache.juneau.http.HttpEntities} class contains convenience static methods for generating HTTP message entities. Returned objects extend from {@code org.apache.http.HttpEntity} but provides the following additional features:
The following methods are provided for creating entities. Each accepts either static values or values from {@link java.util.function.Supplier Suppliers} and returns builders:
HTTP entities are automatically supported in both the server and client REST APIs for requests and responses.
The {@link org.apache.juneau.http.HttpResources} class contains convenience static methods for generating HTTP message resources. Returned objects extend from {@link org.apache.juneau.http.resource.HttpResource} which extends from {@link org.apache.http.HttpEntity} but with additional arbitrary headers.
The following methods are provided for creating entities. Each accepts either static values or values from {@link java.util.function.Supplier Suppliers} and are in the form of builders.
The most common location where resources are used are as returned types of REST operation methods described later.
The {@link org.apache.juneau.http.HttpResponses} class contains convenience static methods for standard HTTP responses. Returned objects extend from {@code org.apache.http.HttpResponse} and are in the form of builders.
The following methods are provided for creating entities:
The most common location where these responses are used are in REST operation methods described later.
The {@link org.apache.juneau.http.annotation} package contains annotations for defining both server and client side APIs. The server-side APIs also use it for producing auto-generated Swagger documentation through the REST API itself.
These annotations are used in a variety of places in the server and client side REST interfaces, especially for remote proxies. These will be described later in those sections.
The {@link org.apache.juneau.http.header} package contains implementations of
These headers extend from the following classes that provide data-type specific functionality:
These subclasses provide various convenience methods to allow for easy fluent-style coding.
The {@link org.apache.juneau.http.header.HeaderList} class is a list of HTTP headers.
Static methods are provided on {@link org.apache.juneau.http.HttpHeaders} to further simplify creation of header lists.
The builder class supports setting default header values (i.e. add a header to the list if it isn't otherwise in the list).
Note that this is different from simply setting a value twice as using default values will not overwrite existing
headers.
The following example notes the distinction:
Various methods are provided for iterating over the headers in this list to avoid array copies.
In general, try to use these over the {@link org.apache.juneau.http.header.HeaderList#getAll() getAll()} / {@link org.apache.juneau.http.header.HeaderList#getAll(String) getAll(String)} methods that require array copies.
The {@link org.apache.juneau.http.header.HeaderList#get(String) get(String)} method is special in that it will collapse multiple headers with the same name into a single comma-delimited list (see RFC 2616 Section 4.2 for rules).
The {@link org.apache.juneau.http.header.HeaderList#get(Class) get(Class)} and {@link org.apache.juneau.http.header.HeaderList#get(String,Class) get(String,Class)} methods are provided for working with {@link org.apache.juneau.http.annotation.Header}-annotated beans.
HeaderList headers = HeaderList.of(Accept.TEXT_JSON, Accept.TEXT_XML);
By default, header names are treated as case-insensitive. This can be changed using the {@link org.apache.juneau.http.header.HeaderList#caseSensitive(boolean) caseSensitive(boolean)} method.
A {@link org.apache.juneau.svl.VarResolver} can be associated with this builder to create header values with embedded variables that are resolved at runtime.
The {@link org.apache.juneau.http.header.HeaderList} object can be extended to defined pre-packaged lists of headers which can be used in various annotations throughout the framework.
The {@link org.apache.juneau.http.part} package contains implementations of
The {@link org.apache.juneau.http.part.PartList} class is a list of HTTP parts (form-data, query-parameters, path-parameters).
PartList
Convenience creators are provided for creating lists with minimal code:
PartList
Static methods are provided on {@link org.apache.juneau.http.HttpParts} to further simplify creation of part lists.
The builder class supports setting default part values (i.e. add a part to the list if it isn't otherwise in the list).
Note that this is different from simply setting a value twice as using default values will not overwrite existing
parts.
The following example notes the distinction:
Various methods are provided for iterating over the parts in this list to avoid array copies.
In general, try to use these over the {@link org.apache.juneau.http.part.PartList#getAll() getAll()} / {@link org.apache.juneau.http.part.PartList#getAll(String) getAll(String)} methods that require array copies.
Similar to the way multiple headers can be collapsed into a single value, the {@link org.apache.juneau.http.part.PartList#get(String) get(String)} method is special in that it will collapse multiple parts with the same name into a single comma-delimited list.
The {@link org.apache.juneau.http.part.PartList#get(Class) get(Class)} and {@link org.apache.juneau.http.part.PartList#get(String,Class) get(String,Class)} methods are provided for working with {@link org.apache.juneau.http.annotation.FormData} / {@link org.apache.juneau.http.annotation.Query} / {@link org.apache.juneau.http.annotation.Path}-annotated beans.
MyQueryBean
A {@link org.apache.juneau.svl.VarResolver} can be associated with this builder to create part values with embedded variables that are resolved at runtime.
The {@link org.apache.juneau.http.part.PartList} object can be extended to defined pre-packaged lists of parts which can be used in various annotations throughout the framework.
The {@link org.apache.juneau.http.entity} package contains implementations of
The {@link org.apache.juneau.http.resource} package contains implementations of {@link org.apache.juneau.http.resource.HttpResource} which are extensions of
HTTP entities and resources can be used by both the server and client side APIs described in later sections.
The {@link org.apache.juneau.http.response} package contains predefined
These are built upon existing HttpComponents APIs:
The most common location where these responses are used are in REST operation methods described later.
The following classes are also provided for constructing your own custom responses:
The {@link org.apache.juneau.http.remote} package contains the annotations used for defining client-side remote proxies.
See Proxies for more information on use of these annotations.
juneau-rest-server-
org.apache.juneau.rest.server_
The
One of the biggest advantages of the Juneau REST framework over similar architectures is that it hides the
serialization layer from the developer.
The developer can work entirely with POJOs and let the Juneau framework handle all the serialization and
parsing work.
The developer need never know what the
The API builds upon the existing JEE Servlet API. The root class, {@link org.apache.juneau.rest.servlet.RestServlet} is nothing but a specialized {@link jakarta.servlet.http.HttpServlet}, and the {@link org.apache.juneau.rest.RestRequest} and {@link org.apache.juneau.rest.RestResponse} classes are nothing more than specialized {@link jakarta.servlet.http.HttpServletRequest} and {@link jakarta.servlet.http.HttpServletResponse} objects. This allows maximum flexibility for the developer since you can let Juneau handle operations such as serialization, or you can revert to the existing servlet APIs to do low-level processing of requests yourself. It also means you need nothing more than a Servlet container such as Jetty to use the REST framework.
Many of the examples in this document are pulled directly from
A REST resource is simply a Java class annotated with {@link org.apache.juneau.rest.annotation.Rest @Rest}. The most common case is a class that extends {@link org.apache.juneau.rest.servlet.BasicRestServlet}, which itself is simply an extension of {@link jakarta.servlet.http.HttpServlet} which allows it to be deployed as a servlet.
Juneau has two sample applications for demonstrating how to use the REST API, one using Jetty and one using Spring Boot:
The
The Jetty application consists of the following application class that registers our top-level servlet:
The root resources class is an example of a router page that is used to attach children to:
This is what it looks like in a browser:
The {@link org.apache.juneau.examples.rest.HelloWorldResource} class is our basic example of a child REST resource:
This is what it looks like in a browser:
It doesn't much simpler than that. In this case, we're simply returning a string that will be converted to any of the supported languages (e.g. JSON, XML, HTML, ...). However, we could have returned any POJO consisting of beans, maps, collections, etc...
The {@link org.apache.juneau.rest.annotation.Rest @Rest} annotation is the primary way of defining and configuring REST resource classes. The functionality of the class itself is covered in detail in the topics below.
The {@link org.apache.juneau.rest.annotation.Rest @Rest} annotation in inheritable from parents and interfaces of resource classes. When multiple annotations are defined at different levels, the annotation values are combined. This is a particularly useful feature because it allows you to define your own configured parent resource classes that can be extended by all your child resources so that they all share common settings.
The following example represents the bare-minimum needed for deploying a top-level REST endpoint with basic JSON marshalling support:
The {@link org.apache.juneau.rest.servlet.RestServlet} class provides all the logic for starting up your REST application when the servlet container calls {@link org.apache.juneau.rest.servlet.RestServlet#init(ServletConfig) init(ServletConfig)}. On startup, it scans your class for annotations and sets up all of your serializers and parsers. It then does this recursively for all child resources.
Users will typically not extend directly from {@link org.apache.juneau.rest.servlet.RestServlet}. Instead, several classes are provided by the framework to provide additional
functionality and to handle different use-cases. Users will typically extend from one of these
The {@link org.apache.juneau.rest.servlet.RestServlet} class itself is not configured with any serializers or parsers. However, it does provide several convenience methods to be aware of:
The
The
For example, if you want to provide a resource that supports all languages in Juneau, simply add the {@link org.apache.juneau.rest.config.BasicUniversalConfig} interface like so:
The
The
The
The following is a breakdown of which classes you will use in different cases:
Child Resources are REST servlets or objects that are linked to parent resources through the {@link org.apache.juneau.rest.annotation.Rest#children() @Rest(children)} annotation.
The path of the child resource gets appended to the path of the parent resource.
So in the example above, the child resource is accessed through the URL
One advantage of using child resources is that they do not need to be declared in the JEE
As explained earlier, child REST objects typically extend from {@link org.apache.juneau.rest.servlet.BasicRestObject} or {@link org.apache.juneau.rest.servlet.BasicRestObjectGroup} and not from one of the servlet classes. They also technically don't even need to extend from those classes and can instead just be a normal class annotated with the bare-minimum {@link org.apache.juneau.rest.annotation.Rest @Rest} and {@link org.apache.juneau.rest.annotation.RestOp @RestOp} annotations.
The path can contain variables that get resolved to {@link org.apache.juneau.http.annotation.Path @Path} parameters or access through the {@link org.apache.juneau.rest.RestRequest#getPathParams()} method.
Variables can be used on either top-level or child resources and can be defined on multiple levels. Path variables resolved in parent resource paths are also available to the child resources.
REST resources are deployed in the following ways:
When deployed in a J2EE container, you MUST extend from one of the servlet classes.
Deployment in a servlet container is typically done by adding a servlet entry for the top-level resources to the JEE
Deployment in a Spring Boot environment involves defining your top-level resources as Spring Beans. Top-level resources must extend from {@link org.apache.juneau.rest.springboot.BasicSpringRestServlet} or {@link org.apache.juneau.rest.springboot.BasicSpringRestServletGroup} so that Juneau can hook into the injection framework provided by Spring. Child resource CAN be defined as injected Spring Beans as well but it is not a requirement.
Lifecycle hooks allow you to hook into lifecycle events of the servlet/resource creation and REST calls.
For example, if you want to add an initialization method to your resource:
Or if you want to intercept REST calls:
The following lifecycle annotations are provided.
REST Java methods are identified on REST servlets using the {@link org.apache.juneau.rest.annotation.RestOp @RestOp} annotation. The annotation allows the framework to identify the available REST methods through reflection.
The following specialized annotations are also provided for specific HTTP methods:
When the
The HTTP method can be inferred from the Java method by starting the method name with any of the following:
If
If
Java methods can contain any of the following parameters in any order:
In Spring Boot environments, any available Spring Beans can also be passed in as parameters.
Additional parameter types can be defined via the annotation {@link org.apache.juneau.rest.annotation.Rest#restOpArgs()} or by calling {@link org.apache.juneau.rest.RestContext.Builder#restOpArgs(Class...)}.
The return type of the Java method can be any serializable POJO as defined in POJO Categories.
It can also be
In addition to POJOs, the following return types are also supported:
REST Java methods can also generate a response via the following:
Additional parameter types can be defined via the annotation {@link org.apache.juneau.rest.annotation.Rest#responseProcessors()} or by calling {@link org.apache.juneau.rest.RestContext.Builder#responseProcessors(Class...)}.
Annotated Java methods can throw any of the following:
All other throwables get processed as follows:
The {@link org.apache.juneau.rest.annotation.RestOp#path() @RestOp(path)} annotation allows
you to define URL path patterns to match against.
These patterns can contain variables of the form
In the following example, 3 separate GET request handlers are defined with different path patterns. Note how the variables are passed in as additional arguments on the method, and how those arguments are automatically converted to the specified class type...
By default, path patterns are matched using a best-match heuristic. When overlaps occur, URLs are matched from most-specific to most-general order:
Paths that end with
The following example shows the distinction.
{@link org.apache.juneau.rest.matcher.RestMatcher RestMatchers} are used to allow multiple Java methods to be tied to the same HTTP method and path but differentiated by some request attribute such as a specific header value.
The interface for matchers is simple:
Through the use of the built-in
For example, the URL
To support overloaded methods, the {@link org.apache.juneau.rest.annotation.Rest#allowedMethodParams() @Rest(allowedMethodParams)} setting must be enabled on your servlet.
Refer to the following Javadocs for more information:
In previous examples we showed the ability to pass in annotated parameters on {@link org.apache.juneau.rest.annotation.RestOp}-annotated methods to parse standard HTTP parts:
Annotations are provided for both request and response HTTP parts.
The annotations used for defining the schema for request HTTP parts are:
These annotation can be used on method parameters or on the parameter types themselves, or a combination of both.
Juneau comes with three basic marshaller types for serializing and parsing Header, Query, Form, and Path parts:
By default, the REST API uses the OpenAPI serializer and parser which allows for schema-based marshalling. You also have the option to use UON marshalling which is schema-less but allows for JSON-equivalent data structures (object/array/primitives/...) using URL-encoding notation. This can be done by overriding the part marshallers through the following APIs:
The OpenAPI marshallers themselves also have the ability to support UON notation for individual parts via the schema itself:
The following annotations allow for defining part schemas based on the OpenAPI standard.
The {@link org.apache.juneau.http.annotation.Header @Header}/{@link org.apache.juneau.http.annotation.Query @Query}/ {@link org.apache.juneau.http.annotation.FormData @FormData}/{@link org.apache.juneau.http.annotation.Path @Path} annotations can be used on parameters of {@link org.apache.juneau.rest.annotation.RestOp @RestOp}-annotated methods to get access to request headers, query parameters, form-data parameters, and path parts.
The most typical scenario is to simply use the
This is functionally equivalent to the following code:
The special name
The {@link org.apache.juneau.http.annotation.Content @Content} annotation is used to identify POJOs to be used as the body of an HTTP request.
This is functionally equivalent to the following code:
In addition to {@link org.apache.juneau.http.annotation.Content @Content}-annotated parameters/types, the body of an HTTP request can be retrieved by passing in parameters of the following types (matched in the specified order):
When used in combination with the mutable {@link org.apache.juneau.Value} object, the {@link org.apache.juneau.http.annotation.StatusCode @StatusCode} and {@link org.apache.juneau.http.annotation.Header @Header} annotations can be used on parameters {@link org.apache.juneau.rest.annotation.RestOp @RestOp}-annotated methods to to define to response codes and headers.
This is functionally equivalent to the following code:
The default registered part marshallers, {@link org.apache.juneau.oapi.OpenApiSerializer} and {@link org.apache.juneau.oapi.OpenApiParser}, are used to marshall POJOs using schemas defined via the {@link org.apache.juneau.annotation.Schema @Schema} annotation.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Schema-based marshalling works for both request and response parts.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
The part and schema annotations are also used for supplying swagger information about the HTTP part. This information is used to populate the auto-generated Swagger documentation and UI.
SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields as well.
Among other things, this allow for annotation values to be defined externally and the ability to produce localized swagger documents
based on the
The {@link org.apache.juneau.http.annotation.Content @Content} annotation can also be used to parse HTTP request bodies using OpenAPI schemas
when the body content type matches the {@link org.apache.juneau.oapi.OpenApiParser} parser via the header
The following shows the same for a request body:
The list of valid POJO types for parameters depends on type and format of the value or items/entries of the value.
For example, instead of
As you can see, the complexity of possible input types expands significantly. For more information about valid parameter types, see OpenAPI Parsers.
By default, HTTP parts that don't have value (such as missing query parameters) end up with null values:
You have several options to provide default values for HTTP parts. The most common is to simply use {@link java.util.Optional} parameters and handle default values programmatically:
You can also specify default values on the annotations:
A third option is to specify default values via the {@link org.apache.juneau.rest.annotation.Rest} and {@link org.apache.juneau.rest.annotation.RestOp} annotations.
Default parts can also be specified programmatically through any of the following methods:
The {@link org.apache.juneau.http.annotation.Request @Request} annotation can used to define proxy interfaces against HTTP requests in combination with the following annotations used on methods:
The example above is identical in behavior to specifying individual annotated parameters on the
The return types of the getters must be the supported parameter types for the HTTP-part annotation used. Schema-based serialization and parsing is used just as if used as individual parameter types. Annotations used are the exact same used on REST parameters and have all the same feature support including automatic Swagger validation and documentation. Part names can either be explicitly specified or automatically inferred from the getter names.
For clarity, the
The {@link org.apache.juneau.http.annotation.Response} annotation can be used to define beans that return HTTP response parts via annotations and methods. They are used in combination with the following annotations:
Response beans can either be returned or thrown from {@link org.apache.juneau.rest.annotation.RestOp @RestOp}-annotated methods.
The following example shows the
Custom exceptions can also extend from one of the predefined HTTP exceptions such as the {@link org.apache.juneau.http.response.Unauthorized} exception:
Request HTTP parts can also be retrieved programmatically through the following classes that can be passed in as parameters or access through {@link org.apache.juneau.rest.RestRequest} bean:
Built in to these APIs are various convenience methods such as converting parts to different types or inline fluent assertions:
Juneau uses {@link org.apache.juneau.parser.Parser Parsers} and {@link org.apache.juneau.serializer.Serializer Serializers} for marshalling
HTTP request and response bodies to POJOs using the
Serializers and parsers can be associated with REST servlets using the following annotations:
Request bodies are parsed and passed in via {@link org.apache.juneau.http.annotation.Content @Content}-annotated parameters, and response bodies are returned or thrown by {@link org.apache.juneau.rest.annotation.RestOp @RestOp}-annotated methods and serialized.
The following classes provide common default serializers and parsers that can be used as-is or augmented by child classes:
Serializers and parsers can also be defined programmatically using an INIT hook method like shown below:
They can also be defined through custom REST contexts and builders.
Config annotations allow you to define serializer and parser properties using specialized annotations at either the class or operation levels:
Swaps are associated serializers and parsers registered on a REST resource via the {@link org.apache.juneau.annotation.BeanConfig} annotation on either the class or method level:
Config annotations are defined for all serializers and parsers:
HTTP form posts can be handled two ways:
The following example shows the first approach of handling an
The next example shows handling it as individual parts:
The advantage to the form input bean is that it can handle any of the parsable types (e.g. JSON, XML...) in addition to URL-Encoding while the latter approach only supports URL-Encoding.
The Juneau framework does not natively support multipart form posts. However, it can be done in conjunction with the Apache Commons File Upload library or through the Servlet 3.0 API directly.
The following is an example that uses the File Upload library to allow files to be uploaded as multipart form posts.
The following shows using the
Guards control access to REST classes and methods. When guards are associated at the class-level, it's equivalent to associating guards on all Java methods on the servlet. If multiple guards are present, ALL guards must pass. (Note that this is different in behavior to Matchers which require only one matcher to pass.)
Guards are associated with resource classes and methods via the following:
A common use for guards is to only allow admin access to certain Java methods...
A guard failure results in an
A simplified format is available for matching based on the user role on the request using the following:
Converters can be thought of as "post-processors" for response POJOs before they get passed to the serializer.
Converters are associated with resource classes and methods via the following:
Juneau defines the following converters out-of-the-box:
The {@link org.apache.juneau.rest.annotation.Rest#messages @Rest(messages)} annotation identifies the location of the resource bundle
for a
By default, the resource bundle name is assumed to match the class name. For example, given the class
Resource bundles are searched using the following base name patterns:
This annotation is used to provide request-localized (based on
Request-localized messages are also available by passing either of the following args into your Java method:
The value can be a relative path like
When using shared resource bundles, keys can be prefixed by class names like so and still retrieve by simple key names:
Messages are automatically inherited from super classes. If a string cannot be found in the bundle of the current class, it will be searched for up the class hierarchy.
The {@link org.apache.juneau.rest.annotation.Rest#encoders @Rest(encoders)} annotation can
be used to associate character encoders with a servlet class.
Encoders can be used to enable various kinds of compression (e.g.
Juneau defines the following encoders out-of-the-box:
The Server API provides methods for associating configuration files with REST servlets so that configuration properties can be defined in external files. It uses the following annotation:
In recap, the Configuration API provides support for INI-style configuration files with embedded string variables:
These properties are then accessible through the {@link org.apache.juneau.config.Config} class.
The annotation itself can contain string variables.
For example, the Microservice API {@link org.apache.juneau.rest.servlet.BasicRestServlet} class defines the
location of the config file as a system property
Spring Boot applications typically define an
Note that properties files are a subset of functionality of INI files (they're basically just INI files with a single default section).
It's therefore possible to use INI-style syntax such as sections in your
Once a config file has been associated with a REST resource, it can be accessed through one of the following: {@link org.apache.juneau.rest.RestContext.Builder#getConfig()} method. It can also be access by passing in a {@link org.apache.juneau.config.Config} bean to any of your REST OP methods.
A common usage is to use this method to initialize fields in your servlet.
Another common usage is to refer to config properties through
It's even possible to reference request-level variables in your config file if you use {@link org.apache.juneau.rest.RestRequest#getConfig()} to access the config file:
You can even add resource bundles into the mix:
In the previous examples, there were several cases where embedded variables were contained within annotation values:
Variables take the form
There are two distinct groups of variables:
The following is the default list of supported variables.
Module | Class | Pattern | Initialization time | Request time | Examples |
---|---|---|---|---|---|
juneau-svl | {@link org.apache.juneau.svl.vars.EnvVariablesVar} | $E{key[,default]} | yes | yes | $E{PATH} |
{@link org.apache.juneau.svl.vars.SystemPropertiesVar} | $S{key[,default]} | yes | yes | $S{java.home} | |
{@link org.apache.juneau.svl.vars.ArgsVar} | $A{key[,default]} | yes | yes | $A{foo,null} | |
{@link org.apache.juneau.svl.vars.ManifestFileVar} | $MF{key[,default]} | yes | yes | $MF{Main-Class} | |
{@link org.apache.juneau.svl.vars.IfVar} | $IF{arg,then[,else]} | yes | yes | $IF{$S{my.boolean.property},foo,bar} | |
{@link org.apache.juneau.svl.vars.SwitchVar} | $SW{arg,p1:then1[,p2:then2...]} | yes | yes | $SW{$S{os.name},*win*:Windows,*:Something else} | |
{@link org.apache.juneau.svl.vars.CoalesceVar} | $CO{arg1[,arg2...]} | yes | yes | $CO{$S{my.property},$E{my.property},n/a} | |
{@link org.apache.juneau.svl.vars.PatternMatchVar} | $PM{arg,pattern} | yes | yes | $PM{$S{os.name},*win*} | |
{@link org.apache.juneau.svl.vars.NotEmptyVar} | $NE{arg} | yes | yes | $NE{$S{foo}} | |
{@link org.apache.juneau.svl.vars.UpperCaseVar} | $UC{arg} | yes | yes | $UC{$S{foo}} | |
{@link org.apache.juneau.svl.vars.LowerCaseVar} | $LC{arg} | yes | yes | $LC{$S{foo}} | |
juneau-config | {@link org.apache.juneau.config.vars.ConfigVar} | $C{key[,default]} | yes | yes | $C{REST/staticFiles} |
juneau-rest-server | {@link org.apache.juneau.rest.vars.FileVar} | $F{path[,default]}} | yes | yes | $F{resources/MyAsideMessage.html, Oops not found!} |
{@link org.apache.juneau.rest.vars.ServletInitParamVar} | $I{name[,default]} | yes | yes | $I{my.param} | |
{@link org.apache.juneau.rest.vars.LocalizationVar} | $L{key[,args...]} | no | yes | $L{MyMessage,foo,bar} | |
{@link org.apache.juneau.rest.vars.RequestAttributeVar} | $RA{key1[,key2...]} | no | yes | $RA{attrName} | |
{@link org.apache.juneau.rest.vars.RequestFormDataVar} | $RF{key1[,key2...]} | no | yes | $RF{paramName} | |
{@link org.apache.juneau.rest.vars.RequestHeaderVar} | $RH{key1[,key2...]} | no | yes | $RH{Header-Name} | |
{@link org.apache.juneau.rest.vars.RequestPathVar} | $RP{key1[,key2...]} | no | yes | $RP{pathVAr} | |
{@link org.apache.juneau.rest.vars.RequestQueryVar} | $RQ{key1[,key2...]} | no | yes | $RQ{paramName} | |
{@link org.apache.juneau.rest.vars.RequestSwaggerVar} | $RS{key} | no | yes | $RS{title} | |
{@link org.apache.juneau.rest.vars.RequestVar} | $R{key1[,key2...]} | no | yes | $R{contextPath} | |
{@link org.apache.juneau.rest.vars.SerializedRequestAttrVar} | $SA{contentType,key[,default]} | no | yes | $SA{application/json,$RA{foo}} | |
{@link org.apache.juneau.rest.vars.UrlVar} | $U{uri} | no | yes | $U{servlet:/foo} | |
{@link org.apache.juneau.rest.vars.UrlEncodeVar} | $UE{uriPart} | yes | yes | $U{servlet:/foo?bar=$UE{$RA{bar}} | |
{@link org.apache.juneau.rest.widget.Widget} | $W{name} | no | yes | $W{MenuItemWidget} |
Custom variables can be defined on resources via the following API:
The methods involved with variables are:
The {@link org.apache.juneau.rest.servlet.BasicRestServlet} and {@link org.apache.juneau.rest.servlet.BasicRestObject} classes come with built-in support for serving up static files through the following REST operation:
The static file finder can be accessed through the following methods:
By default, the {@link org.apache.juneau.rest.staticfile.StaticFiles} bean is configured as follows:
StaticFiles
.
Static files can be configured programmatically through the following APIs:
Client version headers are used to support backwards compatibility for breaking REST interface changes. Using them, you're able to return different responses based on which client is making a request.
The APIs involved with defining client version headers are:
One of the most useful features of Juneau is the ability to generate Swagger-based OPTIONS pages for self-documenting designs (i.e. REST interfaces that document themselves).
As described previously, the
Using {@link org.apache.juneau.dto.swagger.ui.SwaggerUI}, we're able to render that JSON as a Swagger user interface:
Any subclass of {@link org.apache.juneau.rest.servlet.BasicRestServlet} and {@link org.apache.juneau.rest.servlet.BasicRestObject} gets an auto-generated Swagger UI when performing an
The underlying mechanics are simple. The {@link org.apache.juneau.rest.servlet.BasicRestServlet#getSwagger(RestRequest)} method returns a {@link org.apache.juneau.dto.swagger.Swagger} bean consisting of information gathered from annotations and other sources. Then that bean is swapped for a {@link org.apache.juneau.dto.swagger.ui.SwaggerUI} bean when rendered as HTML.
Note that to have your resource create Swagger UI, you must either extend from one of the basic resource classes or provide
your own
Let's look at the various parts of the
The top part of the page shows general information about the REST interface:
The information is pulled from the {@link org.apache.juneau.rest.annotation.Rest#swagger() @Rest(swagger)} annotation.
In this particular case, the Swagger is pulled in from a localized Swagger JSON file located in the
{
Note that the {@link org.apache.juneau.rest.vars.FileVar $F} variable allows for request-locale-sensitive name matching so that you can provide localized Swagger information.
The {@link org.apache.juneau.rest.vars.FileVar $F} variable simply expands to a string to fill the {@link org.apache.juneau.rest.annotation.Swagger#value() @Swagger(value)} annotation. You could equivalently embed JSON directly into your annotation like so:
However, a more typical (and less error-prone) scenario is to define all of your Swagger as annotations:
All annotations support SVL Variables, so you could for example pull localized strings from resource bundles using {@link org.apache.juneau.rest.vars.LocalizationVar $L} variables.
A third option is to define your Swagger information in your {@link org.apache.juneau.rest.annotation.Rest#messages @Rest(messages)} resource bundle using predefined Swagger keywords:
Information defined in multiple locations are merged into a single set of data. When the same information is provided in multiple locations, the following order-of-precedence is used:
Tags allow you to group operations into general categories.
In the user interface, these can be expanded/collapsed by clicking on the tag sections.
In the example below, the
Tags are also defined in the
The annotation-only approach is shown here:
swagger=
Tags are associated with operations using the {@link org.apache.juneau.rest.annotation.OpSwagger#tags() @OpSwagger(tags)} annotation:
Operations can be mapped to multiple tags.
Tags are optional. Operations not mapped to tags are listed in the UI before tagged operations.
For example, the
The following shows the annotations defined on the
Methods marked as deprecated will show up as deprecated in the Swagger UI:
Expanding operations shows you a list of parameters:
Parameter information can be defined in a couple of ways. The typical way is through annotations on parameters
being passed to your
Another option is to specify your parameter information in the
This information could have also been defined in the Swagger JSON for the resource as well.
The parameter section contains information about the request body as well for PUT and POST methods, as shown here:
The definition of this method is shown here:
Note that the schema information on the body parameter is automatically detected if not provided.
Under the input parameters are listed the possible responses for the resource:
The
Note that additional responses can be specified by throwing exceptions annotated with the {@link org.apache.juneau.http.annotation.Response @Response} annotation such as this one:
Like input parameters, the Swagger for responses can be define in multiple locations such as:
The {@link org.apache.juneau.jsonschema.JsonSchemaGenerator.Builder#useBeanDefs()} setting can be used to reduce the size of your
generated Swagger JSON files by creating model definitions for beans and referencing those definitions through
This setting is disabled by default but can be set on the {@link org.apache.juneau.rest.RestContext.Builder} object:
In the Swagger UI, this causes bean definitions to show up in the Models section at the bottom of the page:
In the generated Swagger JSON, embedded schema information for beans will be replaced with references such as the one shown below for the
{
Note that this does not affect how the information is rendered for that bean in the Swagger UI:
The look-and-feel of the Swagger UI is controlled via a single CSS file:
In the microservice template, this file is located in the
{@link org.apache.juneau.rest.annotation.Rest}-annotated classes get automated timing and error statistic information for all
If you extend from {@link org.apache.juneau.rest.servlet.BasicRestServlet} or {@link org.apache.juneau.rest.servlet.BasicRestObject}, then the statics are made available through the REST interface via the following method:
Rendered in a browser, it looks like this:
The default REST configuration provides a link to the stats in the navlinks section of the page:
The exception hash shown is the same hash that is shown in the log file and provides a quick way of locating the exception in the logs.
Programmatic access to the statistics are provided via the following methods:
The {@link org.apache.juneau.html.annotation.HtmlDocConfig @HtmlDocConfig} annotation is used to customize the HTML view of POJOs serialized by {@link org.apache.juneau.html.HtmlDocSerializer}.
It's used in the following locations:
For example, the following shows setting the title on a page:
The purpose of this annotation is to populate the HTML document view which by default consists of the following structure:
The outline above is controlled by the {@link org.apache.juneau.html.HtmlDocTemplate} interface which can be overridden via the {@link org.apache.juneau.html.annotation.HtmlDocConfig#template() @HtmlDocConfig(template)} annotation.
The
SVL variables can be used in any of these annotations:
An important distinction needs to be made about the HTML representations produced by the REST API. These should not be considered User Interfaces but rather Developer Interfaces.
UIs should hide the end-user from the underlying architecture. The audience generally consists of non-technical people not interested in how the UI works.
DIs, on the other hand, should NOT hide the end-user from the underlying architecture. Instead, it's a thin veneer over the REST interface with the following goals:
As a result, the following guidelines are recommended:
The {@link org.apache.juneau.rest.widget.Widget} class allows you to add arbitrary HTML, CSS, and Javascript
to HTML pages.
They are registered in the following location:
The
The HTML content returned by the {@link org.apache.juneau.rest.widget.Widget#getHtml(RestRequest,RestResponse) getHtml(RestRequest,RestResponse)}
method is added wherever the
The CSS returned by {@link org.apache.juneau.rest.widget.Widget#getScript(RestRequest,RestResponse) getScript(RestRequest,RestResponse)} is added to the style section in the page header.
The Javascript returned by {@link org.apache.juneau.rest.widget.Widget#getScript(RestRequest,RestResponse) getScript(RestRequest,RestResponse)} is added to the script section in the page header.
The following examples shows how to associate a widget with a REST method and then have it rendered in the links
and aside section of the page.
It shows an example of a widget that renders an image located in the
The {@link org.apache.juneau.rest.widget} package contains predefined reusable widgets.
{@link org.apache.juneau.rest.widget.MenuItemWidget} is an abstract class for rendering menu items with drop-downs. It defines some simple CSS and Javascript for enabling drop-down menus in the nav section of the page (although nothing keeps you from using it in an arbitrary location in the page).
The script specifies a
Subclasses implement the following two methods:
For example, to render a link that brings up a simple dialog in a div tag:
The HTML content returned by the {@link org.apache.juneau.rest.widget.MenuItemWidget#getHtml(RestRequest,RestResponse) getHtml(RestRequest,RestResponse)} method is added where the
{@link org.apache.juneau.rest.widget.ContentTypeMenuItem} is a predefined Widget that returns back a list of hyperlinks for rendering the contents of a page in a variety of content types.
The variable it resolves is
An example of this widget can be found in the
It renders the following popup-box:
{@link org.apache.juneau.rest.widget.QueryMenuItem} is a predefined Widget that returns a menu-item drop-down form for entering search/view/sort arguments.
The variable it resolves is
This widget is designed to be used in conjunction with the {@link org.apache.juneau.rest.converter.Queryable} converter, although implementations can process the query parameters themselves if they wish to do so by using the {@link org.apache.juneau.rest.httppart.RequestQueryParams#getSearchArgs()} method to retrieve the arguments and process the data themselves.
An example of this widget can be found in the
It renders the following popup-box:
Tooltips are provided by hovering over the field names.
When submitted, the form submits a GET request against the current URI with special GET search API query parameters.
(e.g.
{@link org.apache.juneau.rest.widget.ThemeMenuItem} is a predefined Widget that returns back a list of hyperlinks for rendering the contents of a page in the various default styles.
The variable it resolves is
An example of this widget can be found in the
{@link org.apache.juneau.rest.widget.PoweredByJuneau} is a predefined Widget that places a powered-by-Juneau message on a page.
The variable it resolves is
It produces a simple Apache Juneau icon floating on the right.
Typically it's used in the footer of the page, as shown below in the
It renders the following image:
{@link org.apache.juneau.rest.widget.Tooltip} is a predefined template for adding tooltips to HTML5 bean constructs, typically in menu item widgets.
The following examples shows how tooltips can be added to a menu item widget.
The HTML views of POJOs can somewhat be considered a rudimentary User Interface. In reality, a better term for them would be a Developer Interface as they're meant to be used primarily by developers and not end users. Despite that distinction, it is possible to 'brand' the HTML page to whatever you desire.
The sample root page below includes some default branding for Juneau and Apache:
http://localhost:10000/helloWorld
The Juneau REST framework does not provide specific branding support (i.e. there is no concept of a brand icon). Instead, it just uses the existing open-ended API for defining branding via annotations on your REST classes.
The default annotation values use {@link org.apache.juneau.config.vars.ConfigVar $C} variables to pull in values from an optional external configuration file such as the one shown below:
The take-away here is that the "User Interface" is open-ended, lets you define pretty much anything you want through arbitrary HTML, and allows you either hardcode your interface inside annotations or pull them in via string variables from other places such as external config files.
The sample root page renders in the default "devops" look-and-feel:
http://localhost:10000
The sample root page provides a dropdown widget to try out the other default look-and-feels:
For example, the "light" look-and-feel:
http://localhost:10000/?stylesheet=styles%2Flight.css
And the "dark" look-and-feel:
http://localhost:10000/?stylesheet=styles%2Fdark.css
The stylesheet URL is controlled by the {@link org.apache.juneau.html.annotation.HtmlDocConfig#stylesheet() @HtmlDocConfig(stylesheet)} annotation. The {@link org.apache.juneau.rest.config.BasicUniversalConfig} interface defines the stylesheet served up as a static file:
The
To provide your own stylesheet, simply override the stylesheet attribute and point to a different file:
You can try out different stylesheets by passing in a
In case you're curious about how the menu item works, it's defined via a widget:
The
The REST APIs provides supports enabling logging of HTTP requests and responses through the following annotations:
Debug mode enables the following:
The possible annotation values are:
These annotations support SVL variables, so it's possible to define them as a system property for example.
The {@link org.apache.juneau.rest.annotation.Rest#debugOn() @Rest(debugOn)} annotation can also be used
to enable debugging. It takes a comma-delimited list of key-value pairs, the keys
being class or method names, and the values being one of
The primary advantage of {@link org.apache.juneau.rest.annotation.Rest#debugOn() @Rest(debugOn)} is that you can control debugging externally such as through a system property or environment variable:
Debugging can also be enabled programmatically with the use of the following APIs:
HTTP calls can be logged with the following levels of granularity:
The following examples show the output format for each detail type:
WARNING: [500] HTTP POST /foo?foo=bar
WARNING: === HTTP Call (incoming) =================================================== [500] HTTP POST /foo?foo=bar Request length: 3 bytes Response code: 500 Response length: 3 bytes Exec time: 20ms ---Request Headers--- Foo: bar ---Response Headers--- Foo: bar Content-Type: text/plain === END ===================================================================
WARNING: === HTTP Call (incoming) =================================================== [500] HTTP POST /foo?foo=bar Request length: 3 bytes Response code: 500 Response length: 3 bytes Exec time: 20ms ---Request Headers--- Foo: bar ---Response Headers--- Foo: bar Content-Type: text/plain ---Request Content UTF-8--- Foo ---Request Content Hex--- 46 6F 6F ---Response Content UTF-8--- Foo ---Response Content Hex--- 46 6F 6F === END ===================================================================
The interface responsible for generating the log entries is {@link org.apache.juneau.rest.logger.CallLogger} and the default is {@link org.apache.juneau.rest.logger.BasicCallLogger} which produces the output above. It can be changed through any of the following:
The call logger uses logging rules to map requests to logging detail levels. By default, these are the logging rules:
RestLogger
.
Thrown exceptions get logged with a stack trace hash and a counter like below:
WARNING: [500,9b85cc96.13] HTTP POST /foo?foo=bar
Stack trace hashes are controlled by the {@link org.apache.juneau.rest.stats.ThrownStore} bean which is configured via the following:
By default, a 200 (OK) status is automatically set as the HTTP status when a Java method executes successfully.
Other status codes can be generated by throwing a {@link org.apache.juneau.http.response.BasicHttpException} with a specific HTTP status code, or calling {@link jakarta.servlet.http.HttpServletResponse#setStatus(int)}.
Non-OK (200) status codes are automatically triggered by the following conditions:
Unauthorized | A {@link org.apache.juneau.rest.guard.RestGuard guard} prevented the method from being executed | |
Not Found | No matching path patterns were found on any method | |
Method Not Implemented | A path pattern matched but no Java methods were found for the HTTP method | |
Not Acceptable |
A path pattern matched but no Java methods were found with a matching serializer for the
|
|
Precondition Failed | A path pattern matched but no Java methods were found that were not rejected by {@link org.apache.juneau.rest.matcher.RestMatcher matchers} | |
Unsupported Media Type |
A path pattern matched but no Java methods were found with a matching parser for the
|
|
Internal Server Error | The Java method threw an exception other than {@link org.apache.juneau.http.response.BasicHttpException} |
The following URL parameters have special meaning and can be passed in through the URL of the request:
&plainText=true |
Response will always be Useful for debugging. |
&debug=true | Enable debug mode for request. |
&noTrace=true |
If an error occurs, don't log the stack trace to the log file.
Useful for automated JUnit testcases testing error states to prevent the log file from filling up with useless stack traces. |
&method=X |
Overload the HTTP method as a GET parameter (e.g Must be enabled via {@link org.apache.juneau.rest.annotation.Rest#allowedMethodParams() @Rest(allowedMethodParams)} setting. |
&Header-Name=headerValue |
Specify a header value as a GET parameter.
Must be enabled via {@link org.apache.juneau.rest.annotation.Rest#allowedHeaderParams() @Rest(allowedHeaderParams)} setting. |
&body=X |
Pass in the HTTP body content on PUT and POST methods as a UON-encoded GET parameter.
Can be disabled via {@link org.apache.juneau.rest.annotation.Rest#disableContentParam() @Rest(disableContentParam)} setting. |
&x-response-headers=X |
Pass-through headers to the response.
Must be a UON-encoded map of key-value pairs. |
Since REST servlets are basically just
The following code shows how to register your REST servlets in an OSGi
The {@link org.apache.juneau.rest.RestContext} object is the workhorse class for all of the configuration of a single REST resource class. It's by-far the most important class in the REST API.
Every class annotated with
The {@link org.apache.juneau.rest.RestContext.Builder} class extends {@link org.apache.juneau.BeanContext.Builder} allowing you to programmatically set any properties defined on that builder class. It also implements {@link jakarta.servlet.ServletConfig}
To access this object, simply pass it in as a constructor argument or in an INIT hook:
This class is vast. Combined with {@link org.apache.juneau.rest.RestOpContext} (which is the equivalent per-method context), these classes define the entire configuration and workflow of the REST API.
There are multiple ways to programmatically alter how RestContext behaves. The most straightforward are the following builder methods which are direct equivalents to values defined on the {@link org.apache.juneau.rest.annotation.Rest} annotation:
For more complex configurations, access to sub-builders is provided via the following methods:
The programmatic equivalent to the annotated lifecycle methods are below:
The {@link org.apache.juneau.rest.RestOpContext} object is the workhorse class for an individual {@link org.apache.juneau.rest.annotation.RestOp}-annotated method.
Every class annotated with
To access the builder for these objects, simply implement the following init method that will be called for each {@link org.apache.juneau.rest.annotation.RestOp}-annotated method.
There are multiple ways to programmatically alter how RestOpContext behaves. The most straightforward are the following builder methods which are direct equivalents to values defined on the {@link org.apache.juneau.rest.annotation.RestOp} annotation:
For more complex configurations, access to sub-builders is provided via the following methods:
The REST Server API uses the concept of registered response processors for converting objects returned by REST methods or set through {@link org.apache.juneau.rest.RestResponse#setContent(Object)} into appropriate HTTP responses.
By default, REST resource classes are registered with the following response processors:
Custom response processors can be associated with REST resources via the following:
Response processors can be used to process POJOs that cannot normally be handled through Juneau serializers, or because it's simply easier to define response processors for special cases.
The following example shows how to create a response processor to handle special
The REST/RPC (RPC over REST) API allows the creation of client-side remote proxy interfaces for calling methods on server-side POJOs using entirely REST.
The following example shows a remote interface:
The requirements for a remote interface method are:
Throwables with public no-arg or single-arg-string constructors are automatically recreated on the client side when thrown on the server side.
Remote Interface proxies are instantiated on the client side using one of the following methods:
Since we build upon the existing
Here's an example of the above interface being used:
Under the covers, this method call gets converted to a REST POST.
HTTP POST http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook/createPerson(org.apache.juneau.examples.addressbook.Person) Accept: application/json Content-Type: application/json [ { "name":"John Smith", "birthDate":"Aug 1, 1999", "addresses":[ { "street":"My street", "city":"My city", "state":"My state", "zip":12345, "isCurrent":true } ] } ]
Note that the body of the request is an array. This array contains the serialized arguments of the method. The object returned by the method is then serialized as the body of the response.
There are two ways to expose remote interfaces on the server side:
In either case, the proxy communications layer is pure REST. Therefore, in cases where the interface classes are not available on the client side, the same method calls can be made through pure REST calls. This can also aid significantly in debugging, since calls to the remote interface service can be made directly from a browser with no coding involved.
The {@link org.apache.juneau.rest.remote.RrpcServlet} class is a simple specialized servlet with an abstract
The
If you point your browser to the servlet above, you get a list of available interfaces:
http://localhost:10000/remote
Clicking the hyperlinks on each shows you the list of methods that can be invoked on that service.
Note that the
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook
Since
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.AddressBook
Let's see how we can interact with this interface through nothing more than REST calls to get a better idea on how this works. We'll use the same method call as in the introduction. First, we need to create the serialized form of the arguments:
Object[]
That produces the following JSON output:
[
{
name:
Note that in this example we're using JSON. However, various other content types can also be used such as XML, URL-Encoding, UON, or HTML. In practice however, JSON will preferred since it is often the most efficient.
Next, we can use a tool such as Poster to make the REST call. Methods are invoked by POSTing the serialized object array to the URI of the interface method. In this case, we want to POST our JSON to the following URL:
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook/createPerson(org.apache.juneau.examples.addressbook.CreatePerson)
Make sure that we specify the
When we execute the POST, we should see the following successful response whose body contains the returned
From there, we could use the following code snippet to reconstruct the response object from JSON:
String response =
If we alter our servlet to allow overloaded GET requests, we can invoke methods using nothing more than a browser...
For example, to invoke the
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook/getPeople?method=POST
Here we call the
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook/findPerson(int)?method=POST&body=@(3)
When specifying the POST body as a
The hyperlinks on the method names above lead you to a simple form-entry page where you can test passing parameters in UON notation as URL-encoded form posts.
As mention earlier here, Juneau serializers have sophisticated support for transforming relative URIs to absolute form.
The following example shows a REST method that returns a list of URIs of various forms:
When requested as JSON, it produces the following result:
{
URI resolution is controlled by the following settings:
URIs are resolved by both regular and part serializers.
The {@link org.apache.juneau.rest.beans} package contains a set of reusable utility beans meant to help with putting together explorable REST interfaces.
The {@link org.apache.juneau.examples.rest.UtilityBeansResource} class shows how these beans are used. The resource class is hosted in the example REST applications rendered below:
The {@link org.apache.juneau.examples.rest.UtilityBeansResource#getChildDescriptions() getChildDescriptions()} method shows an example of rendering a list of descriptive links for child endpoints.
The {@link org.apache.juneau.examples.rest.UtilityBeansResource#aBeanDescription() aBeanDescription()} method shows an example of rendering simple schema information about an arbitrary bean class.
The {@link org.apache.juneau.examples.rest.UtilityBeansResource#aHyperlink() aHyperlink()} method shows an example of rendering a simple hyperlink.
The {@link org.apache.juneau.examples.rest.UtilityBeansResource#aSeeOtherRoot() aSeeOtherRoot()} method shows an example
of sending a
Clicking on the link will just redirect to this same page.
Typically this is useful for endpoints where you want to redirect back to the servlet root, such as a DELETE.
The {@link org.apache.juneau.examples.rest.HtmlBeansResource} class shows how {@link org.apache.juneau.dto.html5 HTML5 beans} can be used to generate arbitrary HTML on REST endpoints.
The {@link org.apache.juneau.examples.rest.HtmlBeansResource#aTable() aTable()} method shows an example of rendering an HTML table.
The {@link org.apache.juneau.examples.rest.HtmlBeansResource#aDiv() aDiv()} method shows an example of rendering a div tag with mixed content.
The {@link org.apache.juneau.examples.rest.HtmlBeansResource#aForm() aForm()} method shows an example of rendering an HTML form.
juneau-rest-server-springboot-
org.apache.juneau.rest.server.springboot_
The
The Juneau REST servlet APIs are designed to work seemlessly with the Spring Boot framework. The only restriction is that your top-level REST resource must extend from one of the following classes:
These classes are the equivalent to the {@link org.apache.juneau.rest.servlet.BasicRestServlet} and {@link org.apache.juneau.rest.servlet.BasicRestServletGroup} except they hook into the injection framework of Spring Boot to provide resolution of beans (e.g. child resources, various configuration classes).
The
Our root resource servlet serves as a router page. It is defined as follows:
The
Note that the message rendered is coming from our injected message provider:
juneau-rest-client-
org.apache.juneau.rest.client_
Built upon the feature-rich Apache HttpClient library, the Juneau RestClient API adds support for fluent-style REST calls and the ability to perform marshalling of POJOs to and from HTTP parts.
Breaking apart the fluent call, we can see the classes being used:
RestClient.Builder
It additionally provides support for creating remote proxy interfaces using REST as the transport medium.
The classes are closely tied to Apache HttpClient, yet provide lots of additional functionality:
Instances of this class are built using the {@link org.apache.juneau.rest.client.RestClient.Builder} class which can be constructed using the {@link org.apache.juneau.rest.client.RestClient#create() RestClient.create()} method as shown above.
Clients are typically created with a root URI so that relative URIs can be used when making requests. This is done using the {@link org.apache.juneau.rest.client.RestClient.Builder#rootUrl(Object)} method.
The {@link org.apache.juneau.rest.client.RestClient} class creates {@link org.apache.juneau.rest.client.RestRequest} objects using the following methods:
The {@link org.apache.juneau.rest.client.RestRequest} class creates {@link org.apache.juneau.rest.client.RestResponse} objects using the following methods:
The distinction between the two methods is that {@link org.apache.juneau.rest.client.RestRequest#complete() complete()} automatically consumes the response body and {@link org.apache.juneau.rest.client.RestRequest#run() run()} does not. Note that you must consume response bodies in order for HTTP connections to be freed up for reuse! The {@link java.io.InputStream InputStreams} returned by the {@link org.apache.juneau.rest.client.ResponseContent} object are auto-closing once they are exhausted, so it is often not necessary to explicitly close them.
The following examples show the distinction between the two calls:
By default, JSON support is provided for HTTP request and response bodies. Other languages can be specified using any of the following builder methods:
Clients can also support multiple languages:
When using clients with multiple language support, the request language is selected by setting the
Languages can also be specified per-request.
The {@link org.apache.juneau.rest.client.RestClient.Builder} class provides convenience methods for setting common serializer and parser settings.
Other methods are also provided for specifying the serializers and parsers used for lower-level marshalling support:
HTTP parts (headers, query parameters, form data...) are serialized and parsed using the {@link org.apache.juneau.httppart.HttpPartSerializer} and {@link org.apache.juneau.httppart.HttpPartParser} APIs. By default, clients are configured to use {@link org.apache.juneau.oapi.OpenApiSerializer} and {@link org.apache.juneau.oapi.OpenApiParser}. These can be overridden using the following methods:
Per-client or per-request HTTP parts (headers, query/form data, path parameters) can be manipulated via the following methods that return back builders for those parts:
Convenience methods are also provided for quickly adding parts:
The supplier methods are particularly useful for header values whose values may change over time (such as
The request body can either be passed in with the client creator method (e.g. {@link org.apache.juneau.rest.client.RestClient#post(Object,Object) post(uri,body)}), or can be specified via the following methods:
The request body can be any of the following types:
After execution using {@link org.apache.juneau.rest.client.RestRequest#run()} or {@link org.apache.juneau.rest.client.RestRequest#complete()}, the following methods can be used to get the response status:
Equivalent methods with mutable parameters are provided to allow access to status values without breaking fluent call chains.
The assertion method is provided for quickly asserting status codes in fluent calls.
Response headers are accessed through the following methods:
Unlike {@link org.apache.juneau.rest.client.RestResponse#getFirstHeader(String)} and {@link org.apache.juneau.rest.client.RestResponse#getLastHeader(String)}, the {@link org.apache.juneau.rest.client.RestResponse#getHeader(String)}
method returns an empty {@link org.apache.juneau.rest.client.ResponseHeader} object instead of returning
RestResponse
The {@link org.apache.juneau.rest.client.ResponseHeader} class extends from the HttpClient {@link org.apache.http.Header} class and provides several convenience methods:
The {@link org.apache.juneau.rest.client.ResponseHeader#schema(HttpPartSchema)} method allows you to perform parsing of OpenAPI formats for header parts.
Assertion methods are also provided for fluent-style calls:
Note how in the following example, the fluent assertion returns control to the {@link org.apache.juneau.rest.client.RestResponse} object after the assertion has been completed:
The response body is accessed through the following method:
The {@link org.apache.juneau.rest.client.ResponseContent} class extends from the HttpClient {@link org.apache.http.HttpEntity} class and provides several convenience methods:
The response body can only be consumed once unless it has been cached into memory. In many cases, the body is automatically cached when using the assertions methods or methods such as {@link org.apache.juneau.rest.client.ResponseContent#asString()}. However, methods that involve reading directly from the input stream cannot be called twice. In these cases, the {@link org.apache.juneau.rest.client.RestResponse#cacheContent()} and {@link org.apache.juneau.rest.client.ResponseContent#cache()} methods are provided to cache the response body in memory so that you can perform several operations against it.
Assertion methods are also provided for fluent-style calls:
Object assertions allow you to parse the response body into a POJO and then perform various tests on that resulting POJO.
The {@link org.apache.juneau.rest.client.RestCallHandler} interface provides the ability to provide custom handling of requests.
Note that there are other ways of accomplishing this such as extending the {@link org.apache.juneau.rest.client.RestClient} class and overriding the {@link org.apache.juneau.rest.client.RestClient#run(HttpHost,HttpRequest,HttpContext)} method or by defining your own {@link org.apache.http.protocol.HttpRequestExecutor}. Using this interface is often simpler though.
The {@link org.apache.juneau.rest.client.RestCallInterceptor} API provides a quick way of intercepting and manipulating requests and responses beyond the existing {@link org.apache.http.HttpRequestInterceptor} and {@link org.apache.http.HttpResponseInterceptor} APIs.
One of the more powerful features of the REST client class is the ability to produce Java interface proxies against arbitrary 3rd party REST resources.
The methods to retrieve remote interfaces are:
Annotations are used on the interface and interface methods to specify how to convert input and output to HTTP headers, query parameters, form post parameters, or request/response bodies.
The call above translates to the following REST call:
POST http://localhost:10000/petstore/pets?debug=true HTTP/1.1 Accept: application/json Content-Type: application/json E-Tag: 475588d4-0b27-4f56-9296-cc683251d314 { name: 'Fluffy', price: 9.99 }
The
The {@link org.apache.juneau.http.remote.Remote @Remote} annotation is used on your interface class to identify it as a REST proxy interface.
The
The {@link org.apache.juneau.http.remote.Remote#path @Remote(path)} annotation is used to define the HTTP path of the REST service.
The path can be an absolute path to your REST service.
PetStore
VarResolver.DEFAULT can also be used in the path.
When a relative path is specified, it's relative to the root-url defined on the
RestClient
When no path is specified, the root-url defined on the
RestClient
The {@link org.apache.juneau.http.remote.Remote#headers @Remote(headers)} and {@link org.apache.juneau.http.remote.Remote#headerList @Remote(headerList)} annotations are used to add headers on all requests.
The {@link org.apache.juneau.http.remote.Remote#version @Remote(version)} and {@link org.apache.juneau.http.remote.Remote#versionHeader @Remote(versionHeader)} annotations are used to specify the client-side version of this interface that can be used on the server side to perform version-specific handling.
This can be used in conjunction with the server-side client-versioning support.
The {@link org.apache.juneau.http.remote.RemoteOp @RemoteOp} annotation is applied to methods
of
Specialized sub-annotations are provided for common HTTP methods:
The HTTP method and path are mapped to a Java method using the
The Java method name can be anything.
In such cases,
For example, the
In such cases, the
Method names matching the following pattern are assumed to be implying the HTTP method name:
(get|put|post|delete|options|head|connect|trace|patch).*
do(?i)(get|put|post|delete|options|head|connect|trace|patch)
Java method name | Inferred HTTP method | Inferred HTTP path |
---|---|---|
getPet() | GET | /pet |
get() | GET | / |
postPet() | POST | /pet |
fooPet() | [default] | /fooPet |
doGet() | GET | / |
doGET() | GET | / |
doFoo() | [default] | /doFoo |
The return type of the Java methods of can be any of the following:
If you're only interested in the HTTP status code of the response, you can use the {@link org.apache.juneau.http.remote.RemoteOp#returns() returns} annotation with a value of {@link org.apache.juneau.http.remote.RemoteReturn#STATUS STATUS}:
If your
The {@link org.apache.juneau.http.annotation.Content @Content} annotation can be applied to arguments of
The argument can be any of the following types:
OpenAPI schema based serialization can be used by using the {@link org.apache.juneau.oapi.OpenApiSerializer} class.
See OpenAPI Serializers for information about supported data types in OpenAPI serialization.
If your
The {@link org.apache.juneau.http.annotation.FormData @FormData} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
Multi-part arguments (i.e. those with name ==
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Query @Query} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
Multi-part arguments (i.e. those with name ==
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Header @Header} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
Multi-part arguments (i.e. those with name ==
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Path @Path} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
Multi-part arguments (i.e. those with name ==
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Request @Request} annotation can be applied to a type of a
PetStore
The
The annotated methods must be no-arg and public. They can be named anything.
Any of the following annotations can be used on the methods:
The behavior and functionality of all of the annotations are the same as if they were used on method arguments directly. This means full support for OpenAPI serialization and validation.
Annotations on methods are inherited from parent classes and interfaces. For example, the request bean above could have defined annotations in an interface to keep them clear from the implementation:
The {@link org.apache.juneau.http.annotation.Response @Response} annotation can be applied to types returned by
The
PetStore
The annotated methods must be no-arg. They can be named anything.
Any of the following annotations can be used on the methods:
The behavior and functionality of all of the annotations are the same as if they were used on method arguments directly. This means full support for OpenAPI serialization and validation.
A common coding practice is to use the same Java interface to define both your server and client side REST interfaces. The advantage to this approach is that changes that you make to your REST interface can be reflected in both places at the same time, reducing the chances for compatibility mistakes.
What makes this possible is that method-level annotations such as
The general approach is to define your {@link org.apache.juneau.http.remote.Remote @Remote}-annotated interface first. The following example is pulled from the PetStore app:
Next you define the implementation of your interface as a normal Juneau REST resource:
Then use the interface as a remote resource like so:
RestClient client = RestClient.
In the example above, we chose to add the
Note how we didn't need to use the
The following methods provide logging of requests and responses:
The following example shows the results of logging all requests that end with
This produces the following console output:
=== HTTP Call (outgoing) ====================================================== === REQUEST === POST http://localhost/bean ---request headers--- Accept: application/json5 ---request entity--- Content-Type: application/json5 ---request content--- {f:1} === RESPONSE === HTTP/1.1 200 ---response headers--- Content-Type: application/json ---response content--- {f:1} === END =======================================================================",
It should be noted that if you enable request logging detail level {@link org.apache.juneau.DetailLevel#FULL}, response bodies will be cached by default which may introduce a performance penalty.
Additionally, the following method is also provided for enabling debug mode:
Enabling debug mode has the following effects:
Several methods are provided for customizing the underlying HTTP client and client builder classes:
Additionally, all methods on the
Refer to the {@code org.apache.http.client.impl.HttpClientBuilder} docs for more information.
The
The {@link org.apache.juneau.rest.client.RestRequest} and {@link org.apache.juneau.rest.client.RestResponse} objects can also be extended and integrated by overriding the {@link org.apache.juneau.rest.client.RestClient#createRequest(URI, String, boolean)} and {@link org.apache.juneau.rest.client.RestClient#createResponse(RestRequest, HttpResponse, Parser)} methods.
The Juneau REST client itself does not implement any support for authentication. Instead, it delegates it to the underlying Apache HTTP Client interface.
The following sections show how some common authentication mechanisms can be set up using HTTP Client APIs.
The {@link org.apache.juneau.rest.client.RestClient.Builder#basicAuth(String,int,String,String)} method can be used to quickly enable BASIC authentication support.
This is functionally equivalent to the following:
RestClient.Builder
The {@link org.apache.juneau.rest.client.RestClient.Builder} class does not itself provide FORM-based authentication since there is no standard way of providing such support. Typically, to perform FORM-based or other types of authentication, you'll want to create your own subclass of {@link org.apache.juneau.rest.client.RestClient.Builder} and override the {@link org.apache.juneau.rest.client.RestClient.Builder#createHttpClient()} method to provide an authenticated client.
The following example shows an implementation of a client that performs FORM-based authentication against the IBM Jazz platform.
The following example shows an implementation of a client that performs OIDC authentication against the IBM Jazz platform.
juneau-rest-mock-
org.apache.juneau.rest.mock_
The
The {@link org.apache.juneau.rest.mock.MockRestClient} class is used for performing serverless unit testing of {@link org.apache.juneau.rest.annotation.Rest @Rest}-annotated and {@link org.apache.juneau.http.remote.Remote @Remote}-annotated classes.
The {@link org.apache.juneau.rest.mock.MockRestClient} itself extends from {@link org.apache.juneau.rest.client.RestClient} providing it with the rich feature set of that API. The following shows a simple example of invoking a PUT method on a simple REST interface and asserting the correct status code and response body:
Breaking apart the fluent method call above will help you understand how this works.
The concept of the design is simple. The {@link org.apache.juneau.rest.mock.MockRestClient} class is used to create instances of {@link org.apache.juneau.rest.mock.MockServletRequest} and {@link org.apache.juneau.rest.mock.MockServletResponse} which are passed directly to the call handler on the resource class {@link org.apache.juneau.rest.RestOpInvoker#invoke(RestOpSession)}. In effect, you're fully testing your REST API as if it were running in a live servlet container, yet not actually having to run in a servlet container. All aspects of the client and server side code are tested, yet no servlet container is required. The actual over-the-wire transmission is the only aspect being bypassed.
The
The {@link org.apache.juneau.rest.mock.MockRestRequest} object has convenience methods provided to allow you to set properties directly on the underlying {@link jakarta.servlet.http.HttpServletRequest} object. The following example shows how this can be used to directly set roles on the request object to perform security testing.
The {@link org.apache.juneau.rest.mock.MockRestClient} class has a debug mode that will cause your HTTP requests and responses to be sent to the console:
MockRestClient
The {@link org.apache.juneau.rest.mock.MockRestClient} class can also be used for testing of {@link org.apache.juneau.http.remote.Remote}-annotated interfaces against {@link org.apache.juneau.rest.annotation.Rest @Rest}-annotated resources.
juneau-microservice-core-
org.apache.juneau.microservice.core_
Juneau Microservice is an API for creating stand-alone executable jars with automatic support for Juneau configurations and console commands.
Features include:
The Microservice API consists of a base class for defining executable microservices.
Features include:
The Microservice API consists of the following packages and classes:
By itself the Microservice API doesn't provided much functionality but it does provide the basis for the Jetty Microservice described later.
The most-basic creation of a microservice from an entry-point method is shown below:
The lifecycle methods of the {@link org.apache.juneau.microservice.Microservice} class consists of the following:
A typical implementation of an app with lifecycle methods might look like the following:
If your application consists of a single microservice, you can use the {@link org.apache.juneau.microservice.Microservice#getInstance()} method from anywhere in your code:
The {@link org.apache.juneau.microservice.Microservice#startConsole()} and {@link org.apache.juneau.microservice.Microservice#stopConsole()} control the lifecycle of the console commands. Typically you'll want to control these separately from the app so that you can easily restart your application from the console without affecting the console itself.
The lifecycle methods on the {@link org.apache.juneau.microservice.Microservice} class are purposely left non-final so that subclasses can override them to provide customized behavior.
Command-line arguments can be associated with a microservice using the {@link org.apache.juneau.microservice.Microservice.Builder#args(String...)} method.
When specified, the arguments can be retrieved using the {@link org.apache.juneau.microservice.Microservice#getArgs()} method which provides an API for easily accessing command-line arguments using common notation:
Args
Specifying the command-line arguments also makes them available through {@link org.apache.juneau.svl.vars.ArgsVar $A} SVL variables. These can be used in the configuration file and throughout various Juneau annotations.
The {@link org.apache.juneau.microservice.Microservice.Builder#manifest(Object)} method can be used to specify the contents or location of of the main manifest file of the executable jar.
If you do not specify the location/contents of the manifest file, the microservice will attempt to resolve it through the following methods:
If you do manually specify the manifest file, you can pass in any of the following types:
The manifest file can be retrieved using the the {@link org.apache.juneau.microservice.Microservice#getManifest()} method which provides an API for accessing manifest file entries. This method returns an instance of {@link org.apache.juneau.utils.ManifestFile} which extends from {@link org.apache.juneau.collections.JsonMap} allowing you to retrieve entries as any data types supported by that class.
ManifestFile
The manifest is also used for the {@link org.apache.juneau.svl.vars.ManifestFileVar $MF} SVL variable.
The following methods can be used to define the configuration for your microservice using the powerful Config API:
If you do not specify any of this information, we attempt to resolve it through the following methods:
If no configuration file is found, and empty in-memory configuration is used.
The {@link org.apache.juneau.microservice.Microservice.Builder#configName(String) configName(String)} method allows you to explicitly specify the name of the external configuration file location for your microservice.
Microservice
.
By default, we try to find the file on the file system and then the classpath. If located on the file system, then the configuration is writeable and the microservice can automatically listen for and react to changes in the configuration file on the file system. If located on the classpath, then the configuration can still react to modifications made to it through the Config API but the changes cannot be persisted since the location prevents the file from being modified.
The {@link org.apache.juneau.microservice.Microservice.Builder#configStore(ConfigStore) configStore(ConfigStore)} method can be used to explicitly specify a configuration store. This can include your own custom configuration store, such as one that's implemented in a relational database.
Microservice
.
The {@link org.apache.juneau.microservice.Microservice.Builder#config(Config) config(Config)} method can be used to explicitly specify a {@link org.apache.juneau.config.Config} file as the microservice configuration. When this method is used, the above two methods are bypassed entirely.
Config
Once the configuration is resolved, it is made as the system default configuration available through the {@link org.apache.juneau.config.Config#getSystemDefault()}.
This in turn allows it to be used by REST resources that reference the system default configuration via the BasicRestConfig interface.
The {@link org.apache.juneau.microservice.Microservice#getConfig()} method can be used to get access to the configuration.
Config
Changes to the configuration file can trigger notifications that can be used to restart your microservice or make various other on-the-fly changes. This can be accomplished by either overriding the {@link org.apache.juneau.microservice.Microservice#onConfigChange(ConfigEvents)} or implementing a listener and using the {@link org.apache.juneau.microservice.MicroserviceListener#onConfigChange(Microservice,ConfigEvents)} methods. These will be described in detail later.
As a convenience, the
The Microservice API incorporates the Simple Variable Language API.
The variable resolver can be augmented through the following methods:
A typical usage pattern is shown below:
The variable resolver becomes much more powerful when used in REST resource annotations which will be described latter in juneau-microservice-jetty
By default, support for the following variables are provided:
The Microservice API provides support for simple console commands.
When started, the console renders the following output:
Running class 'Microservice' using config file 'my-microservice.cfg'. List of available commands: exit -- Shut down service restart -- Restarts service help -- Commands help >
The builder methods for controlling the console are as follows:
By default, the supported commands are pulled from the configuration file:
New commands can be added by adding them to the configuration file, or programmatically using the {@link org.apache.juneau.microservice.Microservice.Builder#consoleCommands(ConsoleCommand...) consoleCommands(ConsoleCommand...)} builder method.
The API for defining console commands is shown below:
By default, the console input and output are taken from {@link java.lang.System#in} and {@link java.lang.System#out}. These can be overridden using the {@link org.apache.juneau.microservice.Microservice.Builder#console(Scanner,PrintWriter) console(Scanner,PrintWriter)} method.
As mentioned previously, the lifecycle methods for the {@link org.apache.juneau.microservice.Microservice} class are explicitly defined as non-final so that they can be overridden by subclasses.
In addition to this support, an interface for defining event listeners for your microservice:
This listener API can be used for listening for and reacting to configuration changes on the file system.
Note that the {@link org.apache.juneau.microservice.Microservice#onConfigChange(ConfigEvents)} method can also be overridden to react to configuration changes as well:
juneau-microservice-jetty-
org.apache.juneau.microservice.jetty_
Juneau Microservice Jetty is an API for creating stand-alone executable jars that can be used to start lightweight configurable REST interfaces with all the power of the Juneau REST server and client APIs.
The Jetty Microservice API consists of a combination of the Juneau Core, Server, and Client APIs and an embedded Eclipse Jetty Servlet Container.
The API builds upon the Core Microservices classes to produce easy-to-create and easy-to-use microservices in a standard Java 1.8+ environment.
The
The most-basic creation of a Jetty microservice from an entry-point method is shown below:
To review, the {@link org.apache.juneau.microservice.Microservice} class contains the following lifecycle methods:
The {@link org.apache.juneau.microservice.jetty.JettyMicroservice} class which extends from {@link org.apache.juneau.microservice.Microservice} provides the following additional lifecycle methods:
The additional lifecycle methods are typically not called directly but are exposed to allow subclasses to provide customized behavior for these events. For this reason, these methods are left as non-final so that they can be overridden.
A typical implementation of an app with lifecycle methods might look like the following:
Similar to {@link org.apache.juneau.microservice.Microservice#getInstance()}, the {@link org.apache.juneau.microservice.jetty.JettyMicroservice#getInstance()} also allows easy access to the microservice:
This section describes how to define a top-level REST resource page and deploy it in our microservice. The example is a router page that serves as a jumping off page to child resources.
When deployed, it looks like this in a browser:
http://localhost:10000
If you click the
http://localhost:10000/helloWorld
...which is generated by this class...
The most-common case for deploying the top-level resource is to use the {@link org.apache.juneau.microservice.jetty.JettyMicroservice.Builder#servlet(Class)} method:
However, there are multiple ways of deploying top-level resources:
The following predefined resource classes are also provided for easy inclusion into your microservice:
In Config, we described how to associate a configuration file with your microservice. In this section we describe how that configuration can be used to customize the behavior or your REST resource classes.
The most common usage for the configuration file is to reference values using the {@link org.apache.juneau.config.vars.ConfigVar $C} variable in annotations.
For example, the {@link org.apache.juneau.rest.config.DefaultConfig} interface that defines the annotations that control the look-and-feel of
classes that extend from {@link org.apache.juneau.rest.servlet.BasicRestServlet} use several
These values in turn are pulled from the external configuration file shown below.
Note that the configuration file can also contain
Configuration files can also be accessed programmatically. There are 3 primary ways of getting access to the config file:
Any initialization-time variables can be used.
Any initialization-time variables can be used.
Additional user-defined variables at the servlet level can be defined by adding a
{@link org.apache.juneau.rest.annotation.RestInit} hook method
and using the org.apache.juneau.rest.RestContext.Builder.vars(Class...) method.
Any initialization-time or request-time variables can be used.
Additional user-defined variables can be defined at this level by overriding the
org.apache.juneau.rest.RestContext.Builder.vars(Class...) method.
That
This particular example is needlessly complex but it gives an idea of how variables can be used recursively to produce sophisticated results
The Jetty microservice comes with a bare-bones
The
SVL variables in the
The HTTP port used is controlled via the following:
JettyMicroservice
.
The first available port is then made available through the system property
The {@link org.apache.juneau.microservice.jetty.JettyMicroservice.Builder#jettyServerFactory(JettyServerFactory)} method is also provided to use your own customized Jetty server.
The Microservice project contains a
These files can be used to tailor the look-and-feel of your microservice.
http://localhost:10000/helloWorld
The REST configuration section of your microservice configuration file can be used to tailor the header and footer on the pages:
The {@link org.apache.juneau.rest.config.DefaultConfig} interface (which defines the default settings for {@link org.apache.juneau.rest.servlet.BasicRestServlet} pulls in this information using {@link org.apache.juneau.config.vars.ConfigVar $C} and {@link org.apache.juneau.rest.vars.UrlVar $U} variables:
Note that the
The theme files are externally accessible and can be modified to produce any look-and-feel you desire.
The microservice still works without the files directory. An embedded
If you're testing out changes in the theme stylesheets, you may want to set the following system property that prevents caching of those files so that you don't need to restart the microservice each time a change is made:
This example shows how the {@link org.apache.juneau.microservice.jetty.JettyMicroservice} class
can be extended to implement lifecycle listener methods or override existing methods.
We'll create a new class
Optionally, you can extend the {@link org.apache.juneau.microservice.jetty.JettyMicroservice.Builder} class as well:
my-jetty-microservice-
The
It includes a combination of the Juneau Core, Server, and Client APIs and all libraries needed to execute in a Java 1.8+ environment.
Follow these instructions to create a new template project in Eclipse.
The important elements in this project are:
At this point, you're ready to start the microservice from your workspace.
The
Go to Run -> Run Configurations -> Java Application -> my-jetty-microservice and click Run. In your console view, you should see the following output:
Running class 'JettyMicroservice' using config file 'mjm.cfg'. Server started on port 10000 List of available commands: exit -- Shut down service restart -- Restarts service help -- Commands help >
Now open your browser and point to
http://localhost:10000
You can enter the command
The
The easiest way to build your microservice is to run the following from the project root.
mvn clean install
Your
To start from a command line, run the following command from inside your
java -jar my-jetty-microservice-1.0.jar
You should see the following console output:
Running class 'JettyMicroservice' using config file 'mjm.cfg'. Server started on port 10000 List of available commands: exit -- Shut down service restart -- Restarts service help -- Commands help >
If you get this error message: java.net.BindException: Address already in use
,
then this microservice is already running elsewhere and so it cannot bind to port 10000.
my-springboot-microservice-
The
It includes a combination of the Juneau Core, Server, and Client APIs and all libraries needed to execute in a Java 1.8+ environment.
One significant difference is that we are not using the Juneau {@link org.apache.juneau.microservice.Microservice} API for our application but instead using the existing Spring Boot API.
Follow these instructions to create a new template project in Eclipse.
The important elements in this project are:
At this point, you're ready to start the microservice from your workspace.
The
Go to Run -> Run Configurations -> Java Application -> my-springboot-microservice and click Run. In your console view, you should see the following output:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.1.RELEASE) ... INFO: Tomcat started on port(s): 8080 (http) with context path '' Dec 21, 2012 12:30:00 AM org.springframework.boot.StartupInfoLogger logStarted INFO: Started App in 1.999 seconds (JVM running for 2.999)
Now open your browser and point to
http://localhost:5000
The
The easiest way to build your microservice is to run the following from the project root.
mvn clean install
Your
To start from a command line, run the following command from inside your
java -jar my-springboot-microservice-1.0.jar
You should see the following console output:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.1.RELEASE) ... INFO: Tomcat started on port(s): 8080 (http) with context path '' Dec 21, 2012 12:30:00 AM org.springframework.boot.StartupInfoLogger logStarted INFO: Started App in 1.999 seconds (JVM running for 2.999)
The
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.
Visit the GitHub project hosting the application.
The project is broken down into the following subprojects:
The Pet Store app is a Spring Boot application that can be started up by running the
Clone the Petstore project on your machine.
git clone https://github.com/apache/juneau-petstore.git
Open terminal inside the project directory and run the below command to start the app.
docker build . -t petstore && docker run -p 5000:5000 petstore
The
The
Both sets of annotations are provided by pulling in the Juneau dependency below:
The
The annotations here are a combination of Juneau annotations for controlling marshalling (
The
Notice how little code is necessary to construct a remote proxy.
The
Notice how cleanly Juneau servlets fit into Spring Boot. No special initializers are required to integrate Juneau with Spring Boot.
The
By extending from
This page renders as follows:
http://localhost:5000
The
Clicking the
http://localhost:5000/petstore
The methods defined in our
After running the
http://localhost:5000/petstore/pet
The
http://localhost:10000/petstore/pet?method=OPTIONS
Since we've defined tags on our annotations, the pet-related operations are all grouped under the
Information for all HTTP parts is automatically generated:
The schema models for POJO models is available in the
Auto-generated examples are available for all supported languages:
For example,
Examples can be derived in a number of ways. In our case, we've defined a static method on our
Similar functionality exists for request bodies as well:
At the bottom of the page is a listing of the POJO models in the app:
Security is always an ongoing concern in any library. If you discover any security vulnerabilities in this code, please refer to the instructions found here:
One common security vulnerability is the ability to create arbitrary Java object instances through crafted
user input. For example, support for constructing POJOs based on an input attribute defining a
fully-qualified class name like
Fortunately, Juneau does not support an open-ended
The following example shows a potential vector that circumvents the restriction above:
Juneau does support something similar to a
POJO types of generalized input are also inferred through swaps. Again, since the POJO types are hardcoded at compile time, these should not be subject to demarshalling vulnerabilities. However, it is possible to circumvent this through your swap implementation as shown below:
All other parsers (JSON, URL-Encoding, MessagePack, etc...) work the same way in determining POJO types, so should be safe from demarshalling vulnerabilities.
When accessing security vulnerabilities of any library, dependent libraries must also be taken into account:
Care must be used when defining new {@link org.apache.juneau.svl.Var Vars} using the SVL API since mistakes could potentially expose system properties, environment variables, or even file system files.
For recap, the SVL support allows you to embed variables of the form
An example of a potential security hole is shown below that could potentially expose any file on a file system through a REST request:
This code is simply echoing the value of the
In reality, the above security hole does not exist because of the following restrictions:
Even though the built-in Juneau variables are safe, special care is needed when defining your own custom variables. If your variable resolves user input in any way, it's HIGHLY recommended that you override the {@link org.apache.juneau.svl.Var#allowNested()} and {@link org.apache.juneau.svl.Var#allowRecurse()} methods to prevent recursive handling of variables.
Denial of service attacks can be alleviated through the {@link org.apache.juneau.rest.annotation.Rest#maxInput() maxInput()} setting. Arbitrarily-large input will trigger an exception before causing out-of-memory errors. The default value for this setting is 100MB.
Since the parsers do not use intermediate DOMs and instead parse directly into Java objects, deeply nested data structures will almost always trigger stack overflow errors long before memory consumption becomes an issue. However, this is NOT true of the RDF parsers that use an intermediate DOM. If parsing RDF, you may want to consider lowering the max-input value above.
The following guide can be used to help migrate your code to v9.0. Note that you can also refer to the Release Notes for changes as well.
Old | New |
---|---|
|
Has been replaced with {@link org.apache.juneau.rest.annotation.RestOp}, {@link org.apache.juneau.rest.annotation.RestGet}, {@link org.apache.juneau.rest.annotation.RestPut}, {@link org.apache.juneau.rest.annotation.RestPost}, {@link org.apache.juneau.rest.annotation.RestDelete}, {@link org.apache.juneau.rest.annotation.RestOptions} |
|
Has been replaced with {@link org.apache.juneau.http.remote.RemoteOp}, {@link org.apache.juneau.http.remote.RemoteGet}, {@link org.apache.juneau.http.remote.RemotePut}, {@link org.apache.juneau.http.remote.RemotePost}, {@link org.apache.juneau.http.remote.RemoteDelete}. |
|
These have been removed in 9.0 due to the removal of the Jena packages (due to security issues). Replace with {@link org.apache.juneau.rest.servlet.BasicRestServlet} and {@link org.apache.juneau.rest.servlet.BasicRestServletGroup}. |
|
Has been renamed to |
|
Has been renamed to {@link org.apache.juneau.http.annotation.Query#def} / {@link org.apache.juneau.http.annotation.FormData#def}. Note however that |
|
Has been renamed to {@link org.apache.juneau.rest.annotation.Rest#defaultRequestHeaders} and added {@link org.apache.juneau.rest.annotation.Rest#defaultResponseHeaders}. |
|
Changed from a string array to a |
Version 5.0 marks a major release milestone for the Juno/JJSON library. It is now available for download from iRAM under the name "Juno (previously JJSON)". The Juno Starters Guide has been updated to reflect new functionality in this release.
Juno 5.0.0.1 is a moderate update.
Juno 5.0.0.2 is a minor update.
Juno 5.0.0.3 is a minor update.
Juno 5.0.0.4 is a minor update.
Juno 5.0.0.5 is a major update.
Juno 5.0.0.6 is a minor update that fixes a small bug in 5.0.0.5.
Juno 5.0.0.7 is a major update.
Juno 5.0.0.8 is a minor update.
Juno 5.0.0.9 is a moderate update.
Juno 5.0.0.10 is a minor update.
Juno 5.0.0.11 is a moderate update.
Juno 5.0.0.12 is a minor update.
Juno 5.0.0.13 is a minor update.
Juno 5.0.0.14 is a major update.
The biggest change is that the
Instead, the existing {@link org.apache.juneau.serializer.Serializer}, {@link org.apache.juneau.parser.Parser}, SerializerGroup, and ParserGroup classes of the core API have been augmented to replace them.
Adoptions will be required if you have previously used these classes.
Juno 5.0.0.15 is a moderate update.
Juno 5.0.0.16 is a minor update.
Juno 5.0.0.17 is a minor update.
Juno 5.0.0.18 is a moderate update.
The biggest change is the introduction of the RdfSerializer class that uses Jena to generate RDF/XML, RDF/XML-ABBREV, N-Tuple, N3, and Turtle output.
This code should be considered prototype-quality, and subject to change in the future.
There are plans of adding an equivalent
The
However, I'm keeping it around, since it's considerably faster and uses far less memory than the Jena-based serializer since it serializes directly from POJOs to RDF/XML.
It may or may not be removed in the future depending on demand.
Juno 5.0.0.19 is a minor update.
Juno 5.0.0.20 is a major update.
Juno 5.0.0.21 is a minor update.
Juno 5.0.0.22 is a minor update.
Juno 5.0.0.23 is a minor update.
Juno 5.0.0.24 is a major update.
Juno 5.0.0.25 is a minor update.
Juno 5.0.0.26 is a minor update.
Juno 5.0.0.27 is a moderate update.
Juno 5.0.0.28 is a moderate update.
Juno 5.0.0.29 is a moderate update.
Juno 5.0.0.30 is a minor update.
Juno 5.0.0.31 is a moderate update.
Juno 5.0.0.32 is a moderate update.
Juno 5.0.0.33 is a moderate update.
Juno 5.0.0.34 is a moderate update.
Juno 5.0.0.35 is a minor update.
Juno 5.0.0.36 is a minor update.
Juno 5.1.0.0 is a major update.
Juno 5.1.0.1 is a minor update.
Juno 5.1.0.2 is a minor update.
Juno 5.1.0.3 is a moderate update.
Juno 5.1.0.4 is a minor update.
Juno 5.1.0.5 is a moderate update.
Juno 5.1.0.6 is a moderate update.
Juno 5.1.0.7 is a moderate update.
Juno 5.1.0.8 is a moderate update, focused primarily on performance improvements.
Juno 5.1.0.9 is a major update. There weren't very many code changes but the source has been made a part of Jazz Foundation. This required some restructuring of the project. The project on Jazz Hub will eventually be discontinued. However, the libraries on IBM Community Source will continue to be updated regularly.
Juno 5.1.0.10 is a moderate update.
Juno 5.1.0.11 is a moderate update.
Juno 5.1.0.12 is a minor update.
Juno 5.1.0.13 is a minor update.
Juno 5.1.0.14 is a moderate update.
The major addition is support for
Juno 5.1.0.15 is a minor update.
Juno 5.1.0.16 is a moderate update.
Juno 5.1.0.17 is a major update.
Juno 5.1.0.18 is a minor update affecting the server component only.
Juno 5.1.0.19 is a minor update in terms of core functionality.
But it introduces a Microservices project for building REST microservices and docker containers.
Juno 5.1.0.20 is a moderate update.
The biggest improvement is the ability to associate external INI config files with REST servlets using the ConfigFile functionality.
Juno 5.2.0.0 is a major update. Major changes have been made to the microservice architecture and config INI file APIs.
Juno 5.2.0.1 is a moderate update.
Juneau 6.0.0 is a major update.
The major change is rebranding from "Juno" to "Juneau" in preparation for donation to the Apache Foundation.
Juneau 6.0.1 is a minor update.
Juneau 6.1.0 is a major update.
In particular, this release cleans up the {@link org.apache.juneau.BeanContext} API to match
the PropertyStore/{@link org.apache.juneau.Context}/Session paradigm
previously used in the serializer and parser APIs.
It also makes several improvements to the HTML and XML serialization support and introduces HTML5 DTO beans.
Juneau 6.2.0 is a major update.
Typically you're going to simply want to use the
Juneau 6.3.0 is a major update with significant new functionality for defining proxy interfaces against arbitrary 3rd-party REST interfaces.
pages=
links=
Juneau 6.3.1 is a minor release.
htmldoc=
htmldoc=
The major change in this release is the project structure.
The library now consists of the following artifacts found in the Maven group
Category | Maven Artifacts | Description | Prereqs |
---|---|---|---|
Juneau Core | juneau-marshall | Serializers and parsers for:
|
|
juneau-marshall-rdf |
Serializers and parsers for:
|
|
|
juneau-dto |
Data Transfer Objects for:
|
|
|
juneau-svl | Simple Variable Language API |
|
|
juneau-config | Configuration file API |
|
|
Juneau REST | juneau-rest-server | REST Servlet API |
|
juneau-rest-server-jaxrs | Optional JAX-RS support |
|
|
juneau-rest-client | REST Client API |
|
|
Juneau Microservice | juneau-microservice-server | REST Microservice Server API |
|
juneau-microservice-template | Developer template project |
|
|
Examples | Core code examples | ||
REST code examples | |||
Juneau All |
Combination of the following:
|
|
This release ups the Java prerequisite to Java 7.
head={
This release is a minor update. It includes the following prereq updates:
Running class 'RestMicroservice' using config file 'examples.cfg'. Server started on port 10000 List of available commands: exit -- Shut down service restart -- Restarts service help -- Commands help echo -- Echo command > help help NAME help -- Commands help SYNOPSIS help [command] DESCRIPTION When called without arguments, prints the descriptions of all available commands. Can also be called with one or more arguments to get detailed information on a command. EXAMPLES List all commands: > help List help on the help command: > help help >
Commands are pluggable and extensible through the config file.
Version 7.1.0 is a major update with major implementation refactoring across all aspects of the product.
7.2.0 is a major release that introduces several significant new features:
org.apache.juneau.parser.ParseException: Expected '[' at beginning of JSON array. At line 80, column 20. While parsing into: currentClass: List<String> currentProperty: required: java.util.List, field=[null], getter=[public java.util.List org.apache.juneau.dto.swagger.SchemaInfo.getRequired()], setter=[public org.apache.juneau.dto.swagger.SchemaInfo org.apache.juneau.dto.swagger.SchemaInfo.setRequired(java.util.Collection)] ---start-- 0075: "name": "body", 0076: "description": "Pet object that needs to be added to the store", 0077: "required": true, 0078: "schema": { 0079: "required": true, 0080: } 0081: } 0082: ], 0083: "responses": { 0084: "405": { 0085: "description": "Invalid input" ---end---
This release contains mostly bug fixes. Code changes have been made to preserve binary backwards compatibility with 7.1.0.
This release contains minor bug fixes and general improvements to the PetStore sample application.
This release cleans up deprecated APIs from the 7.2.0 release and makes significant modifications to the Microservice APIs.
The project structures of the REST, Microservice, and Examples have been modified to fit new Spring Boot integration support. The structure is now as follows:
8.1.0 introduces some significant new features including:
Config annotations are provided for all serializers and parsers:
Juneau 8.1.1 is a minor release but introduces some new features/modifications.
Juneau 8.1.2 is a moderate release.
Juneau 8.1.3 is a moderate release.
JsonParser.
Juneau 8.2.0 is a major release.
The most significant change is the addition of an entirely new RestClient API build from scratch
with near 100% unit test coverage.
The new API is located in the
Juneau 9.0.0 is a major release. Deprecated APIs that have been accumulating over time have been removed.
Major changes include:
New module containing the common REST classes/annotations uses by both the client and server APIs.
These were previously contained within
Juneau 9.0.1 is a minor release to fix [JUNEAU-249] UI does not resolve variables anymore.
In addition, the following dependencies have been updated: