See: Description
Apache Juneau™ is a single cohesive Java ecosystem consisting of the following parts:
Group | Component | Description |
---|---|---|
Juneau Core | juneau-marshall | A universal toolkit for marshalling POJOs to a wide variety of content types using a common framework with no external library dependencies. |
juneau-marshall-rdf | Extended marshalling support for RDF languages. | |
juneau-dto | A variety of predefined DTOs for serializing and parsing languages such as HTML5, Swagger and ATOM. | |
juneau-config | A sophisticated configuration file API. | |
Juneau REST | juneau-rest-server | A universal REST server API for creating Swagger-based self-documenting REST interfaces using POJOs, simply deployed as one or more top-level servlets in any Servlet 3.1.0+ container. |
juneau-rest-server-springboot | Spring boot integration support. | |
juneau-rest-server-jaxrs | JAX/RS integration support. | |
juneau-rest-client | A universal REST client API for interacting with Juneau or 3rd-party REST interfaces using POJOs and proxy interfaces. | |
juneau-rest-mock | Mocking APIs for server-less end-to-end testing of REST server and client APIs. | |
Examples | juneau-examples-core | Juneau Core API examples. |
juneau-examples-rest | Juneau REST API examples. | |
juneau-examples-rest-jetty | Juneau REST API examples using Jetty deployment. | |
juneau-examples-rest-springboot | Juneau REST API examples using Spring Boot deployment. |
Apache Juneau™ is a single cohesive Java ecosystem consisting of the following parts:
Group | Component | Description |
---|---|---|
Juneau Core | juneau-marshall | A universal toolkit for marshalling POJOs to a wide variety of content types using a common framework with no external library dependencies. |
juneau-marshall-rdf | Extended marshalling support for RDF languages. | |
juneau-dto | A variety of predefined DTOs for serializing and parsing languages such as HTML5, Swagger and ATOM. | |
juneau-config | A sophisticated configuration file API. | |
Juneau REST | juneau-rest-server | A universal REST server API for creating Swagger-based self-documenting REST interfaces using POJOs, simply deployed as one or more top-level servlets in any Servlet 3.1.0+ container. |
juneau-rest-server-springboot | Spring boot integration support. | |
juneau-rest-server-jaxrs | JAX/RS integration support. | |
juneau-rest-client | A universal REST client API for interacting with Juneau or 3rd-party REST interfaces using POJOs and proxy interfaces. | |
juneau-rest-mock | Mocking APIs for server-less end-to-end testing of REST server and client APIs. | |
Examples | juneau-examples-core | Juneau Core API examples. |
juneau-examples-rest | Juneau REST API examples. | |
juneau-examples-rest-jetty | Juneau REST API examples using Jetty deployment. | |
juneau-examples-rest-springboot | Juneau REST API examples using Spring Boot deployment. |
Questions via email to dev@juneau.apache.org are always welcome.
Juneau is packed with features that may not be obvious at first. Users are encouraged to ask for code reviews by providing links to specific source files such as through GitHub. Not only can we help you with feedback, but it helps us understand usage patterns to further improve the product.
Juneau started off as a popular internal IBM toolkit called Juno. Originally used for serializing POJOs to and from JSON (at a time when the concept was new), it later expanded in scope to include a variety of content types, and then later REST servlet, client, and microservice APIs. It's use grew to more than 50 projects and was one of the most popular community source projects within IBM.
In June of 2016, the code was donated to the Apache Foundation under the project
We've strived to keep prerequisites to an absolute minimum in order to make adoption as easy as possible.
The library 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-config | Configuration file API |
|
|
juneau-rest | juneau-rest-server | REST Servlet API |
|
juneau-rest-server-springboot | Spring Boot integration support |
|
|
juneau-rest-server-jaxrs | JAX-RS support |
|
|
juneau-rest-client | REST Client API |
|
|
juneau-microservice | juneau-microservice-core | Microservice API |
|
juneau-microservice-jetty | Jetty Microservice API |
|
|
my-jetty-microservice | Developer template project for Jetty-based microservices. |
|
|
my-springboot-microservice | Developer template project for Spring-Boot-based microservices. |
|
|
juneau-examples | juneau-examples-core | Core code examples | |
juneau-examples-rest | REST code examples | ||
juneau-examples-rest-jetty | REST code examples deployed using Jetty | ||
juneau-examples-rest-springboot | REST code examples deployed using Spring Boot | ||
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.
juneau-marshall-8.1.2.jar
org.apache.juneau.marshall_8.1.2.jar
The
It also defines many convenience utility classes used throughout the framework.
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,
The class hierarchy for the serializers (excluding specialized subclasses) are:
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:
The class hierarchy for the parsers (excluding specialized subclasses) are:
Marshalls
are simple pairings of a Serializer
and Parser
with convenience methods for serializing and parsing POJOs.
Marshalls are often cleaner to use on-the-fly since they have simplified names.
The following shows the Json
marshall in action:
Marshalls exist for all supported languages:
There is a separate set of serializers for serializing HTTP parts (query, form-data, headers, path variables, and plain-text request bodies). The distinction is that these are designed to serialize directly to strings based on Open-API schema information.
The class hierarchy for the part serializers are:
HttpPartSerializer
SimplePartSerializer
- Serializes directly to strings.
UonSerializer
- Serializes to UON notation.
OpenApiSerializer
- Serializes using Open-API schema rules.
There is a separate set of parsers for parsing HTTP parts (query, form-data, headers, path variables, and plain-text request bodies). The distinction is that these are designed to parse directly from strings based on Open-API schema information.
The class hierarchy for the part serializers are:
HttpPartParser
SimplePartParser
- Parses directly from strings.
UonParser
- Parses from UON notation.
OpenApiParser
- Parses using Open-API schema rules.
Serializers and parsers have a wide variety of configurable properties. They all extend from the
BeanContextBuilder
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 s = JsonSerializer
.
Configurable settings can also be set declaratively. The following produces the same serializer.
WriterSerializer s = JsonSerializer
.
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
All serializers and parsers extend from the BeanContext
class.
Therefore, the following properties are common to all serializers and parsers:
BeanContext
BEAN_beanClassVisibility
BEAN_beanConstructorVisibility
BEAN_beanDictionary
BEAN_beanFieldVisibility
BEAN_beanFilters
BEAN_beanMapPutReturnsOldValue
BEAN_beanMethodVisibility
BEAN_beansRequireDefaultConstructor
BEAN_beansRequireSerializable
BEAN_beansRequireSettersForGetters
BEAN_beansRequireSomeProperties
BEAN_beanTypePropertyName
BEAN_bpi
BEAN_bpx
BEAN_debug
BEAN_examples
BEAN_fluentSetters
BEAN_ignoreInvocationExceptionsOnGetters
BEAN_ignoreInvocationExceptionsOnSetters
BEAN_ignorePropertiesWithoutSetters
BEAN_ignoreUnknownBeanProperties
BEAN_ignoreUnknownNullBeanProperties
BEAN_implClasses
BEAN_locale
BEAN_mediaType
BEAN_notBeanClasses
BEAN_notBeanPackages
BEAN_pojoSwaps
BEAN_propertyNamer
BEAN_sortProperties
BEAN_timeZone
BEAN_useEnumNames
BEAN_useInterfaceProxies
BEAN_useJavaBeanIntrospector
In addition to the common properties above, the following properties are common to all serializers:
BeanTraverseContext
BEANTRAVERSE_detectRecursions
BEANTRAVERSE_ignoreRecursions
BEANTRAVERSE_initialDepth
BEANTRAVERSE_maxDepth
Serializer
SERIALIZER_addBeanTypes
SERIALIZER_addRootType
SERIALIZER_listener
SERIALIZER_sortCollections
SERIALIZER_sortMaps
SERIALIZER_trimEmptyCollections
SERIALIZER_trimEmptyMaps
SERIALIZER_trimNullProperties
SERIALIZER_trimStrings
SERIALIZER_uriContext
SERIALIZER_uriRelativity
SERIALIZER_uriResolution
WriterSerializer
OutputStreamSerializer
In addition to the common properties above, the following properties are common to all parsers:
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
However, an even easier way to configure serializers and parsers are to used provided specialized
Config annotations are provided for all serializers and parsers:
BeanConfig
CsvConfig
HtmlConfig
HtmlDocConfig
JsoConfig
JsonConfig
JsonSchemaConfig
MsgPackConfig
OpenApiConfig
ParserConfig
PlainTextConfig
RdfConfig
SerializerConfig
SoapXmlConfig
UonConfig
UrlEncodingConfig
XmlConfig
Config annotations defined on classes and methods can be applied to serializers and parsers using the following methods:
BeanContextBuilder
applyAnnotations(Class)
- Apply annotations from class and all parent classes.
applyAnnotations(Method)
- Apply annotations from method and parent methods.
The ObjectMap
and ObjectList
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:
ObjectMap.serializeTo(java.io.Writer)
or
ObjectList.serializeTo(java.io.Writer)
methods.
Serializer
serialize methods.
ObjectMap.toString()
or ObjectList.toString()
methods which will serialize it as Simplified JSON.
Any valid JSON can be parsed into an unstructured model consisting of generic
ObjectMap
and ObjectList
objects.
(In theory, any valid XML can also be parsed into an unstructured model, although this has not been
officially 'tested')
The
Above the serializers and parsers are the SerializerGroup
and
ParserGroup
classes.
These classes allow serializers and parsers to be retrieved by W3C-compliant HTTP
The REST servlet API builds upon the
All the serializers, parsers, and REST server/client classes use the following design pattern:
For example, the class hierarchy for
Each context object in the hierarchy define properties that can be stored in a
The class hierarchy for
The class hierarchy for
The general idea behind a PropertyStore
is to serve as a reusable configuration of an artifact
(such as a serializer) such that the artifact can be cached and reused if the property stores are 'equal'.
For example, two serializers of the same type created with the same configuration will always end up being the same serializer:
This has the effect of significantly improving performance especially if you're creating many serializers and parsers.
The PropertyStoreBuilder
class is used to build up and instantiate immutable
In the example above, the property store being built looks like the following:
PropertyStore ps = PropertyStore
.
Property stores are immutable, comparable, and their hashcodes are calculated exactly one time. That makes them particularly suited for use as hashmap keys, and thus for caching reusable serializers and parsers.
Refer to the PropertyStore
javadoc for a detailed explaination on how
property stores work.
By default, the Juneau framework can serialize and parse a wide variety of POJOs out-of-the-box. However, two special classes are provided tailor how certain Java objects are handled by the framework. These classes are:
BeanFilter
- Transforms that alter the way beans are handled.
PojoSwap
- Transforms that swap non-serializable POJOs with
serializable POJOs during serialization (and optionally vis-versa during parsing).
StringSwap
- Convenience subclass for swaps that convert
objects to strings.
MapSwap
- Convenience subclass for swaps that convert
objects to maps.
Transforms are added to serializers and parsers (and REST clients) using the following configuration properties:
Annotations are also provided for specifying transforms directly on classes and methods (all described in later sections):
Swap
- Used to tailor how non-bean POJOs get interpreted by the framework.
Bean
- Used to tailor how beans get interpreted by the framework.
Beanc
- Maps constructor arguments to property names on beans with read-only properties.
Beanp
- Used to tailor how bean properties get interpreted by the framework.
BeanIgnore
- Ignore classes, fields, and methods from being interpreted as bean or bean components.
NameProperty
- Identifies a setter as a method for setting the name of a POJO as it's known by its parent object.
ParentProperty
- Identifies a setter as a method for adding a parent reference to a child object.
URI
- Used to identify a class or bean property as a URI.
PojoSwaps
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 POJOs 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:
By default, all serializers and parsers have built in
Enumeration
Iterator
Locale
Class
Calendar
- ISO offset date-time.
Date
- Local date-time
Instant
- ISO instant.
ZonedDateTime
- ISO offset date-time.
LocalDate
- ISO local date.
LocalDateTime
- ISO local date-time.
LocalTime
- ISO local time.
OffsetDateTime
- ISO offset date-time.
OffsetTime
- ISO offset time.
Year
- ISO year.
YearMonth
- ISO year-month.
Temporal
- ISO instant.
TimeZone
XMLGregorianCalendar
ZoneId
Various other swaps are provided in the
ByteArraySwap
InputStreamSwap
ReaderSwap
TemporalCalendarSwap
TemporalCalendarSwap.BasicIsoDate
TemporalCalendarSwap.IsoDate
TemporalCalendarSwap.IsoDateTime
TemporalCalendarSwap.IsoInstant
TemporalCalendarSwap.IsoLocalDate
TemporalCalendarSwap.IsoLocalDateTime
TemporalCalendarSwap.IsoLocalTime
TemporalCalendarSwap.IsoOffsetDate
TemporalCalendarSwap.IsoOffsetDateTime
TemporalCalendarSwap.IsoOffsetTime
TemporalCalendarSwap.IsoOrdinalDate
TemporalCalendarSwap.IsoTime
TemporalCalendarSwap.IsoWeekDate
TemporalCalendarSwap.IsoZonedDateTime
TemporalCalendarSwap.Rfc1123DateTime
TemporalDateSwap
TemporalDateSwap.BasicIsoDate
TemporalDateSwap.IsoDate
TemporalDateSwap.IsoDateTime
TemporalDateSwap.IsoInstant
TemporalDateSwap.IsoLocalDate
TemporalDateSwap.IsoLocalDateTime
TemporalDateSwap.IsoLocalTime
TemporalDateSwap.IsoOffsetDate
TemporalDateSwap.IsoOffsetDateTime
TemporalDateSwap.IsoOffsetTime
TemporalDateSwap.IsoOrdinalDate
TemporalDateSwap.IsoTime
TemporalDateSwap.IsoWeekDate
TemporalDateSwap.IsoZonedDateTime
TemporalDateSwap.Rfc1123DateTime
TemporalSwap
TemporalSwap.BasicIsoDate
TemporalSwap.IsoDate
TemporalSwap.IsoDateTime
TemporalSwap.IsoInstant
TemporalSwap.IsoLocalDate
TemporalSwap.IsoLocalDateTime
TemporalSwap.IsoLocalTime
TemporalSwap.IsoOffsetDate
TemporalSwap.IsoOffsetDateTime
TemporalSwap.IsoOffsetTime
TemporalSwap.IsoOrdinalDate
TemporalSwap.IsoTime
TemporalSwap.IsoWeekDate
TemporalSwap.IsoYear
TemporalSwap.IsoYearMonth
TemporalSwap.IsoZonedDateTime
TemporalSwap.Rfc1123DateTime
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:
UUID
Boolean
, Byte
,
Double
, Float
,
Integer
, Long
, Short
, Date
,
Time
, Timestamp
DateFormat
, MessageFormat
,
NumberFormat
, Date
, Level
DatatypeConverter
Class
If you want to force a bean-like class to be serialized as a string, you can use the
@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 BeanSession.getMediaType()
).
The following example shows how an HTML5 form template object can be created that gets serialized as a
populated 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 PojoSwap.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 POJO 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 POJO. 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 POJOs 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 POJO swap is simply an object transform that only implements the swap()
method.
The unswap()
method is simply left unimplemented.
An example of a one-way swaps would be one that allows Iterators
to be serialized as JSON arrays.
It can make sense to be able to render Iterators
as arrays, but in general it's not possible to
reconstruct an 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 ParseException
to be thrown.
@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 POJO by using the @Swaps
annotation:
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 @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.
The @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
@Bean(properties)
annotation.
Bean properties can be excluded using the @Bean(bpx)
annotation.
Bean properties can be sorted alphabetically using @Bean(sort)
The @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 PropertyNamerDLC
will convert property names to
dashed-lowercase, and these will be used as attribute names in JSON and element names in XML.
The @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 @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 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 @Bean(propertyFilter)
annotation
and PropertyFilter
class can be used to perform interception
and inline handling of bean getter and setter calls.
The @Beanp
annotation is used to tailor how
individual bean properties are interpreted by the framework.
The @Beanp(name)
annotation
is used to override the name of the bean property.
The @Name
annotation is a shortcut for specifying a bean property name:
If the BeanContext.BEAN_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.
BeanContext.BEAN_ignoreUnknownBeanProperties
setting to ignore values
that don't fit into existing properties.
The @Beanp(value)
annotation
is a synonym for @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 @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 InstantiationException
when trying to set these fields.
This property must denote a concrete class with a no-arg constructor.
The @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 @Beanp(bpi)
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 @Beanp(format)
annotation specifies a String format for converting a bean property value to a formatted string.
The @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 @Name
annotation can also be used instead of
If neither
The @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 @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 @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 @Beanc
annotation, using builders can often be cleaner.
A typical builder usage is shown below:
MyBean b = MyBean.
The code for such a builder 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:
Builder
interface.
@Builder
annotation on the POJO class.
The second can be accomplished through any of the following:
The BeanFilter
class is the programmatic equivalent to the
@Bean
annotation.
In practice, it's usually simpler to use the @Bean
and
@Beanp
annotations on your bean classes.
However, bean filters make it possible to accomplish the same when you can't add annotations
to existing code.
Bean filters are defined through BeanFilterBuilders
.
In the previous examples, we defined this bean annotation:
The programmatic equivalent would be:
Bean filters are added to serializers and parsers using the following:
For example:
Note that if you use the annotation, you do NOT need to set anything on the serializers/parsers. The annotations will be detected and bean filters will automatically be created for them.
The BeanContextBuilder.beanFilters(Object...)
method also allows you to pass in interfaces.
Any class that's not a subclass of BeanFilterBuilder
get interpreted
as bean interface classes.
These cause bean implementations of those interfaces to only expose the properties defined on the
interface.
Occasionally, you may want to limit bean properties to only those defined on a parent class or interface. This is accomplished through interface filters.
Interface filters are defined through the following:
For example, let's define the following interface and implementation:
Suppose we only want to render the properties defined on our interface, not the implementation. To do so, we can define the following bean filter:
When serialized, the serialized bean will only include properties defined on the interface.
WriterSerializer s = JsonSerializer
.
The BeanContextBuilder.beanFilters(Object...)
method will automatically interpret any
non-
WriterSerializer s = JsonSerializer
.
The annotation equivalent is Bean#interfaceClass()
.
The annotation can be used in a couple of ways.
Using the annotation on an interface will be inherited by all children.
The annotation can be used on parent classes as well. Child beans will only serialize properties defined on the parent class.
Whereas interface filters limit properties defined on child classes, stop filters do the opposite and limit properties defined on parent classes.
Stop classes are defined through the following:
Stop classes are identical in purpose to the stop class specified by Introspector.getBeanInfo(Class, Class)
.
Any properties in the stop class or in its base classes will be ignored during serialization.
For example, in the following class hierarchy, instances of
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 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:
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 @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:
Beanp(dictionary)
- On individual bean properties through the annotation.
Bean(dictionary)
- On all properties on a bean and all subclasses.
BeanContext.BEAN_beanDictionary
- Configuration property on serializers and parsers.
BeanContextBuilder.dictionary(Object...)
- Builder method on serializers and parsers.
The bean dictionary setting can consist of any of the following types:
@Bean(typeName)
.
BeanDictionaryList
containing a collection of bean classes with type name annotations.
BeanDictionaryMap
containing a mapping of type names to classes without type name annotations.
The
Bean(typePropertyName)
- On individual beans through the annotation.
BeanContext.BEAN_beanTypePropertyName
- Configuration property on serializers and parsers.
BeanContextBuilder.beanTypePropertyName(String)
- Builder method on serializers and parsers.
When using the annotation, you'll typically want to define it on an interface class so that it can be inherited by all subclasses.
Serializer.SERIALIZER_addBeanTypes
configuration property.
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
JsonSerializer s = SimpleJsonSerializer.
The following shows what happens when parsing back into the original object.
JsonParser p = JsonParser.
The BeanContext.BEAN_useInterfaceProxies
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
Virtual beans can also be created programmatically using the
Address address = BeanContext.
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 BeanTraverseContext.BEANTRAVERSE_maxDepth
is not reached
first).
If you still want to use the Juneau serializers on such models, Juneau provides the
BeanTraverseContext.BEANTRAVERSE_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 ObjectMap
is recommended since it has many convenience methods
for converting values to various types.
The same is true when parsing collections. You can use any Collection (e.g. ObjectList
is recommended.
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 JsonParser
and UonParser
classes can read continuous streams by using the PARSER_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 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 classes and settings that control the behavior are:
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:
Serializer.SERIALIZER_uriContext
Serializer.SERIALIZER_uriRelativity
UriRelativity.RESOURCE
UriRelativity.PATH_INFO
Serializer.SERIALIZER_uriResolution
UriResolution.ABSOLUTE
UriResolution.ROOT_RELATIVE
UriResolution.NONE
Juneau automatically interprets any URL
and URI
objects as URIs and will
resolve them accordingly.
The @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 |
---|---|
|
@Beanp
|
|
@Beanp(name="*")
|
|
@BeanIgnore
|
@Bean(bpx="...")
|
|
No equivalent annotation, but can be controlled via:
BeanContext.BEAN_beanFieldVisibility
BeanContext.BEAN_beanMethodVisibility
Future annotation support planned. |
|
|
@Beanc
|
No equivalent.
Future support planned. |
|
|
Juneau uses swaps to convert non-serializable object to serializable forms:
@Swap
|
No equivalent annotation, but can be controlled via various settings:
BeanContext
Serializer
Future annotation support planned. |
|
@Bean(bpi="...")
@Bean(sort=x)
|
|
|
Can be replicated using swaps with |
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 PojoSwaps 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 TemporalDateSwap.IsoLocalDateTime class can be used to
serialize Date objects to ISO8601 strings, and parse them back into
Date objects.
|
|||
4a |
2-way swapped to group [1, 2a, 3ac] objects
For example, a swap that converts a Date to a String .
|
|
yes | yes |
4b |
1-way swapped to group [1, 2, 3] objects
For example, a swap that converts an Iterator to a List .
This would be one way, since you cannot reconstruct an 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 Strings
using the toString() method.
|
yes | no |
detectRecursions
to look for and handle these kinds of loops
(by setting these references to 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
TimeZone
- Uses TimeZone.getID()
POJOs are convertible from Strings using any of the following (matched in the specified order):
Exceptions exist for the following classes:
TimeZone
- Uses TimeZone.getTimeZone(String)
Locale
- Uses Locale.forLanguageTag(String)
after replacing Boolean
- Blank and
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.
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 p =
{
{
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 JsonSerializer
class is used to serialize POJOs into JSON.
The JSON serializer provides the following settings:
The following pre-configured serializers are provided for convenience:
The SimpleJsonSerializer
class can be used to serialized POJOs into Simplified JSON notation.
Simplified JSON is identical to JSON except for the following:
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 Simplified JSON and do a simple string comparison.
WriterSerializer ws = SimpleJsonSerializer.
The JsonParser
class is used to parse JSON into POJOs.
The JSON parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The JSON parser supports ALL valid JSON, including:
The @Json
annotation
is used to override the behavior of 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 @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 JsonSchemaSerializer
class for generating JSON-Schema
documents that describe the output generated by the JsonSerializer
class.
This class shares the same properties as JsonSerializer.getSchemaSerializer()
method has been
added for creating instances of schema serializers from the regular serializer instance.
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 p =
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 XmlSerializer
class is used to serialize POJOs into XML.
The XmlDocSerializer
class is the same, but serializes a
The XML serializers provide the following settings:
The following pre-configured serializers are provided for convenience:
The XmlParser
class is used to parse XML into POJOs.
The XML parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The @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
@Bean(dictionary)
and
@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
XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES
setting in Java
forced us to make some hard design decisions that may not be the most elegant.
The @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 @Xml(format)
annotation can be used to tweak
the XML format of a POJO.
The value is set to an enum value of type XmlFormat
.
This annotation can be applied to both classes and bean properties.
The 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 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 XmlFormat.ELEMENT
format can be applied to bean properties
to override the XmlFormat.ATTRS
format applied on the bean
class.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The XmlFormat.ATTRS
format can be applied to a single bean
property of type XmlFormat.ATTR
annotated
properties, but there must not be an overlap in bean property names and map keys.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
|
The 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 @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 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 XmlFormat.MIXED
format is similar to
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 serialization, but it is needed during parsing to resolve bean
types.
The XmlFormat.MIXED_PWS
format identical to
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 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 XmlFormat.TEXT
format is similar to
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 XmlFormat.TEXT_PWS
is the same except whitespace is
preserved in the output.
Data type | JSON example | Without annotations | With annotations |
---|---|---|---|
|
{
a: |
The XmlFormat.XMLTEXT
format is similar to
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 @Xml
annotation, or can be defined at the package level with the @XmlSchema
annotation.
Below shows it defined at the package level:
Person p =
Now when we run this code, we'll see namespaces added to our output:
Enabling the XmlSerializer.XML_addNamespaceUrisToRoot
setting 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:
One important property on the XML serializer class is
XML_autoDetectNamespaces
.
This property tells the serializer to 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.
The following code will produce the same output as before, but will perform slightly better since it avoids this pre-scan step.
Juneau provides the XmlSchemaSerializer
class for generating XML-Schema
documents that describe the output generated by the XmlSerializer
class.
This class shares the same properties as XmlSerializer.getSchemaSerializer()
method
has been added.
XML-Schema requires a separate file for each namespace.
Unfortunately, does not mesh well with the Juneau serializer architecture which serializes to single writers.
To get around this limitation, the schema serializer will produce a single output, but with multiple
schema documents separated by the null character (
Lets start with an example where everything is in the same namespace. We'll use the classes from before, but remove the references to namespaces. Since we have not defined a default namespace, everything is defined under the default Juneau namespace.
The code for creating our POJO model and generating XML Schema is shown below:
Now if we add in some namespaces, we'll see how multiple namespaces are handled.
The schema consists of 4 documents separated by a
For convenience, the #getValidator(SerializerSession,Object)
method is provided to create a
Validator
using the input from the serialize method.
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 HtmlSerializer
class is used to serialize POJOs into HTML.
The HtmlDocSerializer
class is the same, but wraps the serialized POJO
inside a document template consisting of header, nav, aside, and footer sections.
The HTML serializers provides the following settings:
The following pre-configured serializers are provided for convenience:
The HtmlParser
class is used to parse HTML into POJOs.
They can also parse the contents produced by HtmlDocSerializer
.
The HTML parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The @Html
annotation can be used to customize how POJOs are serialized to HTML on a per-class/field/method basis.
The @Html(link)
annotation adds a hyperlink to a bean property when rendered as HTML.
The @Html(anchorText)
annotation is used to specify the anchor text of a hyperlink.
The @Html(format)
annotation is used to specify what format to use for HTML elements.
For example, the HTML beans defined in the org.apache.juneau.dto.html5
package use
The @Html(noTableHeaders)
annotation is used to prevent beans from being serialized with table headers.
The @Html(noTables)
annotation is used to force beans to be serialized as trees instead of tables
The @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
HtmlDocSerializer
is an extension of HtmlSerializer
that wraps serialized POJOs in a complete HTML document.
This class is used extensively in the creation of POJO-based user interfaces in the REST API.
The HTMLDOC_template
setting defines
a template for the HTML page being generated.
The default template is described next.
The BasicHtmlDocTemplate
class defines a default template for HTML documents
created by HtmlDocSerializer
.
The HTML document created by this template consists of the following structure:
Custom page templates can be created by implementing the HtmlDocTemplate
interface and associating it with your HtmlDocSerializer
using the HTMLDOC_template
setting.
The interface implementation is open-ended allowing you to define the contents of the page any way you wish.
The HtmlSchemaSerializer
class is the HTML-equivalent to the
JsonSchemaSerializer
class.
It's used to generate HTML versions of JSON-Schema documents that describe the output generated by the
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 p =
(
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 UonSerializer
class is used to serialize POJOs into UON.
The UON serializers provides the following settings:
The following pre-configured serializers are provided for convenience:
The UonParser
class is used to parse UON into POJOs.
The UON parser provides the following 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 p =
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 UrlEncodingSerializer
class is used to serialize POJOs into URL-Encoding.
The URL-Encoding serializers provides the following settings:
The following pre-configured serializers are provided for convenience:
The UrlEncodingParser
class is used to parse URL-Encoding into POJOs.
The URL-Encoding parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The @UrlEncoding
annotation
is used to override the behavior of UrlEncodingSerializer
on individual bean classes or properties.
The expandedParams
setting is
used to force bean properties of type array or Collection to be expanded into multiple key/value pairings.
It's identical in behavior to using the UrlEncodingSerializer.URLENC_expandedParams
and UrlEncodingParser.URLENC_expandedParams
properties, but applies to only individual bean properties.
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 MsgPackSerializer
class is used to serialize POJOs into MessagePack.
The MessagePack serializer provides the following settings:
The following pre-configured serializers are provided for convenience:
The MsgPackParser
class is used to parse MessagePack into POJOs.
The MessagePack parser provides the following 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:
OpenApiSerializer
- Converts POJOs to strings.
OpenApiParser
- Converts strings to POJOs.
HttpPartSchema
- Defines the schema for your POJO.
The 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 |
Calendar |
|
No intermediate type. (serialized directly to/from POJO) |
||
empty | String |
|
empty | Boolean |
|
Integer |
||
Long |
||
Float |
||
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 OpenApiSerializer
class is used to convert POJOs to HTTP parts.
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:
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:
HttpPartSerializer s = OpenApiSerializer.
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:
HttpPartSchema ps =
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 OpenApiParser
class is used to convert HTTP parts back into POJOs.
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 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 input =
Note that serializing into generic
We can also parse into Maps as well:
String input =
The
Most variables can be recursively nested within the varKey (e.g.
The VarResolver
class is used to resolve variables.
The VarResolver.DEFAULT
resolver is a reusable instance of this class
configured with the following basic variables:
SystemPropertiesVar
- EnvVariablesVar
- The following logic variables are also provided:
IfVar
- SwitchVar
- CoalesceVar
- PatternMatchVar
- PatternReplaceVar
- PatternExtractVar
- NotEmptyVar
- UpperCaseVar
- LowerCaseVar
- LenVar
- SubstringVar
-
The following shows how variables can be arbitrarily nested...
Variables are defined through the 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 Var
class:
Var
- Superclass of all vars.
SimpleVar
- Superclass of all vars that return strings.
DefaultingVar
- Variables that define a default value if the resolve method returns null.
MapVar
- Variables that pull values from maps.
MultipartVar
- Variables that consist of 2 or more comma-delimited arguments.
StreamedVar
- Superclass of all vars that stream their value to writers.
The following is the list of default variables defined in all modules:
Module | Class | Pattern |
---|---|---|
juneau-svl | EnvVariablesVar |
$E{key[,default]} |
SystemPropertiesVar |
$S{key[,default]} | |
ArgsVar |
$A{key[,default]} | |
ManifestFileVar |
$MF{key[,default]} | |
IfVar |
$IF{arg,then[,else]} | |
SwitchVar |
$SW{arg,pattern1:then1[,pattern2:then2...]} | |
CoalesceVar |
$CO{arg1[,arg2...]} | |
PatternMatchVar |
$PM{arg,pattern} | |
PatternReplaceVar |
$PR{arg,pattern,replace} | |
PatternExtractVar |
$PE{arg,pattern,groupdIndex} | |
NotEmptyVar |
$NE{arg} | |
UpperCaseVar |
$UC{arg} | |
LowerCaseVar |
$LC{arg} | |
LenVar |
$LN{arg[,delimiter]} | |
SubstringVar |
$ST{arg,start[,end]} | |
HtmlWidgetVar |
$W{name} | |
juneau-config | ConfigVar |
$C{key[,default]} |
juneau-rest-server | FileVar |
$F{path[,default]}} |
ServletInitParamVar |
$I{name[,default]} | |
LocalizationVar |
$L{key[,args...]} | |
RequestAttributeVar |
$RA{key1[,key2...]} | |
RequestFormDataVar |
$RF{key1[,key2...]} | |
RequestHeaderVar |
$RH{key1[,key2...]} | |
RequestHeaderVar |
$RI{key} | |
RequestPathVar |
$RP{key1[,key2...]} | |
RequestQueryVar |
$RQ{key1[,key2...]} | |
RequestVar |
$R{key1[,key2...]} | |
SerializedRequestAttrVar |
$SA{contentType,key[,default]} | |
SwaggerVar |
$SS{key1[,key2...]} | |
UrlVar |
$U{uri}> | |
UrlEncodeVar |
$UE{uriPart} | |
WidgetVar (deprecated) |
$W{name} |
The main class for performing variable resolution is VarResolver
.
Two methods are provided for resolving variables:
VarResolver
resolve(String)
- Resolves variables and returns the results as a simple string.
resolveTo(String,Writer)
- Resolves variables and sends results to a writer.
Var resolvers can rely on the existence of other objects.
For example, ConfigVar
relies on the existence of a Config
.
This is accomplished through the following:
The following two classes are identical in behavior except for which objects they can access:
VarResolver
- Has access to context objects only.
VarResolverSession
- Has access to context and session objects.
Context and session objects are set through the following methods:
VarResolverBuilder.contextObject(String,Object)
- Context objects.
VarResolverSession.sessionObject(String,Object)
- Session objects.
VarResolver.createSession(Map)
- Session objects.
Both kinds of objects are accessible through the following method:
Var resolvers can be cloned and extended by using the VarResolver.builder()
method.
Cloning a resolver will copy it's Var
class names and context objects.
VarResolver.DEFAULT
is a reusable variable resolver with default support for the following variables:
SystemPropertiesVar
EnvVariablesVar
ArgsVar
ManifestFileVar
SwitchVar
IfVar
CoalesceVar
PatternMatchVar
PatternReplaceVar
PatternExtractVar
UpperCaseVar
LowerCaseVar
NotEmptyVar
LenVar
SubstringVar
StackOverflowErrors
if
your nested variables result in a recursive loop (e.g. the environment variable
BEANTRAVERSE_detectRecursions
option on the Serializer
class can cause a performance penalty of
around 20%.
Parser
methods that take in ClassMeta
parameters are slightly faster than methods that
take in Class
or Object
parameters, since the latter methods involve
hash lookups to resolve to ClassMeta
parameters.
juneau-marshall-rdf-8.1.2.jar
org.apache.juneau.marshall.rdf_8.1.2.jar
The
Juneau supports serializing and parsing arbitrary POJOs to and from the following RDF formats:
The serializers and parsers work identically to those in
The RdfSerializer
class is the top-level class for all Jena-based serializers.
Language-specific serializers are defined as inner subclasses of the
RdfCommon
RDF_arp_embedding
RDF_arp_err_
RDF_arp_errorMode
RDF_arp_ign_
RDF_arp_iriRules
RDF_arp_warn_
RDF_collectionFormat
RDF_juneauBpNs
RDF_juneauNs
RDF_language
RDF_looseCollections
RDF_n3_abbrevBaseUri
RDF_n3_indentProperty
RDF_n3_minGap
RDF_n3_objectLists
RDF_n3_propertyColumn
RDF_n3_subjectColumn
RDF_n3_useDoubles
RDF_n3_usePropertySymbols
RDF_n3_useTripleQuotedStrings
RDF_n3_widePropertyLen
RDF_rdfxml_allowBadUris
RDF_rdfxml_attributeQuoteChar
RDF_rdfxml_blockRules
RDF_rdfxml_longId
RDF_rdfxml_relativeUris
RDF_rdfxml_showDoctypeDeclaration
RDF_rdfxml_showXmlDeclaration
RDF_rdfxml_tab
RDF_rdfxml_xmlBase
RdfSerializer
The following pre-configured serializers are provided for convenience:
Abbreviated RDF/XML is currently the most widely accepted and readable RDF syntax, so the examples shown here will use that format.
The RdfParser
class is the top-level class for all Jena-based parsers.
Language-specific parsers are defined as inner subclasses of the
The
The RDF parser provides the following settings:
RdfCommon
RDF_arp_embedding
RDF_arp_err_
RDF_arp_errorMode
RDF_arp_ign_
RDF_arp_iriRules
RDF_arp_warn_
RDF_collectionFormat
RDF_juneauBpNs
RDF_juneauNs
RDF_language
RDF_looseCollections
RDF_n3_abbrevBaseUri
RDF_n3_indentProperty
RDF_n3_minGap
RDF_n3_objectLists
RDF_n3_propertyColumn
RDF_n3_subjectColumn
RDF_n3_useDoubles
RDF_n3_usePropertySymbols
RDF_n3_useTripleQuotedStrings
RDF_n3_widePropertyLen
RDF_rdfxml_allowBadUris
RDF_rdfxml_attributeQuoteChar
RDF_rdfxml_blockRules
RDF_rdfxml_longId
RDF_rdfxml_relativeUris
RDF_rdfxml_showDoctypeDeclaration
RDF_rdfxml_showXmlDeclaration
RDF_rdfxml_tab
RDF_rdfxml_xmlBase
RdfParser
The following pre-configured parsers are provided for convenience:
The @Rdf
annotation
is used to override the behavior of the RDF serializers and parsers on individual bean classes or properties.
You'll notice in the previous example that Juneau namespaces are used to represent bean property names. These are used by default when namespaces are not explicitly specified.
The
The
The easiest way to specify namespaces is through annotations.
In this example, we're going to associate the prefix
In general, the best approach is to define the namespace URIs at the package level using a
This assigns a default prefix of
Now when we rerun the sample code, we'll get the following:
Namespace auto-detection (XmlSerializer.XML_autoDetectNamespaces
) is
enabled on serializers by default.
This causes the serializer to make a first-pass over the data structure to look for namespaces.
In high-performance environments, you may want to consider disabling auto-detection and providing an
explicit list of namespaces to the serializer to avoid this scanning step.
This code change will produce the same output as before, but will perform slightly better since it doesn't have to crawl the POJO tree before serializing the result.
Bean properties of type
In the following code, we're adding 2 new properties.
The first property is annotated with
We alter our code to pass in values for these new properties.
Now when we run the sample code, we get the following:
The @URI
annotation can also be used on classes and properties
to identify them as URLs when they're not instances of
The following properties would have produced the same output as before.
Note that the
Also take note of the Serializer.SERIALIZER_uriResolution
,
Serializer.SERIALIZER_uriRelativity
, and
and Serializer.SERIALIZER_uriContext
settings that can be specified on the serializer to resolve relative and context-root-relative URIs to
fully-qualified URIs.
This can be useful if you want to keep the URI authority and context root information out of the bean logic layer.
The following code produces the same output as before, but the URIs on the beans are relative.
For all RDF languages, the POJO objects get broken down into simple triplets.
Unfortunately, for tree-structured data like the POJOs shown above, this causes the root node of the tree
to become lost.
There is no easy way to identify that
By default, the RdfParser
class handles this by scanning all the nodes and
identifying the nodes without incoming references.
However, this is inefficient, especially for large models.
And in cases where the root node is referenced by another node in the model by URL, it's not possible to
locate the root at all.
To resolve this issue, the property RdfSerializer.RDF_addRootProperty
was introduced.
When enabled, this adds a special
To enable, set the
Now when we rerun the sample code, we'll see the added
XML-Schema data-types can be added to non-RdfSerializer.RDF_addLiteralTypes
setting.
To enable, set the
Now when we rerun the sample code, we'll see the added
juneau-dto-8.1.2.jar
org.apache.juneau.dto_8.1.2.jar
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 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 HTML tables.
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 HtmlParser
class can be used convert these HTML documents back
into POJOs.
Other serializers and parsers (e.g. 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
AtomBuilder
class.
To serialize this to ATOM, use the XmlSerializer
class:
The XmlParser
class can be used convert these Atom documents back into POJOs.
Other serializers and parsers (e.g. 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 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 SwaggerElement.get(String,Class)
and 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 swagger = JsonParser.
The SwaggerUI
class is a DTO class for generating Swagger user interfaces
from Swagger
beans.
The
Using 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 Swagger
beans
with Div
elements:
The BasicRestServlet
class (describe later) shows how this swap is used in the REST interface to
generate the Swagger UI shown above:
juneau-config-8.1.2.jar
org.apache.juneau.config_8.1.2.jar
The
Config files are accessed through the Config
class which
are created through the ConfigBuilder
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.
The most common case for configuration values are primitives.
The following methods are provided for accessing primitive values:
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:
The following methods are provided for accessing POJO values:
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 Simplified JSON by default:
The format for beans depends on the serializer and parser registered on the Config which is defined in the builder via the following methods:
The default parser can also be overridden on the following getters:
The following methods are provided for accessing arrays:
The
String[] key1 = c.getStringArray(
String arrays can also be represented in JSON when using the
String[] key1 = c.getObject(
Primitive arrays can also be retrieved using the
Arrays of POJOs can also be retrieved using the
Address[] addresses = c.getObject(
The following methods are provided for accessing maps and collections:
The
Examples are shown below:
List<Address> addresses = c.getObject(
Oftentimes, it might be useful to parse into the ObjectList
and ObjectMap
classes that provide the various convenience methods for working with JSON-like data structures:
ObjectMap m = c.getObject(
The following methods are provided for accessing 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 VarResolver
API.
Config c = Config.
By default, VarResolver.DEFAULT
variable resolver
which provides support for the following variables and constructs:
SystemPropertiesVar
- EnvVariablesVar
- ConfigVar
- 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:
IfVar
- SwitchVar
- CoalesceVar
- PatternMatchVar
- NotEmptyVar
- UpperCaseVar
- LowerCaseVar
-
The
The
The
The
Encoded entries allow for sensitive information such as passwords to be obfuscated.
Encoded entries are denoted with a
For example, the following password is marked for encoding:
The default encoder is ConfigXorEncoder
which is a simple XOR+Base64 encoder.
Custom encoders can be used to provide your own encoding support by implementing the ConfigEncoder
interface.
Encoders are controlled via the following setting:
Encoded values can be set to plain-text values. The curly brackets around the value identify whether the value has been encoded or not.
Unencoded values are encoded when the file is saved using the Config.commit()
method.
They can also be encoded immediately by calling Config.encodeEntries()
.
Config sections can be retrieved in-bulk using
Config.getSectionAsMap(String)
.
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
Config.getSectionAsBean(String,Class,boolean)
.
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
Config.getSectionAsInterface(String,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 Config.set(String,Object,Serializer,ConfigMod[],String,List)
can be used
for adding comments and pre-lines to entries, and specifying encoded values.
The last 4 arguments in Config.set(String,Object,Serializer,ConfigMod[],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 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 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 myAddress = c.getObject(
The following methods can be used to bulk-load configuration values:
Changes can then be committed using the Config.commit()
method.
Configuration change events can be listened for using the following methods:
The ConfigEventListener
interface consists of the following method:
The ConfigEvent
class provides access to all the information about the updated entry:
The listener method is triggered:
Config.commit()
is called.
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.
The Writable
which means it can be
returned as-is by REST methods to be serialized as INI text.
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 c = ConfigBuilder.
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:
ConfigStore
read(String)
- Read a config file.
write(String,String,String)
- Write a config file.
update(String,String)
- Update the contents of a config file.
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:
ConfigFileStore
- File-system storage.
ConfigMemoryStore
- In-memory storage.
The store is defined on the
The default store used is ConfigFileStore.DEFAULT
which defines
the execution directory as the file system directory to store and retrieve files.
The ConfigMemoryStore
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 ConfigFileStore
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. Completing it is left as an exercise:
The purpose of the builder class is to simply set values in the PropertyStore
that's passed to the
The
ConfigStore
register(String,ConfigStoreListener)
- Register a listener on the specified config name.
unregister(String,ConfigStoreListener)
- Unregister a listener on the specified config name.
Note that this is a different listener than ConfigEventListener
.
In this case, we're just listening for changed files:
ConfigStoreListener
ConfigStoreListener.onChange(String)
- Called when file changes. New contents are passed in.
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 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 @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-rest-server-8.1.2.jar
org.apache.juneau.rest.server_8.1.2.jar
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, RestServlet
is nothing but a specialized
HttpServlet
, and the RestRequest
and
RestResponse
classes are nothing more than specialized
HttpServletRequest
and 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 Rest
.
The most common case is a class that extends RestServlet
, which itself is simply an
extension of HttpServlet
which allows it to be deployed as a servlet.
In this example, we define a resource called
Like any servlet, we could define our resource in the
Our servlet code is shown below:
This is what it looks like in a browser:
http://localhost:10000/helloWorld
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 BasicRestServlet
class that we're using here is a subclass of
RestServlet
that provides default support for a variety of content types.
Implementers can choose to use this class, or create their own subclass of
RestServlet
with their own specialized serializers and parsers.
The class hierarchy for the REST servlet class is shown below:
javax.servlet.http.HttpServlet
org.apache.juneau.rest.RestServlet
org.apache.juneau.rest.BasicRestServlet
org.apache.juneau.rest.BasicRestServletGroup
For top-level resources, you'll typically extend from one of the classes above.
For child resources, you can either extend from one of the classes above, or extend from one of the following
that provides identical support but does not extent from
BasicRestConfig
org.apache.juneau.rest.BasicRest
org.apache.juneau.rest.BasicRestGroup
One reason to use the latter classes as your base classes is when you're implementing REST resources as
The servlets with RDF support require Jena on the classpath. All other serializers and parsers do not have any external library dependencies. For this reason, we have separate servlets for supporting RDF so that you don't need Jena if you don't need to support RDF.
Everything is configured through the following classes which you will see a lot:
RestContext
- Each resource class instance has one copy that holds all of its configuration.
RestContextBuilder
- Builder for the class above.
REST resources are deployed in one of two ways:
When deployed in a J2EE container, you MUST extend from one of the servlet classes.
When deployed as a child of another resource, you MAY extend from one of the servlet classes but it's
not necessary.
The only requirement is that the class be annotated with
And even that requirement is relaxed if you implement your own REST resource resolver (described later).
For example:
That's all there is to it! There's no code scanning, module configuration/initialization classes, or anything complex like that. It's just a servlet.
The RestServlet
class is the entry point for your REST resources.
It extends directly from
When the servlet RestContext
object that holds all the configuration
information about your resource in a read-only object.
Most developers are not going to be using the BasicRestServlet
.
The
RestServlet
init(ServletConfig)
- Can override to provide custom initialization.
service(HttpServletRequest,HttpServletResponse)
- Can override to provide custom request handling.
destroy()
- Can override to provide custom cleanup.
getContext()
- Returns all aspects of the configuration of your resource pulled from all annotations on the class/parent-classes and methods.
log(Level,String,Object...)
- Convenience logging method.
log(Level,Throwable,String,Object...)
- Convenience logging method.
The BasicRestServlet
class is a subclass of RestServlet
preconfigured with the following:
The contents of the class is shown below. You should notice that very little code is being used and everything is configurable through annotations:
Additional annotations are pulled in from the BasicRestConfig
interface which simply
exists to define a common set of annotations.
Notice that it has no code at all.
Your top-level resource will simply extend from this class, as shown in the Hello World example from a couple sections back.
It's important to notice that the
Not shown but equally important is that all of the annotations shown have programmatic equivalents via the RestContextBuilder
class
which can be manipulated during servlet initialization.
(As a general rule, all annotations throughout Juneau have programmatic equivalents.)
There's a lot going on in this class. But not to worry, the details will be described later.
The BasicRest
class is identical to the BasicRestServlet
class except that
it does not extend from @Rest(children)
annotation.
The code for this class is virtually identical to BasicRestServlet
but lacks a parent class:
Child Resources are REST servlets or objects that are linked to parent resources through the
@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
A HUGE advantage of using child resources is that they do not need to be declared in the JEE
The 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 BasicRestServletGroup
class is nothing more than a subclass of
BasicRestServlet
with a
The BasicRestGroup
class is identical to the BasicRestServletGroup
class except that
it does not extend from @Rest(children)
annotation.
By default, you can add the @Rest
to any class as long as it has one of the following constructors:
The latter constructor can be used to get access to the RestContextBuilder
object to make
any configurations to the resource before it's initialized.
Resource object resolution is controlled through the following API:
This API can be extended to provide your own custom resource resolution. Later topics discuss how to use this API to instantiate resources using Spring.
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 hook events can be broken down into two categories:
INIT
- Right before initialization.
POST_INIT
- Right after initialization.
POST_INIT_CHILD_FIRST
- Right after initialization, but run child methods first.
DESTROY
- Right before servlet destroy.
START_CALL
- At the beginning of a REST call.
PRE_CALL
- Right before the POST_CALL
- Right after the END_CALL
- At the end of the REST call after the response has been flushed.
The @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 @Rest
annotation can also be used on 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.
guards() |
Guards on child are combined with those on parent class.
Guards are executed child-to-parent in the order they appear in the annotation. Guards on methods are executed before those on classes. |
converters() |
Converters on child are combined with those on parent class.
Converters are executed child-to-parent in the order they appear in the annotation. Converters on methods are executed before those on classes. |
properties() |
Properties on child are combined with those on parent class.
Properties are applied parent-to-child in the order they appear in the annotation. Properties on methods take precedence over those on classes. |
serializers() |
Serializers on child override those on parent class.
Inherit class can be used to inherit and augment values from parent.
None class can be used to suppress inheriting from parent.
Serializers on methods take precedence over those on classes. |
parsers() |
Parsers on child override those on parent class.
Inherit class can be used to inherit and augment values from parent.
None class can be used to suppress inheriting from parent.
Parsers on methods take precedence over those on classes. |
responseHandlers() |
Response handlers on child are combined with those on parent class. |
encoders() |
Encoders on child are combined with those on parent class. |
defaultRequestHeaders() |
Headers on child are combined with those on parent class.
Headers are applied parent-to-child in the order they appear in the annotation. Headers on methods take precedence over those on classes. |
defaultResponseHeaders() |
Headers on child are combined with those on parent class.
Headers are applied parent-to-child in the order they appear in the annotation. |
children() |
Children on child are combined with those on parent class.
Children are list parent-to-child in the order they appear in the annotation. |
path() |
Path is searched for in child-to-parent order. |
title() |
Label is searched for in child-to-parent order. |
description() |
Description is searched for in child-to-parent order. |
config() |
Config file is searched for in child-to-parent order. |
staticFiles() |
Static files on child are combined with those on parent class.
Static files are are executed child-to-parent in the order they appear in the annotation. |
The @RestResource(path)
annotation is used in the following situations:
@RestResource(children)
annotation) to identify
the subpath used to access the child resource relative to the parent.
RestServlet
classes deployed as Spring beans when JuneauRestInitializer
is being used.
The typical usage is to define a path to a child resource relative to the parent resource.
In the example above, assuming the
Note that in this scenario, the
The path can also be used on top-level resources deployed as Spring beans when used with the JuneauRestInitializer
Spring Boot initializer class:
In this case, the servlet will get registered using the path defined on the resource class.
The path can contain variables that get resolved to @Path
parameters
or access through the RestRequest.getPathMatch()
method.
Variables can be used on either top-level or child resources and can be defined on multiple levels.
When variables are used on a path of a top-level resource deployed as a Spring bean in a Spring Boot application, the first part of the URL must be a literal which will be used as the servlet path of the registered servlet.
The 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
Rest
- Settings copied from the annotation during servlet initialization.
RestContextBuilder
- Builder used during servlet initialization.
The annotation should be self-explanatory at this point. The builder allows you to perform all of the same configuration as the annotation programmatically.
The RestContextBuilder
class extends BeanContextBuilder
allowing you to programmatically set any properties defined on that builder class.
It also implements ServletConfig
To access this object, simply pass it in as a constructor argument or in an INIT hook:
Warning: The builder class is huge. Through it, you can configure bean/serializer/parser settings, define config files, children, resource finders, info providers, etc...
REST Java methods are identified on REST servlets using the
@RestMethod
annotation.
The annotation allows the framework to identify the available REST methods through reflection.
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:
RestRequest
- The request object.
HttpServletRequest
- The superclass of RestResponse
- The response object.
HttpServletResponse
- The superclass of ResourceBundle
- Client-localized resource bundle.
MessageBundle
- A resource bundle with additional features.
Locale
- Client locale.
RequestHeaders
- API for accessing request headers.
RequestQuery
- API for accessing request query parameters.
RequestFormData
- API for accessing request form data.
RequestPath
- API for accessing path variables.
RequestBody
- API for accessing request body.
HttpMethod
- The method name matched (when using RestLogger
- Logger with additional features.
RestContext
- The resource read-only context.
Parser
- The parser matching the request content type.
ReaderParser
- The reader parser matching the request content type.
InputStreamParser
- The input stream parser matching the request content type.
Swagger
- The auto-generated Swagger doc.
Config
- The external config for the resource.
RequestProperties
- API for modifying request-time configuration properties.
Path
- Variables in matched URL path patterns.
FormData
- Multipart form post parameter values.
HasFormData
- Denotes whether the form data parameter exists.
Query
- Query parameters. Using this prevents the HTTP body from being processed as a URL-Encoded form post.
HasQuery
- Denotes whether the query parameter exists.
Header
- A header value.
Body
- The HTTP content parsed as a POJO.
Method
- The HTTP method name.
Request
- HTTP request parts available through a proxy bean interface.
Response
- HTTP response parts available through a POJO.
ResponseHeader
- HTTP response header.
ResponseStatus
- HTTP response status code.
RestRequest
class.
RestContext.REST_paramResolvers
- For configuring custom parameter types.
The RestRequest
object is an extension of the
There are many useful methods on this object, but the main ones are shown below:
RestRequest
getHeaders()
- HTTP request headers.
getQuery()
- Query parameters.
getFormData()
- Form-data parameters.
getBody()
- HTTP request body.
getPathMatch()
- Path match variables.
getLogger()
- Logging.
getInfoProvider()
- Swagger documentation provider.
getSwagger()
- Generated Swagger information.
getConfig()
- External configuration API.
getVarResolverSession()
- String variable resolver.
getMessageBundle()
- Localized messages.
getProperties()
- Configuration properties.
getClasspathReaderResource(String,boolean,MediaType,boolean)
- Various methods for retrieving static files from classpath.
The RestResponse
object is an extension of the
Some important methods on this class are:
RestResponse
setOutput(Object)
- Set response output programmatically.
getHtmlDocBuilder()
- Set HTTP page contents programmatically.
getDirectWriter(String)
- Direct access to underlying response writer.
The RequestBody
object is the API for accessing the body of an HTTP request.
It can be accessed by passing it as a parameter on your REST Java method:
Some important methods on this class are:
RequestBody
getReader()
- Get body as a Reader.
getInputStream()
- Get body as an InputStream.
asType(Class)
- Get body converted to a POJO.
asType(Type,Type...)
- Get body converted to a map or collection of POJOs.
asString()
- Get body as a simple string.
asHex()
- Get body as a hex-encoded string.
asSpacedHex()
- Get body as spaced-hex-encoded string.
The RequestHeaders
object is the API for accessing the headers of an HTTP request.
It can be accessed by passing it as a parameter on your REST Java method:
Some important methods on this class are:
RequestHeaders
get(String,Class)
- Get header value converted to a POJO.
get(String,Type,Type)
- Get header value converted to a map or collection of POJOs.
getString(String,String)
- Get header value as a simple string.
getInt(String,int)
- Get header value as an integer.
getBoolean(String,boolean)
- Get header value as a boolean.
addDefault(String,Object)
- Programmatically set a default value for a header.
The RequestAttributes
object is the API for accessing the standard servlet attributes on an HTTP request
(i.e. ServletRequest.getAttribute(String)
.
It wraps the request attributes in a Map
interface and provides several convenience methods.
The ObjectMap
so all the convenience methods defined on
that API are also available when working with request attributes:
Modifications made to request attributes through the
The RequestQuery
object is the API for accessing the GET query parameters of an HTTP request.
It can be accessed by passing it as a parameter on your REST Java method:
An important distinction between the behavior of this object and
Some important methods on this class are:
RequestQuery
get(String,Class)
- Get query parameter value converted to a POJO.
get(String,Type,Type)
- Get query parameter value converted to a map or collection of POJOs.
getString(String,String)
- Get query parameter value as a simple string.
getInt(String,int)
- Get query parameter value as an integer.
getBoolean(String,boolean)
- Get query parameter value as a boolean.
addDefault(String,Object)
- Programmatically set a default value for a query parameter.
getSearchArgs()
- Returns query parameter search arguments.
The RequestFormData
object is the API for accessing the HTTP request body as form data.
It can be accessed by passing it as a parameter on your REST Java method:
Note that this object does NOT take GET parameters into account and only returns values found in the body of the request.
Some important methods on this class are:
RequestFormData
get(String,Class)
- Get form-data parameter values converted to a POJO.
get(String,Type,Type)
- Get form-data parameter value converted to a map or collection of POJOs.
getString(String,String)
- Get form-data parameter value as a simple string.
getInt(String,int)
- Get form-data parameter value as an integer.
getBoolean(String,boolean)
- Get form-data parameter value as a boolean.
addDefault(String,Object)
- Programmatically set a default value for a form-data parameter.
The @RestMethod(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:
The match heuristic behavior can be overridden by the
@RestMethod(priority)
annotation
property.
However, in practice this is almost never needed.
Paths that end with RequestPath.getRemainder()
or parameters with the
The following example shows the distinction.
Annotations are provided for easy access to URL parameters with automatic conversion to any parsable type.
For example, the following example can process the URL
The RequestPath
object is the API for accessing the matched variables
and remainder on the URL path.
Some important methods on this class are:
RequestPath
get(String,Class)
- Get path match variable converted to a POJO.
get(String,Type,Type)
- Get path match variable converted to a map or collection of POJOs.
getString(String)
- Get patch match variable as a simple string.
getInt(String)
- Get path match variable as an integer.
getBoolean(String)
- Get path match variable as a boolean.
getRemainder()
- Get the path match remainder.
The return type can be any serializable POJO as defined in POJO Categories.
It can also be RestResponse.setOutput(Object)
method.
Out-of-the-box, besides POJOs, the following return types are handled as special cases:
InputStream
RestResponse.getNegotiatedOutputStream()
.
ServletResponseWrapper.setContentType(String)
to set
the Reader
RestResponse.getNegotiatedWriter()
.
ServletResponseWrapper.setContentType(String)
to set
the Streamable
Writable
ZipFileList
This is controlled through the following extensible API:
REST Java methods can generate output in any of the following ways:
Reader
, InputStream
, Streamable
,
Writable
RestResponse.setOutput(Object)
with any of the types above.
Writer
directly by calling
RestResponse.getNegotiatedWriter()
and writing the output yourself.
RestContext.REST_responseHandlers
- For configuring custom response handlers.
The ReaderResource
class is a convenience object for defining thread-safe
reusable character-based responses.
In essence, it's a container for character data with optional response headers and support for
resolving SVL variables.
The class is annotated with @Response
which allows it to be returned as responses by REST methods.
The StreamResource
class is the binary equivalent to the ReaderResource
object.
In essence, it's a container for binary data with optional response headers.
The class is annotated with @Response
which allows it to be returned as responses by REST methods.
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:
Predefined response beans are provided for all standard HTTP responses. These can be used as-is or extended to provide customized HTTP responses.
These predefined response beans are an example of @Response
-annotated
objects that are describe in detail later.
Without going into details, this is how the SeeOther
is defined:
The SeeOtherRoot
class shows how these predefined beans can be extended.
Note that the runtime behavior of the following code is identical to the example above. However, the important distinction is that in the previous example, the 302 response would show in the generated Swagger (since we can see the response through reflection), whereas it will NOT show up in the following example (since all we see is an Object response).
Exceptions are defined for all standardized HTTP responses. These can be used to trigger HTTP errors simply by throwing an exception.
These are identical in behavior to the Predefined Responses in the previous section, except these are meant to be thrown instead of returned.
org.apache.juneau.http.exception
BadRequest
Conflict
ExpectationFailed
FailedDependency
Forbidden
Gone
HttpVersionNotSupported
InsufficientStorage
InternalServerError
LengthRequired
Locked
LoopDetected
MethodNotAllowed
MisdirectedRequest
NetworkAuthenticationRequired
NotAcceptable
NotExtended
NotFound
NotImplemented
PayloadTooLarge
PreconditionFailed
PreconditionRequired
RangeNotSatisfiable
RequestHeaderFieldsTooLarge
ServiceUnavailable
TooManyRequests
Unauthorized
UnavailableForLegalReasons
UnprocessableEntity
UnsupportedMediaType
UpgradeRequired
UriTooLong
VariantAlsoNegotiates
These exception extend from RuntimeException
, so they can optionally be specified in the thrown
declaration of the method.
The important distinction is that when part of the thrown declaration, they show up in the generated Swagger
documentation, whereas they don't if not. This behavior can be used to define what error conditions show in your Swagger doc.
The org.apache.juneau.rest.helper
package contains several predefined beans to help when constructing
REST interfaces.
The ResourceDescription
class is a bean with name/description
properties for labeling and linking to child resources.
The following examples is pulled from the REST examples:
It get rendered as a table of name/description columns with links to child methods:
The internals of the class show it simply has two bean properties with a link annotation defined on the name property:
ResourceDescriptions
is a convenience class for doing the same.
The example above can also be written as follows (which you'll notice is more concise):
The @HtmlLink
annotation can also be useful
for rendering custom hyperlinks:
The LinkString
bean is a predefined
Both examples render the following consisting of a list of hyperlinks:
In all other languages, it gets serialized as a simple bean with two properties.
The BeanDescription
class provides a simple view
of a bean and it's properties.
This example renders the following:
The ChildResourceDescriptions
is a convenience bean for generating
a table of child resources.
The BasicRestServletGroup
class uses this to generate router pages:
Note that all it requires is a RestRequest
object and it will generate a router
page using reflection against the resource class.
For example, the
The BasicRestServletGroup
class:
The SeeOtherRoot
class can be used to redirect to the root URI
of a resource class.
The runtime behavior is the same as the following:
One distinction is that the former defines the description
The restRPC (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:
RrpcServlet
.
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 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[] args =
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 UonSerializer
for more information about this encoding.
Usually you can also pass in JSON if you specify
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.
Parameters annotated with any of the following are parsed using the registered OpenApiParser
and
therefore support OpenAPI syntax and validation:
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
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
Or even POJOs that take in arrays of
Or even POJOs that take in the whole 2-dimensional array:
As you can see, the complexity of possible input types expands significantly. For more information about valid parameter types, see OpenAPI Parsers
Parameters annotated with any of the following are serialized using the registered OpenApiSerializer
and
therefore support OpenAPI syntax and validation:
ResponseHeader
Response
(
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
The following shows the same for a response body:
For more information about the valid parameter types, see OpenAPI Serializers
The annotations used for defining the schema for request HTTP parts are:
The annotations used for defining the schema for response HTTP parts are:
The sub-annotations used in the annotation above are:
The @Body
annotation is used to identify POJOs to be used as the body of an HTTP request.
This is functionally equivalent to the following code:
Any of the following types can be used for the parameter or POJO class (matched in the specified order):
Reader
InputStream
Reader
by having one of the following non-deprecated methods:
InputStream
by having one of the following non-deprecated methods:
String
by having one of the following non-deprecated methods:
Optional
of anything on this list.
The OpenApiSerializer
class can be used to serialize HTTP bodies to OpenAPI-based output.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
For more information about valid parameter types when using OpenAPI parsing, see OpenAPI Parsers
The
Default REST SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields.
List<MyBean>
. This is due to the fact that Spring uses CGLIB to recompile classes
at runtime, and CGLIB was written before generics were introduced into Java and is a virtually-unsupported library.
Therefore, parameterized types will often be stripped from class definitions and replaced with unparameterized types
(e.g. List
). Under these circumstances, you are likely to get ClassCastExceptions
when trying to access generalized ObjectMaps
as beans. The best solution to this issue is to either
specify the parameter as a bean array (e.g. MyBean[]
) or declare the method as final so that CGLIB
will not try to recompile it.
The @FormData
annotation is used to retrieve request form post entries.
FormData
_default
- Default value if not present.
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
api()
- Free-form Swagger JSON.
collectionFormat
- How collections of items are formatted.
description
- Description.
example()
- Serialized example.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxItems
- Input validation. Maximum number of items in a collection.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minItems
- Input validation. Minimum number of items in a collection.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Form data entry name.
parser
- Override the part parser.
pattern
- Input validation. Must match regular expression.
required
- Input validation. Form data entry must be present.
type
- The schema type.
uniqueItems
- Input validation. Collections must contain unique items only.
value
- Free-form Swagger JSON.
The most typical scenario is to simply use the
This is functionally equivalent to the following code:
The special name
The registered REST_partParser
is used to convert strings
to POJOs and controls what POJO types are supported.
By default, this is the OpenApiParser
which supports the standard Swagger-based rules for parsing.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
For more information about valid parameter types, see OpenAPI Parsers
The
Default REST SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields.
@Body
annotation or RestRequest.getBody()
method
for @Query
annotation can be used to retrieve a URL parameter in the URL string without triggering the
servlet to drain the body content.
List<MyBean>
. This is due to the fact that Spring uses CGLIB to recompile classes
at runtime, and CGLIB was written before generics were introduced into Java and is a virtually-unsupported library.
Therefore, parameterized types will often be stripped from class definitions and replaced with unparameterized types
(e.g. List
). Under these circumstances, you are likely to get ClassCastExceptions
when trying to access generalized ObjectMaps
as beans. The best solution to this issue is to either
specify the parameter as a bean array (e.g. MyBean[]
) or declare the method as final so that CGLIB
will not try to recompile it.
This annotation can be used to detect the existence of a parameter when it's not set to a particular value.
This is functionally equivalent to the following code:
The parameter type must be either Boolean
.
The following table shows the behavioral differences between
This annotation should not be combined with the @Body
annotation or RestRequest.getBody()
method
for
The @HasQuery
annotation can be used to check for the existing of a URL parameter in the URL string
without triggering the servlet to drain the body content.
The @Query
annotation is used to retrieve request URL query parameters.
It's identical to @FormData
, but only retrieves the parameter from the URL string, not URL-encoded form posts.
Query
_default
- Default value if not present.
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
api
- Free-form Swagger JSON.
collectionFormat
- How collections of items are formatted.
description
- Description.
example
- Serialized example.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxItems
- Input validation. Maximum number of items in a collection.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minItems
- Input validation. Minimum number of items in a collection.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Query parameter name.
parser
- Override the part parser.
pattern
- Input validation. Must match regular expression.
required
- Input validation. Query parameter must be present.
type
- The schema type.
uniqueItems
- Input validation. Collections must contain unique items only.
value
- Free-form Swagger JSON.
Unlike @FormData
, using this annotation does not result in the servlet reading the contents of
URL-encoded form posts.
Therefore, this annotation can be used in conjunction with the @Body
annotation or
RestRequest.getBody()
method for
The most typical scenario is to simply use the
This is functionally equivalent to the following code:
The special name
The registered REST_partParser
is used to convert strings
to POJOs and controls what POJO types are supported.
By default, this is the OpenApiParser
which supports the standard Swagger-based rules for parsing.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
For more information about valid parameter types, see OpenAPI Parsers
The
Default REST SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields.
Identical to @HasFormData
, but only checks the existing of the parameter in the URL string, not
URL-encoded form posts.
Unlike @HasFormData
, using this annotation does not result in the servlet reading the contents
of URL-encoded form posts.
Therefore, this annotation can be used in conjunction with the @Body
annotation or
RestRequest.getBody()
method for
This is functionally equivalent to the following code:
The parameter type must be either Boolean
.
The following table shows the behavioral differences between
The @Header
annotation is used to retrieve request headers.
Header
_default
- Default value if not present.
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
api
- Free-form Swagger JSON.
collectionFormat
- How collections of items are formatted.
description
- Description.
example
- Serialized example.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxItems
- Input validation. Maximum number of items in a collection.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minItems
- Input validation. Minimum number of items in a collection.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Header name.
parser
- Override the part parser.
pattern
- Input validation. Must match regular expression.
required
- Input validation. Header must be present.
type
- The schema type.
uniqueItems
- Input validation. Collections must contain unique items only.
value
- Free-form Swagger JSON.
The most typical scenario is to simply use the
This is functionally equivalent to the following code:
The special name
The registered REST_partParser
is used to convert strings
to POJOs and controls what POJO types are supported.
By default, this is the OpenApiParser
which supports the standard Swagger-based rules for parsing.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
For more information about valid parameter types, see OpenAPI Parsers
The
Default REST SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields.
The @Path
annotation is used to retrieve request path parameters.
Path
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
api
- Free-form Swagger JSON.
collectionFormat
- How collections of items are formatted.
description
- Description.
example
- Serialized example.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Path variable name.
parser
- Override the part parser.
pattern
- Input validation. Must match regular expression.
type
- The schema type.
value
- Free-form Swagger JSON.
The most typical scenario is to simply use the
This is functionally equivalent to the following code:
Note that the path variable name
The special name
The registered REST_partParser
is used to convert strings
to POJOs and controls what POJO types are supported.
By default, this is the OpenApiParser
which supports the standard Swagger-based rules for parsing.
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g.
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as
For more information about valid parameter types, see OpenAPI Parsers
The
Default REST SVL Variables (e.g. "$L{my.localized.variable}") are supported on annotation fields.
The @Request
annotation can be applied to a parameter interface type of a
Request
partParser
- Override the part parser.
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.
For clarity, the
The @Response
annotation is used to identify schema information about an HTTP response.
Response
api
- Free-form Swagger JSON.
code
- HTTP status code.
description
- Description.
example
- Serialized example.
examples
- Serialized examples per media type.
headers
- Swagger about headers added to response.
partSerializer
- Override the part serializer.
schema
- Swagger schema.
value
- Free-form Swagger JSON.
It can be used in the following locations:
When the
When applied to an exception class, this annotation defines Swagger schema and information on non-200 return types.
The following example shows the
Custom exceptions can also extend from one of the predefined HTTP exceptions such as the Unauthorized
exception:
When applied type classes returned by a Java method, this annotation defines schema and Swagger information on the body of responses.
In the example above, we're using the
Another example showing how a redirect can be defined:
The
The
The Value
(a placeholder for objects).
In the above example, the
The @ResponseStatus
annotation can be used on
the method of a
The @ResponseHeader
annotation can be used on
the method of a
The @ResponseBody
annotation can be used on
the method of a
If a
By default, POJOs representing the body of the request are serialized using the Juneau serializer
matching the requesting OpenApiSerializer
class can be used to serialize response bodies using OpenAPI rules.
The following examples show part-schema-based serialization of response bodies:
The
The attributes on this annotation are also used to populate the generated Swagger for the method.
For example, in the case of the
When the @Response(code)
value is specified,
the HTTP status is automatically set to that value on the response regardless of how it's used.
The following two examples are equivalent:
The @ResponseHeader
annotation can be applied to
ResponseHeader
_default
- Default value if not present.
_enum
- Output validation. Must match one of the values.
$ref
- Schema reference.
api
- Free-form Swagger JSON.
code
- HTTP status codes that this header applies to.
collectionFormat
- How collections of items are formatted.
description
- Description.
example
- Serialized example.
exclusiveMaximum
- Output validation. Whether maximum is exclusive.
exclusiveMinimum
- Output validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Output validation. Maximum numeric value.
maxItems
- Output validation. Maximum number of items in a collection.
maxLength
- Output validation. Maximum length of a string.
minimum
- Output validation. Minimum numeric value.
minItems
- Output validation. Minimum number of items in a collection.
minLength
- Output validation. Minimum length of a string.
multipleOf
- Output validation. Number must be a multiple of.
name
- Header name.
pattern
- Output validation. Must match regular expression.
serializer
- Override the part serializer.
type
- The schema type.
uniqueItems
- Output validation. Collections must contain unique items only.
value
- Free-form Swagger JSON.
This annotation can only be applied to parameters of type Value
.
The following examples show 3 different ways of accomplishing the same task of setting an HTTP header on a response:
The attributes on this annotation are also used to populate the generated Swagger for the method.
For example, in the case of the
The @ResponseStatus
annotation annotation can be applied to
This can only be applied to parameters of the Value
class with an Integer
type.
The best way to handle a form post is usually by using an input bean.
The following is a class that takes in URL-Encoded form post of the
form
Another possibility is to access the form parameters individually:
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. The latter approach only supports URL-Encoding.
ServletRequestWrapper.getParameter(String)
method since this will cause the
underlying JEE servlet to parse the HTTP body as a form post.
RestRequest.getBody()
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
REST resources use the Serializer
API for defining serializers for
serializing response POJOs.
The servlet will pick which serializer to use by matching the request Serializer.getMediaTypeRanges()
method.
Serializers can be associated with REST servlets in the following ways:
Rest(serializers)
- Annotation on resource Java class.
RestMethod(serializers)
- Annotation on resource Java method.
RestContext.REST_serializers
- Programmatic.
The following are all equivalent ways of defining serializers used by a resource:
REST resources use the Parser
API for defining parsers for parsing request
body content and converting them into POJOs.
The servlet will pick which parser to use by matching the request Parser.getMediaTypes()
method.
Parsers can be associated with REST servlets in the following ways:
Rest(parsers)
- Annotation on resource Java class.
RestMethod(parsers)
- Annotation on resource Java method.
RestContextBuilder.parsers(Class[])
- Programmatic.
The following are all equivalent ways of defining parsers used by a resource:
As shown in previous sections, Juneau serializers and parsers are highly-configurable through properties. (See Configurable Properties)
These properties can be defined for serializers and parsers registered on a REST resource via the following:
Rest(properties)
RestContextBuilder
- Various methods on the context builder.
The programmatic equivalent to this is:
Properties can also be overridden at the Java method level:
Rest(flags)
- Shorthand for boolean properties.
RestMethod(flags)
- Shorthand for boolean properties.
RestContextProperties
RestMethodProperties
Config annotations allow you to define serializer and parser properties using specialized annotations. All configuration properties have annotation-equivalents.
The following shows the difference between the two approaches:
Using config annotations is often cleaner and supports the same SVL variable support as those in the REST annotations.
Config annotations are defined for all serializers and parsers:
BeanConfig
CsvConfig
HtmlConfig
HtmlDocConfig
JsoConfig
JsonConfig
JsonSchemaConfig
MsgPackConfig
OpenApiConfig
ParserConfig
PlainTextConfig
RdfConfig
SerializerConfig
SoapXmlConfig
UonConfig
UrlEncodingConfig
XmlConfig
Config annotations can be defined at both the class and method level just like properties.
The Juneau serializers and parsers can be configured on how to handle POJOs through the use of Transforms. (See Transforms)
Transforms are associated serializers and parsers registered on a REST resource via the following:
BeanConfig(beanFilters)
- On class or methods.
BeanConfig(pojoSwaps)
- On class or methods.
RestContextBuilder.beanFilters(Object...)
RestContextBuilder.pojoSwaps(Object...)
The programmatic equivalent to this is:
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.
Guards are classes that control access to REST classes and methods.
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 RestGuard.guard(RestRequest,RestResponse)
and processing the response
yourself.
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.
Specialized guards are provided for controlling access to servlet classes and methods based on user roles. These are controlled via annotations on the REST class and methods:
The
The syntax allows for any of the following:
If patterns are used, you must specify the list of declared roles using the
Converters can be thought of as "post-processors" for POJOs before they get passed to the serializers.
Converters are associated with resource classes and methods via the following:
They can also be defined at the method level:
The following converter is used to provide support for addressing child nodes in a POJO tree with URL path
remainders.
In this code, the 3rd parameter is the object that was returned by the Java method.
The converter uses the PojoRest
wrapper class to address nodes in the
tree.
Juneau defines the following converters out-of-the-box:
RestConverter
Queryable
Traversable
Introspectable
The @Rest(messages)
annotation is used to associate a resource bundle with a servlet class.
The resource bundle can also be passed into the method by simply specifying a parameter
of type ResourceBundle
or MessageBundle
:
If a resource bundle is shared by multiple servlets, the label and description can be prefixed by the class name:
The @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:
In the previous examples, there were several cases where embedded variables were contained within annotation values:
Variables take the form
Variables are configured on resources via the following API:
The methods involved with variables are:
RestContext.getVarResolver()
RestRequest.getVarResolverSession()
RestRequest.getClasspathReaderResource(String,boolean)
There are two distinct groups of variables:
@Rest
.
RestContext.getVarResolver()
method returns initialization-time variables only.
@HtmlDoc
.
RestRequest.getVarResolverSession()
method returns initialization and request-time variables.
The following is the default list of supported variables.
Module | Class | Pattern | Initialization time | Request time | Examples |
---|---|---|---|---|---|
juneau-svl | EnvVariablesVar |
$E{key[,default]} | yes | yes | $E{PATH} |
SystemPropertiesVar |
$S{key[,default]} | yes | yes | $S{java.home} | |
ArgsVar |
$A{key[,default]} | yes | yes | $A{foo,null} | |
ManifestFileVar |
$MF{key[,default]} | yes | yes | $MF{Main-Class} | |
IfVar |
$IF{arg,then[,else]} | yes | yes | $IF{$S{my.boolean.property},foo,bar} | |
SwitchVar |
$SW{arg,p1:then1[,p2:then2...]} | yes | yes | $SW{$P{os.name},*win*:Windows,*:Something else} | |
CoalesceVar |
$CO{arg1[,arg2...]} | yes | yes | $CO{$S{my.property},$E{my.property},n/a} | |
PatternMatchVar |
$PM{arg,pattern} | yes | yes | $PM{$P{os.name},*win*} | |
NotEmptyVar |
$NE{arg} | yes | yes | $NE{$S{foo}} | |
UpperCaseVar |
$UC{arg} | yes | yes | $UC{$S{foo}} | |
LowerCaseVar |
$LC{arg} | yes | yes | $LC{$S{foo}} | |
juneau-config | ConfigVar |
$C{key[,default]} | yes | yes | $C{REST/staticFiles} |
juneau-rest-server | FileVar |
$F{path[,default]}} | yes | yes | $F{resources/MyAsideMessage.html, Oops not found!} |
ServletInitParamVar |
$I{name[,default]} | yes | yes | $I{my.param} | |
LocalizationVar |
$L{key[,args...]} | no | yes | $L{MyMessage,foo,bar} | |
RequestAttributeVar |
$RA{key1[,key2...]} | no | yes | $RA{attrName} | |
RequestFormDataVar |
$RF{key1[,key2...]} | no | yes | $RF{paramName} | |
RequestHeaderVar |
$RH{key1[,key2...]} | no | yes | $RH{Header-Name} | |
RequestPathVar |
$RP{key1[,key2...]} | no | yes | $RP{pathVAr} | |
RequestQueryVar |
$RQ{key1[,key2...]} | no | yes | $RQ{paramName} | |
RequestVar |
$R{key1[,key2...]} | no | yes | $R{contextPath} | |
RestInfoVar |
$RI{key} | no | yes | $RI{externalDocs} | |
SerializedRequestAttrVar |
$SA{contentType,key[,default]} | no | yes | $SA{application/json,$RA{foo}} | |
UrlVar |
$U{uri}> | no | yes | $U{servlet:/foo} | |
UrlEncodeVar |
$UE{uriPart} | yes | yes | $U{servlet:/foo?bar=$UE{$RA{bar}} | |
WidgetVar |
$W{name} | no | yes | $W{MenuItemWidget} |
The Server API provides methods for associating configuration files with REST servlets so that configuration properties can be defined in external files.
In recap, the Configuration API provides support for INI-style configuration files with embedded string variables:
These properties are then accessible through the Config
class.
Config c = Config.
Configuration files are associated with REST resources through the following:
The annotation itself can contain string variables.
For example, the Microservice API BasicRestServlet
class defines the
location of the config file as a system property
Once a config file has been associated with a REST resource, it can be accessed through the
RestContextBuilder.getConfig()
method.
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
RestRequest.getConfig()
to access the config file:
You can even add resource bundles into the mix:
The @Rest(staticFiles)
annotation is used to define paths and locations of statically-served files such as images or HTML
documents.
The value is a JSON map of paths to packages/directories located on either the classpath or working directory.
Static files are found by calling Class.getResource(String)
up the class hierarchy.
If not found, then an attempt is made to find the class in the Java working directory.
In the example above, given a GET request to
RestContext.getMediaTypeForName(String)
method.
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:
The RestInfoProvider
class is used to find the title
and description for your resource and also generate the Swagger documentation.
It can be overridden to provide your own custom Swagger documentation.
The methods on this interface are:
The info provider in turn supplies the information returned by the following methods:
Info providers are registered through the following property:
While you can implement this interface from scratch, you may want to instead consider extending
from the
The BasicRestInfoProvider
class is the default implementation of the
RestInfoProvider
interface.
It finds and collects information gathered from the following locations:
The class itself is designed to be extended if you wish to rely mostly on the default behavior, but tweak certain aspects.
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 SwaggerUI
, we're able to render that JSON as a Swagger user interface:
Any subclass of BasicRestServlet
gets an auto-generated Swagger UI when performing an
The underlying mechanics are simple.
The BasicRestServlet.getOptions(RestRequest)
method returns a Swagger
bean
consisting of information gathered from annotations and other sources.
Then that bean is swapped for a SwaggerUI
bean when rendered as HTML.
Here's the class that defines the behavior:
Note that to have your resource create Swagger UI, you must either extend from BasicRestServlet
or provide
your own Swagger
object and a SwaggerUI
swap.
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 @Rest(swagger)
annotation.
In this particular case, the Swagger is pulled in from a localized Swagger JSON file located in the
$F
variable.
{
Note that the $F
variable allows for request-locale-sensitive name matching so that you can provide
localized Swagger information.
The $F
variable simply expands to a string to fill the @ResourceSwagger(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 $L
variables.
A third option is to define your Swagger information in your @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 @MethodSwagger(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
Note: The
Another option is to specify your parameter information in the Queryable
class:
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.
The
The examples for query/form-data/path/header parameters can be defined using the
This value gets converted to an
{
Examples for request bodies includes all supported
These are based on the parsers registered on your servlet or method.
Selecting any of the content types shows you a representative example for the POJO:
There are several options for defining examples for request bodies:
@Body(example)
annotation.
@Body(examples)
annotation.
When using @Body(example)
, you specify a Simple JSON representation of your POJO.
The Swagger generator will then convert that JSON into a POJO using the registered serializers on the REST method to produce examples for all
supported language types.
The @Body(examples)
annotation allows you to specify raw output values per media type.
This field allows you to override the behavior and show examples for only specified media types or different examples for different media types.
The Swagger generator uses these to create an
Another option is to define these directly in your resource Swagger JSON file, or via @Rest(swagger)
/@RestMethod(swagger)
.
Juneau also supports auto-generation of JSON-Schema directly from POJO classes.
By default, the generated swagger uses to the JSONSCHEMA_addExamplesTo
setting to automatically add examples to beans, collections, arrays, and maps:
Examples can be defined via static methods, fields, and annotations on the classes themselves.
Examples can also be specified via generic properties as well using the BeanContext.BEAN_examples
property or @BeanConfig(examples)
annotation at either the class or method level.
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 @Response
annotation such
as this one:
Like input parameters, the Swagger for responses can be define in multiple locations such as:
@Response
annotated classes, methods, and parameters.
@Rest(swagger)
/ @RestMethod(swagger)
annotations.
The
Examples are provided for any supported
Examples are pulled from the
There are several options for defining examples for response bodies:
@Response(example)
annotation.
@Response(examples)
annotation.
The @Response(example)
annotation can be used on either your
This is a Simple JSON representation of the body that is converted to a POJO and then serialized to all the registered serializers on the REST method to produce examples for all
supported language types.
These values are then used to automatically populate the
Direct per-media-type examples can also be defined using the @Response(examples)
annotation:
Juneau also supports auto-generation of JSON-Schema directly from POJO classes.
By default, the generated swagger uses to the JSONSCHEMA_addExamplesTo
setting to automatically add examples to beans, collections, arrays, and maps:
In particular, examples can be defined via static methods, fields, and annotations on the classes themselves.
Examples can also be specified via generic properties as well using the BeanContext.BEAN_examples
property
or @BeanConfig(examples)
annotation at either the class or method level.
Response headers are also rendered in the Swagger UI:
These can be auto-generated from @ResponseHeader
annotations defined on either
method parameters or type classes.
The example above shows one of each:
The JsonSchemaGenerator.JSONSCHEMA_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
By default, this flag is enabled when extending from BasicRestServlet
:
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
The @HtmlDocConfig
annotation is used to customize the HTML
view of your serialized POJOs.
It's used in the following locations:
@Rest
-annotated classes.
@RestMethod
-annotated methods.
The annotation itself is just a convenience for setting configuration properties set
on the HtmlDocSerializer
class.
For example, the following two pieces of code are equivalent:
The purpose of these annotation is to populate the HTML document view which by default consists of the following structure:
The outline above is controlled by the HtmlDocTemplate
interface
which can be overridden via the @HtmlDoc(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 Widget
class allows you to add arbitrary HTML, CSS, and Javascript
to HTML pages.
They are registered in the following locations:
HtmlDoc(widgets)
RestContextBuilder.widgets(Class...)
RestContextBuilder.widgets(Widget...)
RestContext.REST_widgets
The
The HTML content returned by the getHtml(RestRequest,RestResponse)
method is added wherever the
The CSS returned by getScript(RestRequest,RestResponse)
is added to the style section in the page header.
The Javascript returned by 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 @Rest(staticFiles)
):
The
The org.apache.juneau.rest.widget
package contains predefined reusable widgets.
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:
MenuItemWidget
getLabel(RestRequest,RestResponse)
- The menu item label.
getContent(RestRequest,RestResponse)
- The menu item content.
For example, to render a link that brings up a simple dialog in a div tag:
The HTML content returned by the getHtml(RestRequest,RestResponse)
method is added where the
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:
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 Queryable
converter, although implementations
can process the query parameters themselves if they wish to do so by using the RequestQuery.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. Queryable
class knows how to perform these filters against collections of POJOs.
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
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:
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 $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 @HtmlDocConfig(stylesheet)
annotation.
The BasicRestServlet
class 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 MenuItemWidget
, a
specialized widget for creating pop-up menus.
In the case of
The following annotations are provided for specifying default header values for requests and responses:
Rest(defaultRequestHeaders)
Rest(defaultResponseHeaders)
Default headers can also be specified programmatically by overriding the following methods:
The REST APIs provides support for fine-tuned control of logging for HTTP requests and responses.
The APIs involved are:
Rest
RestMethod
RestContext
REST_callLoggerConfig
- Underlying configuration property for @Rest(logging)
.
REST_debug
- Underlying configuration property for @Rest(debug)
.
Logging
- Annotation for @Rest(logging)
and @RestMethod(logging)
level()
- The logging level.
rules()
- The logging rules.
useStackTraceHashing()
- Enables de-duplication of logged stack traces and replacement with hash IDs.
stackTraceHashingTimeout()
- Controls how frequently hashed stack traces are re-logged.
disabled()
- Disables logging completely or per-request.
LoggingRule
- Annotation for @Logging(rules)
level()
- The logging level.
codes()
- Status code patterns and ranges to match against.
exceptions()
- Exception class names and patterns to match against.
debugOnly()
- Match only if debug is set on the request.
req()
- The level of request detail.
res()
- The level of response detail.
verbose()
- Shortcut for disabled()
- Disables logging completely or per-request.
The amount of detail on requests and responses is controlled via the @LoggingRule(req)
and @LoggingRule(res)
annotations:
The following code shows the output produced using the
The log output produced from the code above looks like this:
WARNING: [500] HTTP POST /foo java.lang.StringIndexOutOfBoundsException at org.apache.juneau.rest.annotation.RestResourceLoggingTest$MyRestClass.myRestMethod(RestResourceLoggingTest.java:672) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) ...
In comparison, using
Request/response bodies are only cached for logging if debugging is enabled via the
The log file now contains the following with shows the contents of the request and response bodies in both UTF-8 and spaced-hex:
WARNING: === HTTP Request (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 Body UTF-8--- Foo ---Request Body Hex--- 46 6F 6F ---Response Body UTF-8--- Foo ---Response Body Hex--- 46 6F 6F === END =================================================================== java.lang.StringIndexOutOfBoundsException at org.apache.juneau.rest.annotation.RestResourceLoggingTest$MyRestClass.myRestMethod(RestResourceLoggingTest.java:672) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) ...
Logging rules can match against status codes, exception class names, and the debug flag.
The following shows various examples of logging rule matches.
Rules are matched in the order listed on the annotation. Rules on methods are matched before rules on classes. Rules on child methods/classes are matched before rules on parent classes.
The debug mode setting allows HTTP request/response bodies to be cached in memory for logging purposes:
The possible values for the debug setting are (case-insensitive):
Note that caching HTTP bodies in memory can produce significant performance penalties, so use the setting wisely.
In particular, do not leave this setting enabled on production instances since it can easily lead to
denial-of-service attacks.
Servlet code can check the debug setting via the RestRequest.isDebug()
method allowing it to be used
for custom purposes.
Debug mode can also be enabled on a request by calling the RestRequest.setDebug(Boolean)
or
RestResponse.setDebug(Boolean)
methods. Note however that you must be sure not to have already consumed
the request or started writing the response before calling this method.
The @Logging(disabled)
and @LoggingRule(disabled)
annotations
can be used to disable logging entirely or on a per-request basis:
The possible values for the disabled setting are (case-insensitive):
Disabled logging is particularly useful when running testcases that are expected to throw exceptions or cause other errors and you don't want these errors logged.
Disabled logging can also be set on a request by calling the RestRequest.setNoTrace(Boolean)
or
RestResponse.setNoTrace(Boolean)
methods.
The @Logging(useStackTraceHashing)
setting can be used to eliminate duplicate stacktraces
in your log file by logging them once and then logging identifying hash IDs.
In the example below, we're causing two exceptions but only logging the first one:
The log file will show a 4 byte hash ID
Jul 11, 2019 3:38:48 PM org.apache.juneau.rest.BasicRestCallLogger log WARNING: [500,9b85cc96.1] HTTP POST /foo java.lang.StringIndexOutOfBoundsException at org.apache.juneau.rest.annotation.RestResourceLoggingTest$MyRestClass.myRestMethod(RestResourceLoggingTest.java:671) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) ... WARNING: [500,9b85cc96.2] HTTP POST /foo
The @Logging(stackTraceHashingTimeout)
setting can be used to periodically log the stacktrace
to the log file again.
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 RestException
with a
specific HTTP status code, or calling HttpServletResponse.setStatus(int)
.
Non-OK (200) status codes are automatically triggered by the following conditions:
Unauthorized | A 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
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 RestException |
Through the use of the built-in
For example, the URL
To support overloaded methods, the @Rest(allowedMethodParams)
setting must be enabled on your servlet.
The following URL parameters have special meaning and can be passed in through the URL of the request:
&plainText=true |
Response will always be WSERIALIZER_useWhitespace enabled).
Useful for debugging. |
&debug=true | Request body content will be dumped to log file. |
&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 @Rest(allowedMethodParams) setting.
|
&Header-Name=headerValue |
Specify a header value as a GET parameter.
Must be enabled via @Rest(allowedHeaderParams) setting.
|
&body=X |
Pass in the HTTP body content on PUT and POST methods as a UON-encoded GET parameter.
Must be enabled via @Rest(allowBodyParam) setting.
|
&x-response-headers=X |
Pass-through headers to the response.
Must be a UON-encoded map of key-value pairs. |
A very easy-to-use API is provided for defining your own serializers and parsers at both the servlet and method levels.
The following examples show a custom serializer and parser defined at the method level.
It's the
Since REST servlets are basically just
The following code shows how to register your REST servlets in an OSGi
The Juneau REST server API is compatible with dependency injection frameworks such as Spring.
The important class is the RestResourceResolver
class which is used
to resolve child servlet/resource implementation classes inside parent contexts.
In other words, it's used for resolving @Rest(children)
instances.
The general approach starts with defining a resolver that uses the Spring application context for resolution:
Next, define the Spring configuration to return our resolver:
Finally, define your
After that, just define constructors on your child resources to take in Spring beans:
Juneau is built as a veneer on top of the Servlet API, allowing you to use low-level Servlet APIs whenever needed. This allows you to take advantage of the newest HTTP/2 features implemented in the new Servlet 4.0 specification.
GenericServlet.init(ServletConfig)
or GenericServlet.init()
for initialization just like any other servlet.
juneau-rest-server-jaxrs-8.1.2.jar
org.apache.juneau.rest.server_8.1.2.jar
The
The Juneau framework contains the
It should be noted that although some of the functionality of the Juneau Server API is provided through the JAX-RS
integration components, it is not nearly as flexible as using the RestServlet
class directly.
What you can do with the Juneau JAX-RS provider classes:
@RestMethod
and JuneauProvider
annotations.
What you can't do with the Juneau JAX-RS provider classes:
HtmlDocSerializer
class.
The Juneau JAX-RS provider API consists of the following classes:
BaseProvider
- The base provider class that implements the JAX-RS
JuneauProvider
- Annotation that is applied to subclasses of BasicProvider
- A default provider that provides the same level
of media type support as the BasicRestServlet
class.
For the most part, when using these components, you'll either use the existing
juneau-rest-server-springboot-8.1.2.jar
org.apache.juneau.rest.server.springboot_8.1.2.jar
The
The Juneau integration component for Spring Boot consists of the following classes:
A typical Spring Boot application can use the JuneauRestInitializer
to find
and register Juneau REST servlets via the JuneauRestRoot
annotation.
The initializer will search for Spring beans annotated with the
Another option is to use the
The root servlets are given an instance of SpringRestResourceResolver
which allows
for child resources to be defined as injectable Spring beans.
The Overview > juneau-examples-rest-springboot section describes how the Examples REST application is deployed using Spring Boot and quickly deployable as an online application using Heroku.
When using the SpringRestResourceResolver
.
The resource resolver gets passed down through the children hierarchy, allowing child resources to be defined as injectable Spring beans.
The root resource class must extend from BasicRestServletGroup
class is our router class
that extends from
Because Spring Boot will automatically register any beans that extend from BasicRestGroup
and BasicRest
instead:
juneau-rest-client-8.1.2.jar
org.apache.juneau.rest.client_8.1.2.jar
The REST client API provides the ability to access remote REST interfaces and transparently convert the input and output to and from POJOs using any of the provided serializers and parsers.
Built upon the Apache HttpClient libraries, it extends that API and provides specialized APIs for working with REST interfaces while maintaining all the functionality available in the HttpClient API.
Juneau provides an HTTP client API that makes it extremely simple to connect to remote REST interfaces and seemlessly send and receive serialized POJOs in requests and responses.
Serializer
classes.
Parser
classes.
HttpClientBuilder
class.
The client API is designed to work as a thin layer on top of the proven Apache HttpClient API. By leveraging the HttpClient library, details such as SSL certificate negotiation, proxies, encoding, etc... are all handled in Apache code.
The Juneau client API prereq's Apache HttpClient 4.5+. At a minimum, the following jars are required:
RestClient
class exposes all the builder methods on the Apache
HttpClient HttpClientBuilder
class.
The
Remote resources are instantiated using one of the following methods:
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 @Remote
annotation is used on your interface class
to identify it as a REST proxy interface.
The
The @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 p = client.getRemote(PetStore.
When a relative path is specified, it's relative to the root-url defined on the
RestClient client = RestClient.
When no path is specified, the root-url defined on the
RestClient client = RestClient.
The @RemoteMethod
annotation is applied to methods
of
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:
@Response
-annotated type. (described later)
Reader
- Returns access to the raw reader of the response.
InputStream
- Returns access to the raw input stream of the response.
If you're only interested in the HTTP status code of the response, you can use the returns
annotation with a value of STATUS
:
If your
The @Body
annotation can be applied to arguments of
The argument can be any of the following types:
Serializer
registered with the Reader
- Raw contents of Reader
will be serialized to remote resource.
InputStream
- Raw contents of InputStream
will be serialized to remote resource.
OpenAPI schema based serialization can be used by using the OpenApiSerializer
class.
See Overview > juneau-marshall > OpenAPI Details > OpenAPI Serializers for information about supported data types in OpenAPI serialization.
If your
The @FormData
annotation can be applied to arguments of
FormData
_default
- Default value if not present.
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
collectionFormat
- How collections of items are formatted.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxItems
- Input validation. Maximum number of items in a collection.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minItems
- Input validation. Minimum number of items in a collection.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Form data entry name.
pattern
- Input validation. Must match regular expression.
required
- Input validation. Form data entry must be present.
serializer
- Override the part serializer.
skipIfEmpty
- Don't add if value is null or empty.
type
- The schema type.
uniqueItems
- Input validation. Collections must contain unique items only.
Single-part arguments (i.e. those with name !=
HttpPartSerializer
registered with the
OpenApiSerializer
by default) or associated via the @FormData(serializer)
annotation.
Multi-part arguments (i.e. those with name ==
Reader
- Raw contents of Reader
will be serialized to remote resource.
InputStream
- Raw contents of InputStream
will be serialized to remote resource.
HttpPartSerializer
(OpenApiSerializer
by default).
HttpPartSerializer
(OpenApiSerializer
by default).
See the link below for information about supported data types in OpenAPI serialization.
The @Query
annotation can be applied to arguments of
Query
_default
- Default value if not present.
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
collectionFormat
- How collections of items are formatted.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxItems
- Input validation. Maximum number of items in a collection.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minItems
- Input validation. Minimum number of items in a collection.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Query parameter name.
pattern
- Input validation. Must match regular expression.
required
- Input validation. Query parameter must be present.
serializer
- Override the part serializer.
skipIfEmpty
- Don't add if value is null or empty.
type
- The schema type.
uniqueItems
- Input validation. Collections must contain unique items only.
Single-part arguments (i.e. those with name !=
HttpPartSerializer
registered with the
OpenApiSerializer
by default) or associated via the @Query(serializer)
annotation.
Multi-part arguments (i.e. those with name ==
Reader
- Raw contents of Reader
will be serialized directly a query string.
HttpPartSerializer
(OpenApiSerializer
by default).
HttpPartSerializer
(OpenApiSerializer
by default).
See the link below for information about supported data types in OpenAPI serialization.
The @Header
annotation can be applied to arguments of
Header
_default
- Default value if not present.
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
collectionFormat
- How collections of items are formatted.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxItems
- Input validation. Maximum number of items in a collection.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minItems
- Input validation. Minimum number of items in a collection.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Header name.
pattern
- Input validation. Must match regular expression.
required
- Input validation. Header must be present.
serializer
- Override the part serializer.
skipIfEmpty
- Don't add if value is null or empty.
type
- The schema type.
uniqueItems
- Input validation. Collections must contain unique items only.
Single-part arguments (i.e. those with name !=
HttpPartSerializer
registered with the
OpenApiSerializer
by default) or associated via the @Header(serializer)
annotation.
Multi-part arguments (i.e. those with name ==
HttpPartSerializer
(OpenApiSerializer
by default).
HttpPartSerializer
(OpenApiSerializer
by default).
See the link below for information about supported data types in OpenAPI serialization.
The @Path
annotation can be applied to arguments of
Path
_enum
- Input validation. Must match one of the values.
allowEmptyValue
- Input validation. Allow empty value.
collectionFormat
- How collections of items are formatted.
exclusiveMaximum
- Input validation. Whether maximum is exclusive.
exclusiveMinimum
- Input validation. Whether minimum is exclusive.
format
- The schema type format.
items
- The schema of items in a collection.
maximum
- Input validation. Maximum numeric value.
maxLength
- Input validation. Maximum length of a string.
minimum
- Input validation. Minimum numeric value.
minLength
- Input validation. Minimum length of a string.
multipleOf
- Input validation. Number must be a multiple of.
name
- Path variable name.
pattern
- Input validation. Must match regular expression.
serializer
- Override the part serializer.
type
- The schema type.
Single-part arguments (i.e. those with name !=
HttpPartSerializer
registered with the
OpenApiSerializer
by default) or associated via the @Path(serializer)
annotation.
Multi-part arguments (i.e. those with name ==
HttpPartSerializer
(OpenApiSerializer
by default).
HttpPartSerializer
(OpenApiSerializer
by default).
See the link below for information about supported data types in OpenAPI serialization.
The @Request
annotation can be applied to a type of a
Request
partSerializer
- Override the part serializer.
PetStore store = restClient.getRemote(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 @Response
annotation can be applied to types returned by
Response
partParser
- Override the part parser.
The
PetStore store = restClient.getRemote(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 @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:
In the example above, we chose to add the
Note how we didn't need to use the
The simplest way to enable SSL support in the client is to use the
RestClientBuilder.enableLaxSSL()
method.
A more typical scenario using default cert and hostname verification is shown here:
RestClient rc = RestClient.create().enableSSL().sslProtocols(
The following convenience methods are provided in the builder class for specifying SSL parameters:
SSL support can also be enabled by passing in your own connection manager using RestClientBuilder.httpClientConnectionManager(HttpClientConnectionManager)
.
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 RestClientBuilder.basicAuth(String,int,String,String)
method
can be used to quickly enable BASIC authentication support.
This is functionally equivalent to the following:
RestClientBuilder builder = RestClient.
The RestClientBuilder
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 RestClientBuilder
and override the
RestClientBuilder.createHttpClient()
method to provide an
authenticated client.
The following example shows how the
The following example shows how the
One issue with REST (and HTTP in general) is that the HTTP response code must be set as a header before the body of the request is sent. This can be problematic when REST calls invoke long-running processes, pipes the results through the connection, and then fails after an HTTP 200 has already been sent.
One common solution is to serialize some text at the end to indicate whether the long-running process
succeeded (e.g.
The RestClient
class has convenience methods for scanning the
response without interfering with the other methods used for retrieving output.
The following example shows how the RestCall.successPattern(String)
method can be used to look for a SUCCESS message in the output:
The RestCall.failurePattern(String)
method does the opposite.
It throws an exception if a failure message is detected.
These convenience methods are specialized methods that use the
RestCall.responsePattern(ResponsePattern)
method which uses regular
expression matching against the response body.
This method can be used to search for arbitrary patterns in the response body.
The following example shows how to use a response pattern finder to find and capture patterns for
Using response patterns does not affect the functionality of any of the other methods
used to retrieve the response such as RestCall.getResponseAsString()
or RestCall.getResponse(Class)
.
HOWEVER, if you want to retrieve the entire text of the response from inside the match methods,
use RestCall.getCapturedResponse()
since this method will not absorb
the response for those other methods.
The RestCall
class provides various convenience
If you want to pipe output without any intermediate buffering, you can use the
RestCall.byLines()
method.
This will cause the output to be piped and flushed after every line.
This can be useful if you want to display the results in real-time from a long running process producing
output on a REST call.
Use the RestClientBuilder.debug()
method to enable logging for HTTP requests
made from the client.
Under-the-covers, this is simply a shortcut for adding the RestCallLogger.DEFAULT
interceptor to the client.
This causes the following output to be generated by the Java
=== HTTP Call (outgoing) ======================================================= === REQUEST === POST http://localhost:10000/testUrl HTTP/1.1 ---request headers--- Debug: true No-Trace: true Accept: application/json ---request entity--- Content-Type: application/json ---request content--- {"foo":"bar","baz":123} === RESPONSE === HTTP/1.1 200 OK ---response headers--- Content-Type: application/json;charset=utf-8 Content-Length: 21 Server: Jetty(8.1.0.v20120127) ---response content--- {"message":"OK then"} === END ========================================================================
This setting also causes a
=== HTTP Request (incoming) ==================================================== HTTP POST /testUrl ---Headers--- Host: localhost:10000 Transfer-Encoding: chunked Accept: application/json Content-Type: application/json User-Agent: Apache-HttpClient/4.5 (Java/1.6.0_65) Connection: keep-alive Debug: true Accept-Encoding: gzip,deflate ---Default Servlet Headers--- ---Body--- {"foo":"bar","baz":123} === END ========================================================================
Use the RestClientBuilder.logTo(Level,Logger)
and
RestCall.logTo(Level,Logger)
methods to log HTTP calls.
These methods will cause the HTTP request and response headers and body to be logged to the specified logger.
The method call is ignored if the logger level is below the specified level.
Customized logging can be handled by sub-classing the RestCallLogger
class and using the RestCall.interceptor(RestCallInterceptor)
method.
The RestClientBuilder.interceptors(RestCallInterceptor...)
and
RestCall.interceptor(RestCallInterceptor)
methods can be used to
intercept responses during specific connection lifecycle events.
The RestCallLogger
class is an example of an interceptor that uses
the various lifecycle methods to log HTTP requests.
The RestClientBuilder.rootUrl(Object)
method can be used to specify a
root URL on all requests so that you don't have to use absolute paths on individual calls.
The RestClientBuilder.set(String,Object)
method can be used to
set serializer and parser properties.
For example, if you're parsing a response into POJOs and you want to ignore fields that aren't on the
POJOs, you can use the BeanContext.BEAN_ignoreUnknownBeanProperties
property.
The RestCall.retryable(int,long,RetryOn)
method can be used to
automatically retry requests on failures.
This can be particularly useful if you're attempting to connect to a REST resource that may be in the
process of still initializing.
juneau-rest-mock-8.1.2.jar
org.apache.juneau.rest.mock_8.1.2.jar
The
The API consists of the following classes:
org.apache.juneau.rest.mock2
MockRest
- API for unit testing @Rest
-annotated classes.
MockRemote
- API for unit testing @Remote
-annotated classes.
The MockRest
class is used for performing serverless unit testing of @Rest
-annotated
classes. These include both parent resource classes that extend from RestServlet
and child resources that do not.
The API consists of the following classes:
org.apache.juneau.rest.mock2
MockRest
MockServletRequest
HttpServletRequest
with additional convenience methods for building requests.
MockServletResponse
HttpServletRequest
with additional convenience methods for testing responses.
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 MockRest
class is used to create instances of MockServletRequest
and MockServletResponse
which are passed directly to the call handler on the resource class RestCallHandler.service(HttpServletRequest,HttpServletResponse)
.
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.
The
By default, the MockRest
class specifies the following request headers:
Accept: application/json+simple Content-Type: application/json
The reason for using
Other media types headers can be specified via any of the following methods:
build(Object,Marshall)
- Use media types defined on a marshall.
build(Object,Serializer,Parser)
- Use media types defined on a serializer and parser.
accept(String)
- Explicitly set the contentType(String)
- Explicitly set the Various other convenience methods for common media types are also provided.
The following examples are functionally equivalent for specifying XML serialization:
MockRest mr;
mr = MockRest.
The MockRest
class provides the following methods for creating requests:
For HTTP methods that pass a request body (i.e.
Reader
InputStream
CharSequence
All other body object types are converted to strings using the
A common tactic is to override a bean's
The MockServletRequest
class provides default implementations for all the methods defined
on the HttpServletRequest
in addition to many convenience methods.
The following fluent convenience methods are provided for setting common
The following fluent convenience methods are provided for building up your request.
Fluent setters are provided for all common request headers:
The MockServletResponse
class provides default implementations for all the methods defined
on the HttpServletResponse
in addition to many convenience methods.
The MockRest
class has a debug mode that will cause your HTTP requests and responses to
be sent to the console:
MockRest mr = MockRest
.
The MockRemote
class is used for serverless unit testing of @Remote
-annotated
classes.
The MockRemote
API requires a @Rest
-annotated class to be used as
an underlying mocked resource to process the request and return a response.
It looks simple, but there's a lot going on here.
Remote resource interfaces are normally created through the RestClient.getRemote(Class)
method.
The MockRemote
will create a RestClient
using a specialized MockServletRequest
/MockServletResponse
objects and then pass those to the MockRest
object described in the previous section.
All aspects of the client and server side code are tested, yet no servlet container is required. The actual over-the-wire serialization is the only aspect being bypassed.
By default, the MockRemote
class uses JSON marshalling.
This can be overridden via any of the following methods:
MockRemote.build(Class,Object,Marshall)
MockRemote.build(Class,Object,Serializer,Parser)
MockRemote.create(Class,Object,Marshall)
MockRemote.create(Class,Object,Serializer,Parser)
The MockRemote
class has a debug mode that will cause your HTTP requests and responses to
be sent to the console on both the client and server sides:
juneau-microservice-core-8.1.2.jar
org.apache.juneau.microservice.core_8.1.2.jar
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:
org.apache.juneau.microservice
Microservice
- The base microservice class.
MicroserviceBuilder
- Builder for the microservice class.
MicroserviceListener
- Interface for hooking into lifecyle events of the microservice.
BasicMicroserviceListener
- Adapter for MicroserviceListener class.
org.apache.juneau.microservice.console
ConsoleCommand
- Abstract class for defining console commands.
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 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 Microservice.getInstance()
method
from anywhere in your code:
The Microservice.startConsole()
and 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 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 MicroserviceBuilder.args(String...)
method.
When specified, the arguments can be retrieved using the Microservice.getArgs()
method which provides
an API for easily accessing command-line arguments using common notation:
Args a = Microservice.
Specifying the command-line arguments also makes them available through $A
SVL variables.
These can be used in the configuration file and throughout various Juneau annotations.
The MicroserviceBuilder.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:
ManifestFile
- A pre-parsed manifest file.
Manifest
- A pre-parsed manifest file.
Reader
- Containing the raw contents of the manifest.
InputStream
- Containing the raw contents of the manifest.
File
- File containing the raw contents of the manifest.
String
- Path to file containing the raw contents of the manifest.
Class
- Finds and loads the manifest file of the jar file that the specified class is contained within.
The manifest file can be retrieved using the the Microservice.getManifest()
method which
provides an API for accessing manifest file entries.
This method returns an instance of ManifestFile
which extends from ObjectMap
allowing
you to retrieve entries as any data types supported by that class.
ManifestFile mf = Microservice.
The manifest is also used for the $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 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 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 config(Config)
method can be used to explicitly specify a Config
file as the microservice configuration. When this method is used, the above two methods are bypassed entirely.
Config config =
Once the configuration is resolved, it is made as the system default configuration available through the Config.getSystemDefault()
.
This in turn allows it to be used by REST resources that reference the system default configuration via the BasicRestConfig
interface.
The Microservice.getConfig()
method can be used to get access to the configuration.
Config c = Microservice.
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 Microservice.onConfigChange(ConfigEvents)
or implementing
a listener and using the 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 Overview > juneau-microservice-jetty
By default, support for the following variables are provided:
SystemPropertiesVar
EnvVariablesVar
ArgsVar
ConfigVar
ManifestFileVar
IfVar
SwitchVar
CoalesceVar
PatternMatchVar
UpperCaseVar
LowerCaseVar
NotEmptyVar
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 consoleCommands(ConsoleCommand...)
builder method.
The API for defining console commands is shown below:
By default, the console input and output are taken from System.in
and System.out
.
These can be overridden using the console(Scanner,PrintWriter)
method.
The Microservice API provides build-in but configurable and overridable support for logging.
The method for configuring logging is as follows:
If not specified, the logging configuration is pulled in from the configuration file:
The logging configuration can also be defined programmatically through the following API:
If you wish to bypass the default logging configuration entirely, you can pass in your own logger via
the MicroserviceBuilder.logger(Logger)
method.
In addition to configuring the built-in Java logging framework, the following convenience methods are also
provided on the Microservice
class for logging.
As mentioned previously, the lifecycle methods for the 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 Microservice.onConfigChange(ConfigEvents)
method can also be overridden
to react to configuration changes as well:
juneau-microservice-jetty-8.1.2.jar
org.apache.juneau.microservice.jetty_8.1.2.jar
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 Microservice classes to produce easy-to-create and easy-to-use microservices in a standard Java 1.8+ environment.
The
org.apache.juneau.microservice.jetty
JettyMicroservice
- The Jetty microservice class.
JettyMicroserviceBuilder
- Builder for the microservice class.
JettyMicroserviceListener
- Interface for hooking into lifecyle events of the microservice.
BasicJettyMicroserviceListener
- Adapter for JettyMicroserviceListener class.
JettyServerFactory
- Interface for defining custom Jetty servers.
BasicJettyServerFactory
- Adapter for JettyServerFactory class.
The most-basic creation of a Jetty microservice from an entry-point method is shown below:
To review, the Microservice
class contains the following lifecycle methods:
The JettyMicroservice
class which extends from 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 Microservice.getInstance()
, the 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
BasicRestServlet
or other resource groups.
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 JettyMicroserviceBuilder.servlet(Class)
method:
However, there are multiple ways of deploying top-level resources:
JettyMicroserviceBuilder.servlet(Class)
- Using the builder. Several methods provided.
JettyMicroservice.addServlet(Servlet,String)
- After the Jetty container has been started.
The following predefined resource classes are also provided for easy inclusion into your microservice:
ConfigResource
- View and modify the external INI config file.
DirectoryResource
- View and modify file system directories.
LogsResource
- View and control generated log files.
SampleRootResource
- A sample root resource class to get started from.
ShutdownResource
- Shutdown and/or restart the JVM.
In Overview > juneau-microservice-core > 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 $C
variable in annotations.
For example, the BasicRestConfig
interface that defines the annotations that control the look-and-feel of
classes that extend from 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:
Microservice.getConfig()
Any initialization-time variables can be used.
RestContext.getConfig()
Any initialization-time variables can be used.
Additional user-defined variables at the servlet level can be defined by adding a
HookEvent.INIT
hook method
and using the RestContextBuilder.vars(Class...)
method.
RestRequest.getConfig()
- An instance method to access it from inside a REST method.
Any initialization-time or request-time variables can be used.
Additional user-defined variables can be defined at this level by overriding the
RestContextBuilder.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
JettyMicroserviceBuilder.jettyXml(Object,boolean)
method to specify the location or contents
of the file.
SVL variables in the
The HTTP port used is controlled via the following:
JettyMicroserviceBuilder.ports(int...)
method.
JettyMicroservice
.
The first available port is then made available through the system property
The JettyMicroserviceBuilder.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 BasicRestConfig
interface (which defines the default settings for BasicRestServlet
pulls in this information using $C
and $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 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 JettyMicroserviceBuilder
class as well:
my-jetty-microservice-8.1.2.zip
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 'my-jetty-microservice.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 'my-jetty-microservice.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-8.1.2.zip
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 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:
@JuneauRestRoot
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)
juneau-examples-core-8.1.2.zip
The
Follow these instructions to import the Juneau project into Eclipse.
The following shows the core examples provided:
org.apache.juneau.examples.core.json
JsonSimpleExample
- JsonSerializer and JsonParser usage on serialize and deserialize simple Pojo bean.
JsonComplexExample
- JsonSerializer and JsonParser usage on serialize and deserialize complex Pojo bean.
JsonConfigurationExample
- Json Serializers configured using properties defined in JsonSerializer class.
org.apache.juneau.examples.core.xml
XmlSimpleExample
- XmlSerializer and XmlParser usage on serialize and deserialize simple Pojo bean.
XmlComplexExample
- XmlSerializer and XmlParser usage on serialize and deserialize complex Pojo bean.
XmlConfigurationExample
- XmlSerializers configured using properties defined in XmlSerializer class.
org.apache.juneau.examples.core.dto
DtoExample
- Usage of core dto module.
org.apache.juneau.examples.core.rdf
RdfExample
- RdfXmlSerializer usage on serialize simple Pojo bean.
RdfComplexExample
- RdfXmlSerializer usage on serialize complex Pojo bean.
org.apache.juneau.examples.core.html
HtmlSimpleExample
- HtmlSerializer usage on serialize and deserialize simple Pojo bean.
HtmlComplexExample
- HtmlSerializer usage on serialize and deserialize complex Pojo bean.
org.apache.juneau.examples.core.uon
UONExample
- UonSerializer usage on serialize and deserialize simple Pojo bean.
UONComplexExample
- UonSerializer usage on serialize and deserialize complex Pojo bean.
org.apache.juneau.examples.core.svl
SvlExample
- Usage of Svl module in juneau.
juneau-examples-rest-8.1.2.jar
org.apache.juneau.examples.rest_8.1.2.jar
The
Note: These examples can be deployed as buildable Eclipse projects using Jetty or Spring Boot using instructions in the following sections:
The
The class hierarchy for this class is:
RestServlet
- Contains all the REST servlet logic.
BasicRestServlet
- Defines default serializers and parsers, and OPTIONs page logic.
BasicRestServletGroup
- Specialized subclass for grouping other resources.
BasicRestServletJenaGroup
- Group resource with added RDF support.
Pointing a browser to the resource shows the following:
http://localhost:10000
The
The
The
Child resources must be annotated with the @Rest(path)
annotation to
identify the subpath of the child.
Children CAN extend from BasicRestServlet
but it is not a requirement.
Note that these router pages can be arbitrarily nested deep. You can define many levels of router pages for arbitrarily hierarchical REST interfaces.
Let's step back and describe what's going on here:
During servlet initialization of the
When a request for the child URL (
The
Notice that in this case we're not extending from RestServlet
.
We are however implementing BasicRestConfig
which is a no-op
interface that defines a default BasicRestServlet
class.
The only difference between implementing
All other examples in this project extend from
Pointing a browser to the resource shows the following:
http://localhost:10000/helloWorld
Using the special
http://localhost:10000/helloWorld?Accept=text/json&plainText=true
The
The
Pointing a browser to the resource shows the following:
http://localhost:10000/atom
True ATOM feeds require using an
http://localhost:10000/atom?Accept=text/xml&plainText=true
Other languages, such as JSON are also supported:
http://localhost:10000/atom?Accept=text/json&plainText=true
The
JSON Schema DTO
API.
The resource consists of a pre-initialized JsonSchema
object.
Pointing a browser to the resource shows the following:
http://localhost:10000/jsonSchema
For true JSON-Schema, you need to specify the header
http://localhost:10000/jsonSchema?Accept=text/json&plainText=true
The ConfigResource
class is a predefined reusable resource.
It provides a REST interface for reading and altering the microservice config file.
Pointing a browser to the resource shows the following:
http://localhost:10000/config
An edit page is provided for altering the raw config file:
http://localhost:10000/config/edit
The Config
class is a serializable POJO, which makes the resource
relatively straightforward to implement.
The LogsResource
class is a reusable predefined resource.
It provides a REST interface for the log files generated by the microservice.
Pointing a browser to the resource shows the following:
http://localhost:10000/logs
juneau-examples-rest-jetty-8.1.2.zip
The
Follow these instructions to import the REST examples project using Jetty into 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 -> juneau-examples-rest-jetty and click Run. In your console view, you should see the following output:
Running class 'JettyMicroservice' using config file 'juneau-examples-rest-jetty.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 the 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 juneau-examples-rest-jetty-1.0.jar
You should see the following console output:
Running class 'JettyMicroservice' using config file 'juneau-examples-rest-jetty.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.
juneau-examples-rest-springboot-8.1.2.zip
The
Follow these instructions to import the REST examples project using Spring Boot into Eclipse.
The important elements in this project are:
@JuneauRestRoot
At this point, you're ready to start the microservice from your workspace.
The
Go to Run -> Run Configurations -> Java Application -> juneau-examples-rest-springboot 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 juneau-examples-rest-springboot-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)
Heroku is a platform-as-a-service that allows applications to be quickly hosted
in the cloud.
The
{
web: java -jar target/juneau-examples-rest-springboot-8.0.0.jar
You'll need to sign up for an account on Heroku. Afterwards, you can go to the apps page to create a new application:
https://dashboard.heroku.com/apps
Click the
https://dashboard.heroku.com/new-app
After clicking the
https://dashboard.heroku.com/apps/juneau-examples-rest/deploy/heroku-git
For this example, we'll use the
https://dashboard.heroku.com/apps/juneau-examples-rest/deploy/heroku-git
Next, run the following commands to cd into our Eclipse project and initialize it as a local git repo:
$ cd juneau-examples-rest-springboot/ $ git init
$ cd juneau-examples-rest-springboot/ $ git init Initialized empty Git repository in /.../juneau-examples-rest-springboot/.git/
Next, run the following command to link our project to the Heroku app:
$ heroku git:remote -a juneau-examples-rest
$ heroku git:remote -a juneau-examples-rest set git remote heroku to https://git.heroku.com/juneau-examples-rest.git
Next, run the following commands to add our files to the git repo and push to the Heroku master branch:
$ git add . $ git commit -am "Initial deploy" $ git push heroku master
$ git add . master (root-commit) 7c94cb9] Initial deploy 123 files changed, 11986 insertions(+) Counting objects: 127, done. $ git commit -am "Initial deploy" $ git push heroku master Delta compression using up to 8 threads. Compressing objects: 100% (113/113), done. Writing objects: 100% (127/127), 363.91 KiB | 21.41 MiB/s, done. ... remote: -----> Compressing... remote: Done: 85.9M remote: -----> Launching... remote: Released v3 remote: https://juneau-examples-rest.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/juneau-examples-rest.git * [new branch] master -> master
If no errors were shown, then our application should now be live.
You can click on the
If everything worked, your browser should now be loaded with our example REST app:
https://juneau-examples-rest.herokuapp.com
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:
Note that the JsoParser
, a thin layer of the Juneau Parser API written on
top of plain-old Java Object Serialization which itself is vulnerable to demarshalling issues.
Due to this, the JSO parser is not included in any of the default REST servlet implementations.
Be especially careful when using this parser, particularly if you want to use it for handing
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 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:
Var.allowNested()
and
Var.allowRecurse()
that can be overridden to prevent recursive processing
of string variables. These are both
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
Var.allowNested()
and Var.allowRecurse()
methods to prevent recursive handling of variables.
Denial of service attacks can be alleviated through the 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.
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.
BeanContext
.BeanFilter
- Filter POJO beans.PojoSwap
- Filter POJOs that aren't beans.PojoIntrospector
class.
HtmlParser
and UrlEncodingParser
classes.
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.
SerializerGroups
and ParserGroups
now share BeanContexts
to reduce memory consumption of class type metadata.
Juno 5.0.0.4 is a minor update.
@RestMethod
annotation on RestServlet
methods.RestServlet
for more information.
Juno 5.0.0.5 is a major update.
@RestMethod
annotation.HtmlSerializer
.XmlSerializer
.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.
ParserListener
class.XmlParser
:
@Xml.format
annotation.BeanContext
BeanContext
:
ClassMeta
API.PropertyNamer
API / @Bean.propertyNamer
annotation.URL
and URI
objects.
PojoQuery
support.
@RestMethod(guards)
- Associate method-level guards.@Method
- The HTTP method name as a String.RestResponse.setOutput(Object)
.
Juno 5.0.0.8 is a minor update.
INI file
support.
Juno 5.0.0.9 is a moderate update.
INI config file support
:
@Bean
annotation to override bean identification settings.ObjectMap.cast(Class)
method to convert @RestMethod(serializers)
and @RestMethod(parsers)
annotations.@RestMethod(properties)
annotation.@RestMethod(defaultRequestHeaders)
annotation.@RestMethod(matchers)
annotation and RestMatcher
class.Juno 5.0.0.10 is a minor update.
ObjectMap.findKeyIgnoreCase(String)
method.
Juno 5.0.0.11 is a moderate update.
BasicRestServlet
.
Juno 5.0.0.12 is a minor update.
RestRequest.getInputStream()
and RestRequest.getReader()
.
Juno 5.0.0.13 is a minor update.
@URI
annotation that allows you to specify classes and bean properties as URLs that aren't HtmlSerializer.HTML_uriAnchorText
HTML serializer property for tailoring how anchor text is rendered.
RestServlet
to automatically handle relative URIs in POJOs.
Juno 5.0.0.14 is a major update.
The biggest change is that the
Instead, the existing Serializer
, 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.
WriterSerializer
base class for defining character-based serializers.OutputStreamSerializer
base class for defining byte-based serializers.SerializerGroup
class with full support for RFC2616 ReaderParser
base class for defining character-based parsers.InputStreamParser
base class for defining byte-based parsers.BeanFilter
class for defining property filters on beans.PojoQuery
class for defining filters on objects (previously called Encoders
for enabling compression in REST servlets and clients.GzipEncoder
class for enabling gzip compression.EncoderGroup
class for managing multiple encoders and finding them based on RFC2616 PlainTextSerializer
and PlainTextParser
classes for serializing/parsing text/plain content.JsoSerializer
class for serializing SoapXmlSerializer
class for serializing BeanContext
class.
ObjectMap
and ObjectList
to better reflect that they're not limited to just JSON support.
PojoQuery
to not confuse it with the new Filter API.
RestClient
API.
Encoders
.Juno 5.0.0.15 is a moderate update.
Serializer
.
Parser
.
Juno 5.0.0.16 is a minor update.
RestResponse
.
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.
JsoParser
class.
Juno 5.0.0.19 is a minor update.
RestServlet
:
Juno 5.0.0.20 is a major update.
RdfSerializer
for serializing POJOs to RDF/XML, RDF/XML-ABBREV, N-Triple, Turtle, and N3.RdfParser
for parsing RDF/XML, RDF/XML-ABBREV, N3, Turtle, and N-Triple back into POJOs.
@RestMethod
annotation:
RestServlet
:
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.
Cognos
support.
Juno 5.0.0.24 is a major update.
ATOM
.
@XmlSchema
and updated @XmlNs
annotations to better mimic JAXB.
CalendarSwap
.
ObjectMap.getObjectList(String)
were not updatable.
JSON Support Overview
document.XML Support Overview
document.RDF Languages Support Overview
document.ATOM Support Overview
document.Juno 5.0.0.25 is a minor update.
ResultSetList
DTO for serializing SQL result sets to JSON/XML/HTML and so forth.
Juno 5.0.0.26 is a minor update.
@RestMethod(path)
for naming consistency.
Juno 5.0.0.27 is a moderate update.
HtmlSerializer
.
BasicRestServlet
now includes PlainTextSerializer
and PlainTextParser
for plain-text support.
UrlEncodingSerializer
/UrlEncodingParser
to reduce the need for quoted string values.Juno 5.0.0.28 is a moderate update.
Juno 5.0.0.29 is a moderate update.
BeanFilter
class to mirror the @Bean
annotation.JsonSerializer
LAX mode now quotes reserved word attributes.
Juno 5.0.0.30 is a minor update.
Juno 5.0.0.31 is a moderate update.
Serializer
and Parser
class hierarchies.XMLGregorianCalendarSwap
to convert these to ISO8601 strings during serialization, and vice versa during parsing.
JsonParser
.
JsonParser.DEFAULT_STRICT
parser.
Juno 5.0.0.32 is a moderate update.
Parser
:
@Bean
annotation can now be defined on interfaces and inherited by subclasses.
JsonType
enum to be serialized to lowercase per the specification (e.g. Cognos
DTOs now have fluent-style bean setters.
BeanMap
API would sometime choose the wrong setter as the bean property setter. Number
.Juno 5.0.0.33 is a moderate update.
WriterSerializer
class.
BeanContext.BEAN_notBeanPackages_add
/ BeanContext.BEAN_notBeanPackages_remove
properties.
JsonSchemaMap
class for supporting linked schemas.
RestServlet
. Juno 5.0.0.34 is a moderate update.
RestRequest
:
RestResponse
:
RestServlet
that allow easier customization by subclasses:
MessageFormat
(e.g. @Bean.stopClass
annotation for specifying stop classes for bean properties.
ResultSetList
:
Juno 5.0.0.35 is a minor update.
RestGuard.guard(RestRequest,RestResponse)
now returns a boolean to allow redirects to login pages.
Juno 5.0.0.36 is a minor update.
ClassMeta
that allowed the removal of
some synchronized blocks.
StringUtils
and ClassUtils
.
Juno 5.1.0.0 is a major update.
ReaderParser
class.
ParserReader
was enhanced to work
well with StringUtils
and ClassUtils
.
RestRequest
now correctly handles cases involving URL-encoded characters in the
path info portion of URLs (e.g. ClassMeta
.
RestRequest
class.
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.
BeanContext.BEAN_beanConstructorVisibility
- Control which bean constructors are visible to Juno.
BeanContext.BEAN_beanClassVisibility
- Control which bean classes are interpreted as beans to Juno.
BeanContext.BEAN_beanFieldVisibility
- Control which fields are visible to Juno as bean properties.
BeanContext.BEAN_beanMethodVisibility
- Control which getters/setters are visible to Juno as bean properties.
NONE
.
Also, the BeanContext.BEAN_useJavaBeanIntrospector
property that lets Juno use the Java bean @BeanIgnore
annotation. Replaces the previous @Json(wrapperAttr)
annotation that automatically wraps beans and objects in a wrapper
attribute when serializing to or parsing from JSON.
readProperty()
and writeProperty()
methods added to BeanFilter
class to allow individualized serialization and parsing behavior on a class-by-class basis.
BeanMap.load()
methods for initializing bean maps.
HtmlDocSerializer
will now embed the data portion of the output in a RestRequest.getJavaMethod()
method for getting access to the method used to handle a request.
Useful for accessing the method name or annotations during requests, such as in calls to RestGuard.guard(RestRequest,RestResponse)
.
Juno 5.1.0.4 is a minor update.
Juno 5.1.0.5 is a moderate update.
ResponseHandler
class and RestResponse.getNegotiatedOutputStream()
that uses encoders if a match is found,
and RestResponse.getOutputStream()
that just return the underlying output stream without any modifications.
HtmlSerializer
where URL keys in Maps were not being serialized as hyperlinks.
JsonSerializer
where RestResponse.getOutputStream()
.
These should not be set if interacting with the output streams at a low level.
Juno 5.1.0.6 is a moderate update.
PojoSwap
.
Since it's rarely used, the PojoSwap
.
See SurrogateSwap
for details.
@Html
annotation.
Will allow the definition of standard XHTML DTOs in future releases.
For now, JsonParser
now ignores trailing Juno 5.1.0.7 is a moderate update.
ResultSetList
for Oracle and SQL Server.
XmlParser
now accepts Traversable
that was causing it to be executed even if the servlet extra path info was empty.
Traversable
where it was not picking up filters and properties defined on REST Java methods.
Juno 5.1.0.8 is a moderate update, focused primarily on performance improvements.
ParserReader
class to handle it's own buffering.
The change allowed several optimizations to be made when dealing with JSON and URL-Encoding
text by avoiding char array copies.
Parser
parse methods to
optimize buffering when the input size is known beforehand.
BeanContext
API to perform better in multi-threaded environments.
BeanContext
objects.
This allows 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.
Encoder
class changed to Juno 5.1.0.10 is a moderate update.
UrlEncodingSerializer
and UrlEncodingParser
classes.
UonSerializer
and UonParser
classes.
BeanContext.getClassMeta(Class)
.
ClassMeta.getPrimitiveDefault()
method for performance reasons.
ClassMeta.newInstance()
can now create array instances.
HtmlSerializer
.
HtmlSerializer
where newlines were not being converted into line breaks.
WriterSerializer.toString(Object)
method that's identical to the serialize method but throws UonSerializer
and UonParser
to serializer and parser lists on
BasicRestServlet
and RestRequestEntity
.
RestClient
class that makes it easier to associate serializer and parser attributes with registered serializer and parser:
RestClient.close()
to mirror change in Apache API.
Juno 5.1.0.11 is a moderate update.
@Html
bean annotation.
@Html(noTables)
annotation that prevents
arrays/Collections from being serialized as tables.
@Html(noTableHeaders)
annotation that prevents
HTML tables from having header rows.
UonParser
.
@UrlEncoding(expandedParams)
annotation that specifies that bean properties of type array/Collection be serialized as multi-part parameters (e.g. TeeOutputStream
and TeeWriter
classes.
ClassMeta.isInstance(Object)
method.
BeanMap.add(String,Object)
method.
Array properties are stored in a temporary list cache until BeanMap.getBean()
is called.
RestRequest
for handling multi-part parameters:
RestResponse.setHeader(String,String)
where setting
the BasicRestServlet
and RestCall.allowRedirectsOnPosts(boolean)
.
Session.toString()
now useful for debugging purposes.
Shows all request/response headers and bodies.
RestCallException
now includes Juno 5.1.0.12 is a minor update.
UonParser
to not treat RestCallInterceptor
.
Allows responses to be inspected and modified before being processed.
Replaces Juno 5.1.0.13 is a minor update.
ClassMeta.newInstance()
method can now create new instances of arrays.
UrlEncodingSerializer
, so arbitrary POJOs can now be passed as arguments.
HtmlDocSerializer
class.
Adds UonParser
where passing in a blank value on an array or collection type in a form post would cause a RestConverter
API fixed to handle the existence of POJO filters.
Introspectable
/Queryable
/Traversable
classes can now work with filtered POJOs.
Juno 5.1.0.14 is a moderate update.
The major addition is support for
PojoIntrospector
class.
RestClient
for working with remoteable services:
BasicRestServlet
and RestRequest
:
Juno 5.1.0.15 is a minor update.
CsvSerializer
.
RestCall
:
Juno 5.1.0.16 is a moderate update.
ClassMeta
that eliminates language-specific code in
the general class metadata.
JsonType.ANY
enum.
@Rdf
and @RdfSchema
annotations. These replace the use of defining namespaced through the XML annotations, and allows XML and RDF to be
serialized using different namespaces.
Juno 5.1.0.17 is a major update.
BeanMap.get(Object)
and BeanMap.put(String,Object)
now
automatically performs filtering if filters are defined on the bean property or bean property class.
BeanPropertyMeta.getClassMeta()
now returns the filtered type of the property.
MultiSet
where exception was being thrown if last set was empty.
ZipFileList
class for providing efficiently zipped directories through the REST interface.
RestRequest
:
RestRequest.getMethodDescription()
HttpServletRequestWrapper.getPathInfo()
to follow Servlet specs.
Returns RestRequest.getContextPath()
RestRequest.getMethodDescription()
BasicRestServlet
and
BasicRestServlet
and
BasicRestServletGroup
class.
@RestMethod(description)
Juno 5.1.0.18 is a minor update affecting the server component only.
RestServlet
:
Juno 5.1.0.19 is a minor update in terms of core functionality.
But it introduces a
ObjectMaps
.
See ObjectMap.include(String[])
and ObjectMap.exclude(String[])
methods.
@Html
annotations can now be applied to bean properties.
IOPipe
utility class.
RestClient.doCallback(String)
method.
RestRequest.getHeaders()
method.
@Bean.properties
annotations to the various
classes in
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
SimpleHtmlWriter
class.
Can be used for simple HTML DOM construction.
ProcBuilder
class for calling external processes.
HtmlDocSerializer
.
HtmlStrippedDocSerializer
where exception was thrown when trying to serialize primitive arrays.
JsonParser
now handles parsing JSON boolean/numeric values as strings to bean properties of type boolean or number.
UrlEncodingSerializer
and UrlEncodingParser
now
represent arrays and collections as key-value pairs where the keys are numbers (e.g. IOPipe
.
StringUtils.parseNumber(String,Class)
now returns zero for empty strings.
This affects the way most parsers handle blank values.
RestServlet
:
BasicRestServlet
now automatically processes RestRequest
methods:
RestResponse
methods:
RestResponse.getDirectWriter(String)
.
RestResponse.getNegotiatedWriter()
.
@RestMethod(encoders)
and
RestMatchers
:
Juno 5.2.0.0 is a major update. Major changes have been made to the microservice architecture and config INI file APIs.
Writable
interface so that it can be serialized by the REST interface as a plain-text INI file instead of as a serialized POJO.
@NameProperty
and @ParentProperty
annotations are provided for identifying methods for setting names and parent POJOs on child POJOs.
For example, the config file Streamable
interface for identifying objects that can be serialized directly to an output stream.
Writable
interface for identifying objects that can be serialized directly to a writer.
ByteArrayInOutStream
FileUtils
ThrowableUtils
ObjectList
:
ObjectMap
:
ArrayUtils
:
IOUtils
:
IOUtils.read(File)
IOUtils.readFile(String)
IOUtils.write(File,Reader)
PojoRest
:
PojoRest.getString(String)
PojoRest.getString(String,String)
PojoRest.getInt(String)
PojoRest.getInt(String,Integer)
PojoRest.getLong(String)
PojoRest.getLong(String,Long)
PojoRest.getBoolean(String)
PojoRest.getBoolean(String,Boolean)
PojoRest.getMap(String)
PojoRest.getMap(String,Map)
PojoRest.getList(String)
PojoRest.getList(String,List)
PojoRest.getObjectMap(String)
PojoRest.getObjectMap(String,ObjectMap)
PojoRest.getObjectList(String)
PojoRest.getObjectList(String,ObjectList)
ProcBuilder
:
StringUtils
:
StringUtils.isEmpty(Object)
StringUtils.nullIfEmpty(String)
StringUtils.base64EncodeToString(String)
StringUtils.base64Encode(byte[])
StringUtils.base64DecodeToString(String)
StringUtils.base64Decode(String)
StringUtils.generateUUID(int)
StringUtils.trim(String)
StringUtils.replaceVars(String,Map)
StringUtils.pathStartsWith(String,String)
StringUtils.pathStartsWith(String,String[])
StringUtils.base64Encode(byte[])
method.
CalendarSwap
and DateSwap
classes now handle blank input better. Returns HtmlDocSerializer
specifies the default CSS location as HtmlDocSerializer
wraps output in two div tags instead of one (e.g. HtmlDocSerializer
.
Parser
methods now check for SerializerGroup
and ParserGroup
ignores serializers and parsers if they throw UrlEncodingParser
creates lists if the same attribute name is encountered more than once. Before it would just replace the previous value with the new value.
Args
:
Args.getArg(int)
.
Non-existent arguments are returned as Args.hasArg(int)
method.
MultiIterable
class.
PojoIntrospector
must now be instantiated with a PojoRest
must now be instantiated with a MessageBundle
and TeeWriter
and TeeOutputStream
.
AsciiSet
.
SerializerGroup
and ParserGroup
now ignores FileUtils.createTempFile(String)
method.
PojoQuery
modified to handle bean getters that throw exceptions.
RestCall
:
RestCall.pipeTo(Writer)
RestCall.pipeTo(Writer,boolean)
RestCall.pipeTo(String,Writer,boolean)
RestCall.getWriter(String)
RestCall.pipeTo(OutputStream)
RestCall.pipeTo(OutputStream,boolean)
RestCall.pipeTo(String,OutputStream,boolean)
RestCall.getOutputStream(String)
RestCall.byLines()
RestCall.captureResponse()
RestCall.successPattern(String)
RestCall.failurePattern(String)
RestCall.run()
- Renamed from RestCall.getCapturedResponse()
RestCall.getResponsePojoRest(Class)
RestCall.getResponsePojoRest()
RestCall.logTo(Level,Logger)
RestCall.setConfig(RequestConfig)
RestCallInterceptor
:
RestClient
:
RestClient.doCall(HttpMethod,Object,Object)
RestClient
defined on RestException
.
RestRequest
:
RestResponse
:
RestServlet
:
JsoParser
from BasicRestServlet
and Streamable
.
Writable
.
Microservice
class that serves as a generic
interface for microservices and their lifecycles.
ConfigResource
- REST resource for viewing and editing microservice config file.
LogsResource
- REST resource for viewing log files.
SampleRootResource
- Sample REST resource that contains the config and logs resource as children.
ShutdownResource
- REST resource for stopping the microservice JVM. Useful for testing purposes.
Juno 5.2.0.1 is a moderate update.
XmlSerializer
where ObjectList
:
ObjectList.putAt(String,Object)
ObjectList.postAt(String,Object)
ObjectList.deleteAt(String)
ObjectMap
:
ObjectMap.putAt(String,Object)
ObjectMap.postAt(String,Object)
ObjectMap.deleteAt(String)
ManifestFile
class
MessageBundle
class. Replaces JuneauLogger
class.
RestContext
:
RestClient
where the HTTP connection pool could end up exhausted if an error occurred.
RestClient
.
RestClient
is garbage collected without being closed:
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.
PropertyStore
class - Used for creating context objects.
Context
class - Read-only configurations for serializers and parsers.
Session
class - One-time use objects used by serializers and parsers.
StreamedVar
.
@Bean(typeName)
- Annotation that defines an identifying name for a bean class.
BeanFilterBuilder.typeName(String)
- Programmatic equivalent to annotation above.
BeanContext.BEAN_beanDictionary
- List of bean classes that make up the bean dictionary for lookup
during parsing.
BeanContext.BEAN_beanTypePropertyName
- The overridable type property name. Default is BeanContext.BEAN_beanDictionary
.
@Bean(typeName)
value replaces the BeanFilterBuilder
class.
Allows the MessagePack
support.
Files
.
See Serializer.serialize(Object,Object)
Files
and other types.
See Parser.parse(Object,ClassMeta)
Parser
:
Serializer
:
@Bean(sort)
annotation.
Class
objects to readable names via SerializerGroup
and ParserGroup
.
HtmlSerializer
:
BeanPropertyMeta
.
Swagger DTOs
.
RestInfoProvider.getTitle(RestRequest)
RestInfoProvider.getDescription(RestRequest)
@RestMethod(summary)
/ @RestMethod(description)
/RestServlet
:
RestMethod.clientVersion()
- The client version range applied to a Java method.
Juneau 6.0.1 is a minor update.
Parser.parse(Object,Class)
for reader-based parsers.
Juneau 6.1.0 is a major update.
In particular, this release cleans up the BeanContext
API to match
the PropertyStore
/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.
XmlFormat.ATTRS
format can now be applied to bean classes to have all bean properties serialized
as attributes instead of elements by default.
XmlFormat.ELEMENT
format can now be applied to bean properties to override the XmlFormat.ATTRS
setting above on specific bean properties.
XmlFormat.ELEMENTS
format can be applied to a bean property of type array/Collection to represent the child elements.
XmlFormat.MIXED
format can be applied to a bean property of type array/Collection to represent mixed content (text + child elements).
XmlFormat.MIXED_PWS
format. Identical to XmlFormat.TEXT
format can be applied to a bean property of a single object to represent a text node as a child.
XmlFormat.TEXT_PWS
format. Identical to XmlFormat.XMLTEXT
format that's identical to XmlFormat.TEXT
except
XML content is not escaped and serialized directly as the child content. The parser will reconvert this to the original XML text during parsing.
XmlSerializer.DEFAULT
serializer now has namespaces disabled,
and XmlSerializer.DEFAULT_NS
has namespaces enabled. The 'XML-JSON' serializers have been eliminated.
BeanContext
class split into separate BeanContext
and BeanSession
classes.
BeanContext
.
SerializerSession
and ParserSession
now extend directly from BeanSession
.
BeanContext
:
BeanContext.BEAN_debug
- Debug setting. Replaces individual debug properties in the serializer and parser contexts.
BeanContext.BEAN_locale
- Specifies a default locale at the context level.
BeanContext.BEAN_timeZone
- Specifies a default timezone at the context level.
BeanContext.BEAN_mediaType
- Specifies a default media type at the context level.
Parser.parse(Object,Class)
- Normal method.
Parser.parse(Object,Type,Type...)
- Method for parsing into parameterized maps and collections.
PojoSwap
class. Now just two methods:
ClassMeta
class.
BeanSession.getMediaType()
method.
Allows for swaps and serializer/parser behavior to be tailored to individual media types.
Calendar
and Date
swaps:
CalendarSwap.ToString
,DateSwap.ToString
- To Strings
using the Date.toString()
method.
CalendarSwap.ISO8601DT
,DateSwap.ISO8601DT
- To ISO8601 date-time strings.
CalendarSwap.ISO8601DTZ
,DateSwap.ISO8601DTZ
- Same as CalendarSwap.ISO8601DTP
,DateSwap.ISO8601DTP
- Same as CalendarSwap.ISO8601DTPZ
,DateSwap.ISO8601DTPZ
- Same as CalendarSwap.RFC2822DT
,DateSwap.RFC2822DT
- To RFC2822 date-time strings.
CalendarSwap.RFC2822DTZ
,DateSwap.RFC2822DTZ
- Same as CalendarSwap.RFC2822D
,DateSwap.RFC2822D
- To RFC2822 date strings.
CalendarSwap.DateTimeSimple
,DateSwap.DateTimeSimple
- To simple CalendarSwap.DateSimple
,DateSwap.DateSimple
- To simple CalendarSwap.TimeSimple
,DateSwap.TimeSimple
- To simple CalendarSwap.DateFull
,DateSwap.DateFull
- To DateFormat.FULL
date strings.
CalendarSwap.DateLong
,DateSwap.DateLong
- To DateFormat.LONG
date strings.
CalendarSwap.DateMedium
,DateSwap.DateMedium
- To DateFormat.MEDIUM
date strings.
CalendarSwap.DateShort
,DateSwap.DateShort
- To DateFormat.SHORT
date strings.
CalendarSwap.TimeFull
,DateSwap.TimeFull
- To DateFormat.FULL
time strings.
CalendarSwap.TimeLong
,DateSwap.TimeLong
- To DateFormat.LONG
time strings.
CalendarSwap.TimeMedium
,DateSwap.TimeMedium
- To DateFormat.MEDIUM
time strings.
CalendarSwap.TimeShort
,DateSwap.TimeShort
- To DateFormat.SHORT
time strings.
CalendarSwap.DateTimeFull
,DateSwap.DateTimeFull
- To DateFormat.FULL
date-time strings.
CalendarSwap.DateTimeLong
,DateSwap.DateTimeLong
- To DateFormat.LONG
date-time strings.
CalendarSwap.DateTimeMedium
,DateSwap.DateTimeMedium
- To DateFormat.MEDIUM
date-time strings.
CalendarSwap.DateTimeShort
,DateSwap.DateTimeShort
- To DateFormat.SHORT
date-time strings.
SerializerGroup.getSerializerMatch(String)
that returns the matched serializer and media type.
ParserGroup.getParserMatch(String)
that returns the matched parser and media type.
EncoderGroup.getEncoderMatch(String)
that returns the matched encoder and encoding.
BeanDictionaryList
class can be used for defining reusable sets of bean dictionaries consisting
of classes annotated with @Bean(typeName)
.
BeanDictionaryMap
class can be used for defining reusable sets of bean dictionaries consisting
of classes not annotated with @Bean(typeName)
.
AtomBuilder
class.
MapSwap
and StringSwap
classes.
WriterSerializer.println(Object)
method. Useful for debugging purposes.
BeanContext.getClassMeta(Type,Type...)
and BeanSession.getClassMeta(Type,Type...)
methods for retrieving Map and Collection class metas.
Replaces the various UonParser.DEFAULT
and UrlEncodingParser.DEFAULT
parsers will now handle whitespace.
Parser.parse(Object,Class)
method can now
read the output from this method.
RestRequest
now passes locale and timezone to serializers/parsers/transforms.
RestRequest
to remove dependency on NameValuePairs
Juneau 6.2.0 is a major update.
XmlFormat.VOID
format to identify HTML void elements.
SwaggerBuilder
class.
VarResolver
.
$IF
variable for if-else block logic.
HtmlParser
can now parse full body contents generated by HtmlDocSerializer
.
MsgPackParser
to allow it to be used in remoteable proxies.
@Bean(typePropertyName)
annotation allows you to
specify the name of the UrlEncodingSerializerBuilder
:
RestServlet
.
ServletConfig
.
RestContext
- A read-only configuration that's the result of a snapshot of the config.
RestServlet
class now has the following initialization method that allows you to override
the config settings define via annotations:
RestResourceResolver
for instantiating it.
RestResourceResolver
class for resolving child resources.
RestCallHandler
class for handling the lifecycle of a REST call.
RestInfoProvider
class for customizing title/description/Swagger information on a REST resource.
RestLogger
class for handling logging.
@RestMethod
to simplify defining page title, text, and links on HTML views:
Typically you're going to simply want to use the
@RestMethod(name)
for more information.
RestRequest.toString()
can be called at any time to view the headers and content of the request
without affecting functionality. Very useful for debugging.
@RestMethod(name)
annotation is now optional. Defaults to RestClient
:
doPost(Object)
doCall(HttpMethod,Object,Object)
- Can now pass in instances of NameValuePairs
for easy form posts.
RestCall
:
uri(Object)
query(String,Object)
queryIfNE(String,Object)
query(Map)
queryIfNE(Map)
query(String)
formData(String,Object)
formDataIfNE(String,Object)
formData(Map)
formDataIfNE(Map)
header(String,Object)
headerIfNE(String,Object)
headers(Map)
headersIfNE(Map)
host(String)
port(int)
userInfo(String,String)
userInfo(String)
scheme(String)
RestClientBuilder
:
executorService(ExecutorService,boolean)
paramFormat(ExecutorService,boolean)
noTrace()
- Adds a debug()
now adds a RestCall
:
runFuture()
getResponseFuture(Class)
getResponseFuture(Type,Type...)
getResponseAsStringFuture()
serializer(Serializer)
- Override the serializer defined on the client for a single call.
parser(Parser)
- Override the parser defined on the client for a single call.
NameValuePairs
.
getResponse(Class)
- Can now pass in any of the following:
HttpResponse
- Returns the raw Reader
- Returns access to the raw reader of the response.
InputStream
- Returns access to the raw input stream of the response.
NameValuePairs
:
append(String,Object)
RetryOn
is now an abstract class with an additional method:
Microservice
class for easier method chaining.
Juneau 6.3.0 is a major update with significant new functionality for defining proxy interfaces against arbitrary 3rd-party REST interfaces.
org.apache.juneau.http
.
SerializerSession
and ParserSession
for retrieving context and runtime-override properties:
pages=
links=
@URI
.
SerializerListener
SerializerBuilder.listener(Class)
ParserListener
ParserBuilder.listener(Class)
BeanContext.BEAN_debug
flag will now capture parser input and make it
available through the @Html(render)
annotation and HtmlRender
class that allows you
to customize the HTML output and CSS style on bean properties:
HtmlDocTemplate
that allows full control over rendering
of HTML produced by HtmlDocSerializer
.
@NameProperty
and @ParentProperty
can now be applied to fields.
BeanContext
:
RestRequest
class functionality has been broken up into the following
functional pieces to reduce its complexity:
RestRequest.getBody()
- The request body.
RestRequest.getHeaders()
- The request headers.
RestRequest.getQuery()
- The request query parameters.
RestRequest.getFormData()
- The request form data parameters.
RestRequest.getPathMatch()
- The path variables and remainder.
RestRequest
- The request object.
HttpServletRequest
- The superclass of RestResponse
- The response object.
HttpServletResponse
- The superclass of Accept
AcceptCharset
AcceptEncoding
AcceptLanguage
Authorization
CacheControl
Connection
ContentLength
ContentType
Date
Expect
From
Host
IfMatch
IfModifiedSince
IfNoneMatch
IfRange
IfUnmodifiedSince
MaxForwards
Pragma
ProxyAuthorization
Range
Referer
TE
UserAgent
Upgrade
Via
Warning
TimeZone
InputStream
ServletInputStream
Reader
OutputStream
ServletOutputStream
Writer
ResourceBundle
- Client-localized resource bundle.
MessageBundle
- A resource bundle with additional features.
Locale
- Client locale.
RequestHeaders
- API for accessing request headers.
RequestQuery
- API for accessing request query parameters.
RequestFormData
- API for accessing request form data.
RequestPath
- API for accessing path variables.
RequestBody
- API for accessing request body.
HttpMethod
- The method name matched (when using Logger
- The logger to use for logging.
JuneauLogger
- Logger with additional features.
RestContext
- The resource read-only context.
Parser
- The parser matching the request content type.
Swagger
- The auto-generated Swagger doc.
RestResponse.getWriter()
was not being flushed automatically
at the end of the HTTP call.
@RestMethod
:
defaultQuery()
defaultFormData()
@RestMethod
:
RestResponse
:
Widget
RestRequest
.
These all have equivalents in RestRequest.getUriContext()
.
Juneau 6.3.1 is a minor release.
PojoQuery
improvements.
Var
implementations can now throw exceptions and will be converted to
navlinks()
- Now an array of strings instead of a JSON object. Simplified syntax.
Widget
API.
@HtmlDoc(widgets)
instead of separately on Widget.getName()
now defaults to the simple class name.
MenuItemWidget
can be used as a starting point for creatint pull-down menu items.
ContentTypeMenuItem
widget that provides a pull-down menu
with hyperlinks for all supported languages for that page:
QueryMenuItem
widget that provides a pull-down menu
of a search/view/order-by/page form:
ThemeMenuItem
widget that provides a pull-down menu
with hyperlinks to show the content in the default stylesheets:
@HtmlDoc
:
style()
- Renamed from stylesheet()
- Renamed from script()
- Add arbitrary Javascript to page header.
@HtmlDoc(nowrap)
so that the setting only applies
to the data contents, not the whole page.
RestRequest
:
@RestMethod(flags)
@HtmlDoc(header)
:
BasicRestServlet
class defines the following default header
that can be easily overridden:
htmldoc=
$F
variable resolver for resolving the contents of
files in the classpath.
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:
|
|
@Swap
annotation.
@Swaps
annotation for defining multiple swaps
against the same POJO when they're differentiated by media types:
Surrogate
interface for identifying surrogate classes.
StringBuilders
.
Readers
and InputStreams
directly to the output stream or writer.
SerializerSession
and ParserSession
objects are now reusable if used within the same thread.
PojoSwap.swap(BeanSession,Object)
and PojoSwap.unswap(BeanSession,Object,ClassMeta)
can now throw arbitrary exceptions instead of having to wrap them in CalendarUtils
class that encapsulates serialization/parsing logic from CalendarSwap
and
DateSwap
.
Html.anchorText()
.
ObjectList
:
ObjectMap
:
PojoRest
:
BeanSession.getMediaType()
wasn't returning a value.
PojoMerge
HtmlElementMixed.children(Object...)
can now take in collections
of objects.
@RestHook
annotation.
RestServlet.init(ServletConfig)
method is now final and can
no longer be extended.
HookEvent.INIT
or
HookEvent.POST_INIT
for initialization.
RestServlet
have been removed:
HookEvent.INIT
instead.
HookEvent.END_CALL
instead.
HookEvent.PRE_CALL
instead.
HookEvent.POST_CALL
instead.
MenuItemWidget
.
HtmlSerializer.DEFAULT
.
RestResourceResolver
instances are now inherited from parent resources to child resources
unless explicitly overridden at the child level.
@RestMethod
:
defaultCharset()
-
Tooltip
Microservice
:
This release ups the Java prerequisite to Java 7.
HttpMethodName
with valid static string HTTP method names.
LinkString
.
Helps avoid confusion since there are other Link classes in the library.
navlinks
.
@HtmlDoc(head)
.
head={
HtmlDocBuilder
class.
This release is a minor update. It includes the following prereq updates:
SerializerGroup.create()
ParserGroup.create()
EncoderGroup.create()
RestClient.create()
SerializerSession.serialize(Object,Object)
has been change to match Serializer.serialize(Object,Object)
.
Var
class to restrict when nested and embedded variables
are resolved.
@RestMethod(maxInput)
for alleviating
potential DoS attacks.
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.
Microservice
startConsole()
getConsoleReader()
getConsoleWriter()
Version 7.1.0 is a major update with major implementation refactoring across all aspects of the product.
PropertyStore
class has been completely rewritten.
It is now a read-only configuration store build using the PropertyStoreBuilder
class.
org.apache.juneau.httppart
HttpPartType
HttpPartSerializer
SimplePartSerializer
HttpPartParser
ContextBuilder.set(String,Object)
.
SerializerSession
:
ParserSession
:
Parser.PARSER_unbuffered
setting allows you to disable internal
buffering on the JSON and UON parsers so that they can be used to read continous streams of objects.
JsonParser.JSON_validateEnd
and UonParser.UON_validateEnd
settings allow you to control whether we validate that there is no garbage at the end of the parsed input.
Parser.PARSER_autoCloseStreams
setting allows input streams and
readers passed into parsers to be automatically closed after parsing.
Surrogate
classes.
@Swap
annotation can now be used with
Surrogate
classes.
BasicRestServlet
.
BasicRestServletGroup
.
RequestAttributeVar
, first non-null value returned by RequestFormDataVar
, first non-null value returned by RestRequest.getFormData(String)
.
RequestHeaderVar
, first non-null value returned by RestRequest.getHeader(String)
.
RestInfoVar
, first non-null value returned by RestRequest.getInfoProvider()
.
Info.getContact()
RestInfoProvider.getDescription(RestRequest)
Swagger.getExternalDocs()
Info.getLicense()
RestInfoProvider.getMethodDescription(Method,RestRequest)
RestInfoProvider.getMethodSummary(Method,RestRequest)
RestInfoProvider.getSiteName(RestRequest)
Swagger.getTags()
Info.getTermsOfService()
RestInfoProvider.getTitle(RestRequest)
Info.getVersion()
RequestPathVar
, first non-null value returned by RestRequest.getPath(String)
.
RequestQueryVar
, first non-null value returned by RestRequest.getQuery(String)
RequestVar
, first non-null other request variable.
RestRequest.getContextPath()
RestRequest.getMethod()
RestRequest.getMethodDescription()
RestRequest.getMethodSummary()
HttpServletRequestWrapper.getPathInfo()
UriContext.getRootRelativePathInfoParent()
HttpServletRequestWrapper.getRequestURI()
RestRequest.getResourceDescription()
RestRequest.getResourceTitle()
UriContext.getRootRelativeServletPathParent()
RestRequest.getServletPath()
UriContext.getRootRelativeServletPath()
RestRequest.getSiteName()
RestContextBuilder
.
RestContext
objects can now be set declaratively through the
following new properties:
REST_allowHeaderParams
REST_allowBodyParam
REST_allowedMethodParams
REST_renderResponseStackTraces
REST_useStackTraceHashes
REST_defaultCharset
REST_maxInput
REST_paramResolvers
REST_converters
REST_guards
REST_responseHandlers
REST_defaultRequestHeaders
REST_defaultResponseHeaders
REST_produces
REST_consumes
REST_clientVersionHeader
REST_resourceResolver
REST_logger
REST_callHandler
REST_infoProvider
REST_path
REST_staticFiles
REST_staticFileResponseHeaders
REST_classpathResourceFinder
REST_useClasspathResourceCaching
REST_widgets
REST_mimeTypes
REST_staticFileResponseHeaders
.
REST_useClasspathResourceCaching
.
REST_classpathResourceFinder
RestMatcher
that has a public constructor that
takes in the server and method arguments.
RequestFormData
:
addDefault(Map)
takes in a
RequestHeaders
:
addDefault(Map)
takes in a
RequestQuery
:
addDefault(Map)
takes in a
RestContext
:
getClasspathResource(String,Locale)
getClasspathResourceAsString(String,Locale)
getClasspathResourceAsString(Class,MediaType,String,Locale)
getClasspathResource(Class,String,Locale)
.
getClasspathResourceAsString(Class,String,Locale)
.
getClasspathResource(Class,Class,MediaType,String,Locale)
.
getDefaultRequestHeaders
returns a
getDefaultRequestHeaders
returns a
RestRequest
:
getConsumes()
and
getProduces()
.
getClasspathReaderResource(String,boolean)
getClasspathReaderResource(String)
@RestMethod
:
consumes()
and
produces()
for overriding the supported media types inferred from the serializers and parsers.
RestCallHandler
and
BasicRestCallHandler
RestInfoProvider
and
BasicRestInfoProvider
RestLogger
,
BasicRestLogger
and NoOpRestLogger
BasicRestResourceResolver
RestInfoProvider
class.
RestResponse.getNegotiatedOutputStream()
now returns a
RestResponse.getNegotiatedWriter()
now returns a DefaultHandler
class now calls BasicRestServlet
class
(which were previously defined on the
RestClient.RESTCLIENT_query
and
builder method RestClientBuilder.query(String,Object)
.
HttpPartSerializer
.
RestCall
class:
RestCall
and RestClient
now implement the BasicRestServlet
and BasicRestServletGroup
can be used instead.
7.2.1 is a major release that introduces several significant new features:
org.apache.juneau.http.annotation
package.
RemoteInterface
- Used for remote proxy interfaces served up through
RestRequest.getRequest(RequestBeanMeta)
method.
@Html(format)
.
SERIALIZER_addRootType
SERIALIZER_addBeanTypes
and is disabled by default.
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---
Parser.PARSER_debugOutputLines
for controlling how many input lines are added to the exception message above.
BeanContext.BEAN_useEnumNames
for controlling whether enums are serialized
using their name or the BeanContext.BEAN_examples
for defining examples of POJOs.
@Example
annotation for defining examples of POJOs.
BeanProperty.name()
@Html(format)
annotation:
HtmlFormat.HTML_CDC
- Format collections as comma-delimited lists.
HtmlFormat.HTML_SDC
- Format collections as space-delimited lists.
ObjectMaps
and ObjectLists
.
SimpleJsonSerializer
class.
RdfXmlSerializer
RdfXmlAbbrevSerializer
N3Serializer
NTripleSerializer
TurtleSerializer
RdfXmlParser
N3Parser
NTripleParser
TurtleParser
SchemaInfo.required(Object...)
was defined as a boolean
instead of a list of strings.
@RestMethod(swagger)
annotations.
@Response
annotation that can be applied to
throwables thrown from REST methods and POJOs returned by REST methods to specify non-200 status return codes and descriptions in Swagger documentation.
org.apache.juneau.rest.helper
BeanDescription
ChildResourceDescriptions
SeeOtherRoot
ResourceDescription
org.apache.juneau.rest.exception
BadRequest
Conflict
ExpectationFailed
FailedDependency
Forbidden
Gone
HttpVersionNotSupported
InsufficientStorage
InternalServerError
LengthRequired
Locked
LoopDetected
MethodNotAllowed
MisdirectedRequest
NetworkAuthenticationRequired
NotAcceptable
NotExtended
NotFound
NotImplemented
PayloadTooLarge
PreconditionFailed
PreconditionRequired
RangeNotSatisfiable
RequestHeaderFieldsTooLarge
ServiceUnavailable
TooManyRequests
Unauthorized
UnavailableForLegalReasons
UnprocessableEntity
UnsupportedMediaType
UpgradeRequired
UriTooLong
VariantAlsoNegotiates
@HtmlDoc(nav)
and @HtmlDoc(navlinks)
can now both be used on the same annotation.
ReaderParser
- The reader parser matching the request content type.
InputStreamParser
- The input stream parser matching the request content type.
Path.name()
annotation parameter is now required.
RestMethod.serializers()
RestMethod.parsers()
RestMethod.beanFilters()
RestMethod.pojoSwaps()
RequestPath
.
@Request
objects can now be used as parameters in RestRequest
:
MenuItemWidget
to allow population of menu item content using Javascript and Ajax calls:
MenuItemWidget
Widget
to allow retrieving classpath resources with embedded SVL variables:
Widget
RestMethod.name()
and RestMethod.path()
have changed.
SeeOtherRoot
.
RestMethod
RestClientBuilder
RestCall.run()
.
RestCall.body(Object)
to match OpenAPI terminology.
RestClient
RestClient.getRemoteResource(Class)
RestClient.getRemoteResource(Class,Object)
RestClient.getRemoteResource(Class,Object,Serializer,Parser)
This release contains mostly bug fixes. Code changes have been made to preserve binary backwards compatibility with 7.1.0.
@Schema
annotation.
ResolvingReaderResource
class has been added that includes the variable-resolving support since
this relies on the RrpcServlet
.
RestClient.getRrpcInterface(Class)
.
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:
Microservice
class.
JettyMicroservice
class.
RestServlet
:
Microservice
class and console support.
MicroserviceListener
API for defining lifecycle event listeners.
ConfigCommand
class for performing config queries and updates through
the microservice console.
JettyMicroservice
class replaces the previous 8.1.0 introduces some significant new features including:
Marshall
format(String,Object...)
- out(String,Object...)
- Prints err(String,Object...)
- Prints TemporalSwap
- For all Java 8 temporal types (e.g. ZonedDateTime
)
TemporalDateSwap
- For Date
TemporalCalendarSwap
- For Calendar
Enumeration
Iterator
Locale
Calendar
- ISO offset date-time.
Date
- Local date-time
Instant
- ISO instant.
ZonedDateTime
- ISO offset date-time.
LocalDate
- ISO local date.
LocalDateTime
- ISO local date-time.
LocalTime
- ISO local time.
OffsetDateTime
- ISO offset date-time.
OffsetTime
- ISO offset time.
Year
- ISO year.
YearMonth
- ISO year-month.
Temporal
- ISO instant.
TimeZone
XMLGregorianCalendar
ZoneId
ConfigFileStore
now automatically resolves file extensions.
ConfigMemoryStore
ended up resolving to the same object.
Config.setSystemProperties()
method for quickly moving configuration settings into the
system properties.
Config annotations are provided for all serializers and parsers:
RequestAttributes
RestContext
RestContextBuilder
RestMethodContext
RestRequest
RestResponse
Attr
RestMethod.attrs()
BasicRestServlet
are registered as top-level servlets even though
you don't want them to be.
BasicRest
- Non-servlet equivalent to BasicRestServlet
BasicRestGroup
- Non-servlet equivalent to BasicRestServletGroup
BasicRestJena
- Non-servlet equivalent to BasicRestServletJena
BasicRestJenaGroup
- Non-servlet equivalent to BasicRestServletJenaGroup
RestMethod
Juneau 8.1.1 is a minor release but introduces some new features/modifications.
Optional
objects and bean properties.
Optional
on method parameters annotated with Header
, FormData
,
Query
, Path
.
RestMethod.debug()
annotation wasn't resulting
in the HTTP request being logged.
RestException
has been deprecated and replaced by HttpException
.
BasicRest
to provide feature-parity with RestServlet
:
Juneau 8.1.2 is a moderate release.
CharMarshall
and StreamMarshall
now have public constructors.
@Beanp
replaces @BeanProperty
.
@Beanc
replaces @BeanConstructor
.
@Remote
replaces @RemoteResource
.
@Bean(dictionary)
and BeanContextBuilder.dictionary(Class...)
.
@Rest
replaces @RestResource
with shorter syntax.
RestResponse.setHeaderSafe(String,String)
to strip invalid characters from header values.
RestClient
is now extendible. The constructor has been made public and simplified to:
RestClient.RestClient(RestClientBuilder)
.
RestClientBuilder
and HttpClientBuilder
have been made deprecated on the former. This eliminates the need to try to keep the two builder classes in
sync.
Copyright © 2016–2019 The Apache Software Foundation. All rights reserved.