Apache Juneau™ is a single cohesive Java ecosystem consisting of the following parts:
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, 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 "org.apache.juneau"
:
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-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 | juneau-all |
Combination of the following:
|
|
The current version of Juneau is {@property juneauVersion}
.
The easiest way to pull in the library is through the following maven dependency:
If you would like to work with the bleeding-edge code, you can access the {@property juneauVersionNext}-SNAPSHOT
version through the following repository:
Each of the components are also packaged as stand-alone OSGi modules.
juneau-marshall-{@property juneauVersion}.jar
org.apache.juneau.marshall_{@property juneauVersion}.jar
The juneau-marshall
artifact contains the API for defining serializers and parsers, and
marshalling support for JSON, XML, HTML, URL-Encoding, UON and others.
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, Maps
, Collections
,
beans, and arrays.
Extensible support for other data types such as Calendars
, Dates
,
Iterators
is available through the use of POJO swaps (described later).
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:
{@link org.apache.juneau.marshall.Marshall Marshalls} are simple pairings of a {@link org.apache.juneau.serializer.Serializer} and {@link org.apache.juneau.parser.Parser} with convenience methods for serializing and parsing POJOs.
Marshalls are often cleaner to use on-the-fly since they have simplified names.
The following shows the {@link org.apache.juneau.marshall.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:
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:
Serializers and parsers have a wide variety of configurable properties. They all extend from the {@link org.apache.juneau.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 builder()
method.
All serializers and parsers extend from the {@link org.apache.juneau.BeanContext} class. Therefore, the following properties are common to all serializers and parsers:
In addition to the common properties above, the following properties are common to all serializers:
In addition to the common properties above, the following properties are common to all parsers:
The {@link org.apache.juneau.ObjectMap} and {@link org.apache.juneau.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:
Any valid JSON can be parsed into an unstructured model consisting of generic {@link org.apache.juneau.ObjectMap} and {@link org.apache.juneau.ObjectList} objects. (In theory, any valid XML can also be parsed into an unstructured model, although this has not been officially 'tested')
The ObjectMap
and ObjectList
classes have many convenience features:
Above the serializers and parsers are the {@link org.apache.juneau.serializer.SerializerGroup} and
{@link org.apache.juneau.parser.ParserGroup} classes.
These classes allow serializers and parsers to be retrieved by W3C-compliant HTTP Accept
and Content-Type
values...
The REST servlet API builds upon the SerializerGroup
and ParserGroup
classes
to provide annotated REST servlets that automatically negotiate the HTTP media types and allow the developer
to work with requests and responses as POJOs.
All the serializers, parsers, and REST server/client classes use the following design pattern:
BeanContext
, JsonSerializer
Context
objects.
BeanSession
, JsonSerializerSession
Sessions
but lighter than Contexts
.
Context
contains one PropertyStore
that defines all the configuration about that object.
For example, the class hierarchy for JsonSerializer
is:
Object
Each context object in the hierarchy define properties that can be stored in a PropertyStore
such as
The class hierarchy for JsonSerializerBuilder
is:
Object
The class hierarchy for JsonSerializerSession
is:
Object
The general idea behind a {@link org.apache.juneau.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 {@link org.apache.juneau.PropertyStoreBuilder} class is used to build up and instantiate immutable
PropertyStore
objects.
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 {@link org.apache.juneau.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:
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):
{@link org.apache.juneau.transform.PojoSwap 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 File
, Reader
,
Iterable
, etc...
These are classes that aren't beans and cannot be represented as simple maps, collections, or primitives.
In the following example, we introduce a PojoSwap
that will swap in ISO8601 strings for
Date
objects:
The swap can then be associated with serializers and parsers like so:
The {@link org.apache.juneau.BeanMap#get(Object)} and {@link org.apache.juneau.BeanMap#put(String,Object)} methods will automatically convert to swapped values as the following example shows:
Another example of a PojoSwap
is one that converts
arrays to
BASE64-encoded strings:
The following example shows the BASE64 swap in use:
Several PojoSwaps
are already provided for common Java objects:
In particular, the {@link org.apache.juneau.transforms.CalendarSwap} and {@link org.apache.juneau.transforms.DateSwap} transforms provide a large number of customized swaps to ISO, RFC, or localized strings.
Swaps have access to the session locale and timezone through the {@link org.apache.juneau.BeanSession#getLocale()} and {@link org.apache.juneau.BeanSession#getTimeZone()} methods. This allows you to specify localized swap values when needed.
If using the REST server API, the locale and timezone are set based on the Accept-Language
and
Time-Zone
headers on the request.
Swaps can also be defined per-media-type.
The {@link org.apache.juneau.transform.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 {@code swap()} method. The {@code unswap()} method is simply left unimplemented.
An example of a one-way swaps would be one that allows {@code Iterators} to be serialized as JSON arrays. It can make sense to be able to render {@code Iterators} as arrays, but in general it's not possible to reconstruct an {@code Iterator} during parsing.
Here is an example of our one-way swap being used. Note that trying to parse the original object will cause a {@link org.apache.juneau.parser.ParseException} to be thrown.
{@link org.apache.juneau.annotation.Swap @Swap} can be used to associate a swap class using an
annotation.
This is often cleaner than using the builder pojoSwaps()
method since you can keep
your swap class near your POJO class.
Multiple swaps can be associated with a POJO by using the {@link org.apache.juneau.annotation.Swaps @Swaps} annotation:
Readers
get serialized directly to the output of a serializer.
Therefore it's possible to implement a swap that provides fully-customized output.
The
When applied to bean properties, the swap annotation need only be applied to either the getter, setter, or field.
The swap annotation can also be applied to the private field of a bean property, like so:
The {@link org.apache.juneau.annotation.Swap#template() @Swap(template)} annotation allows you to associate arbitrary contextual strings with swaps. The primary purpose is for providing template names, such as for Apache FreeMarker, therefore the name 'template'. However, the usage of the string is open-ended.
For example, you could pair a template string like so:
The implementation of the FreeMarker swap would look something like this:
Various methods can be defined on a class directly to affect how it gets serialized.
This can often be simpler than using PojoSwaps
.
Objects serialized as Strings
can be parsed back into their original objects by
implementing one of the following methods on the class:
public static T fromString(String)
method.
valueOf(String)
parse(String)
parseString(String)
forName(String)
forString(String)
public T(String)
constructor.
Note that these methods cover conversion from several built-in Java types, meaning the parsers can automatically construct these objects from strings:
fromString(String)
- {@link java.util.UUID}
valueOf(String)
- {@link java.lang.Boolean}, {@link java.lang.Byte},
{@link java.lang.Double}, {@link java.lang.Float},
{@link java.lang.Integer}, {@link java.lang.Long}, {@link java.lang.Short}, {@link java.sql.Date},
{@link java.sql.Time}, {@link java.sql.Timestamp}
parse(String)
- {@link java.text.DateFormat}, {@link java.text.MessageFormat},
{@link java.text.NumberFormat}, {@link java.util.Date}, {@link java.util.logging.Level}
parseString(String)
- {@link javax.xml.bind.DatatypeConverter}
forName(String)
- {@link java.lang.Class}
If you want to force a bean-like class to be serialized as a string, you can use the
{@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore} annotation on the class to force it to be
serialized to a string using the toString()
method.
Serializing to other intermediate objects can be accomplished by defining a swap method directly on the class:
public X swap(BeanSession)
method, where X
is any serializable
object.
The BeanSession
parameter allows you access to various information about the current
serialization session.
For example, you could provide customized results based on the media type being produced
({@link org.apache.juneau.BeanSession#getMediaType()}).
The following example shows how an HTML5 form template object can be created that gets serialized as a populated HTML5 {@link org.apache.juneau.dto.html5.Form} bean.
Swapped objects can be converted back into their original form by the parsers by specifying one of the following methods:
public static T unswap(BeanSession, X)
method where X
is the
swap class type.
public T(X)
constructor where X
is the swap class type.
The following shows how our form template class can be modified to allow the parsers to reconstruct our original object:
Surrogate classes are very similar in concept to PojoSwaps
except they're simpler to define.
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 PojoSwap
.
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 PojoSwap
:
When the serializer encounters the non-serializable class, it will serialize an instance of the surrogate instead.
The {@link org.apache.juneau.annotation.Bean @Bean} annotation is used to tailor how beans are interpreted by the framework.
Bean property inclusion and ordering on a bean class can be done using the {@link org.apache.juneau.annotation.Bean#properties() @Bean(properties)} annotation.
Bean properties can be excluded using the {@link org.apache.juneau.annotation.Bean#excludeProperties() @Bean(excludeProperties)} annotation.
Bean properties can be sorted alphabetically using {@link org.apache.juneau.annotation.Bean#sort() @Bean(sort)}
The {@link org.apache.juneau.annotation.Bean#propertyNamer() @Bean(propertyNamer)} annotation is used to provide customized naming of properties.
Property namers are used to transform bean property names from standard form to some other form. For example, the {@link org.apache.juneau.PropertyNamerDLC} will convert property names to dashed-lowercase, and these will be used as attribute names in JSON and element names in XML.
The {@link org.apache.juneau.annotation.Bean#interfaceClass @Bean(interfaceClass)} annotation is used to limit properties on beans to specific interface classes. When specified, only the list of properties defined on the interface class will be used during serialization. Additional properties on subclasses will be ignored.
Note that this annotation can be used on the parent class so that it filters to all child classes. Or can be set individually on the child classes.
The {@link org.apache.juneau.annotation.Bean#stopClass @Bean(stopClass)} annotation is another way to limit which properties are serialized (except from the opposite direction). It's identical in purpose to the stop class specified by {@link java.beans.Introspector#getBeanInfo(Class, Class)}. Any properties in the stop class or in its base classes will be ignored during analysis.
For example, in the following class hierarchy, instances of C3
will include property
p3
, but not p1
or p2
.
The {@link org.apache.juneau.annotation.Bean#propertyFilter() @Bean(propertyFilter)} annotation and {@link org.apache.juneau.transform.PropertyFilter} class can be used to perform interception and inline handling of bean getter and setter calls.
The {@link org.apache.juneau.annotation.BeanProperty @BeanProperty} annotation is used to tailor how individual bean properties are interpreted by the framework.
The {@link org.apache.juneau.annotation.BeanProperty#name() @BeanProperty(name)} annotation is used to override the name of the bean property.
If the {@link org.apache.juneau.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.
The {@link org.apache.juneau.annotation.BeanProperty#value() @BeanProperty(value)} annotation is a synonym for {@link org.apache.juneau.annotation.BeanProperty#name() @BeanProperty(name)}. Use it in cases where you're only specifying a name so that you can shorten your annotation.
The following annotations are equivalent:
The {@link org.apache.juneau.annotation.BeanProperty#type() @BeanProperty(type)} annotation is used to identify a specialized class type for a generalized property. Normally the type is inferred through reflection of the field type or getter return type. However, you'll want to specify this value if you're parsing beans where the bean property class is an interface or abstract class to identify the bean type to instantiate. Otherwise, you may cause an {@link java.lang.InstantiationException} when trying to set these fields.
This property must denote a concrete class with a no-arg constructor.
The {@link org.apache.juneau.annotation.BeanProperty#params() @BeanProperty(params)} annotation is for bean properties of type map or collection. It's used to identify the class types of the contents of the bean property object when the general parameter types are interfaces or abstract classes.
The {@link org.apache.juneau.annotation.BeanProperty#properties() @BeanProperty(properties)} annotation is used to limit which child properties are rendered by the serializers. It can be used on any of the following bean property types:
The {@link org.apache.juneau.annotation.BeanProperty#format() @BeanProperty(format)} annotation specifies a String format for converting a bean property value to a formatted string.
The {@link org.apache.juneau.annotation.BeanConstructor @BeanConstructor} annotation is used to map constructor arguments to property names on bean with read-only properties. Since method parameter names are lost during compilation, this annotation essentially redefines them so that they are available at runtime.
The definition of a read-only bean is a bean with properties with only getters, like shown below:
Beans can also be defined with a combination of read-only and read-write properties.
The {@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore} annotation is used to ignore classes, fields, and methods from being interpreted as beans or bean components.
When applied to classes, objects will be converted to strings even though they look like beans.
When applied to fields and getters/setters, they will be ignored as bean properties.
The {@link org.apache.juneau.annotation.NameProperty @NameProperty} annotation is used to identify a setter as a method for setting the name of a POJO as it's known by its parent object.
A commonly-used case is when you're parsing a JSON map containing beans where one of the bean properties is the key used in the map.
The {@link org.apache.juneau.annotation.ParentProperty @ParentProperty} annotation is used to identify a setter as a method for adding a parent reference to a child object.
A commonly-used case is when you're parsing beans and a child bean has a reference to a parent bean.
Parsers will automatically set this field for you in the child beans.
Juneau parsers can use builders to instantiate POJOs. This is useful in cases where you want to create beans with read-only properties. Note that while it's possible to do this using the {@link org.apache.juneau.annotation.BeanConstructor @BeanConstructor} 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:
create()
method on the POJO class that returns a builder instance.
The second can be accomplished through any of the following:
build()
method on the builder class.
The {@link org.apache.juneau.transform.BeanFilter} class is the programmatic equivalent to the {@link org.apache.juneau.annotation.Bean @Bean} annotation.
In practice, it's usually simpler to use the {@link org.apache.juneau.annotation.Bean @Bean} and {@link org.apache.juneau.annotation.BeanProperty @BeanProperty} 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 {@link org.apache.juneau.transform.BeanFilterBuilder 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 {@link org.apache.juneau.BeanContextBuilder#beanFilters(Object...)} method also allows you to pass in interfaces. Any class that's not a subclass of {@link org.apache.juneau.transform.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 {@link org.apache.juneau.BeanContextBuilder#beanFilters(Object...)} method will automatically interpret any
non-BeanFilter
classes passed in as meaning interface classes.
So in the previous example, the BeanFilter
class could have been avoided altogether by just
passing in MyInterface.
to the serializer, like so:
WriterSerializer s = JsonSerializer
.
The annotation equivalent is {@link org.apache.juneau.annotation.Bean#interfaceClass() 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 {@link java.beans.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 C3
will include property p3
,
but not p1
or p2
.
Juneau serializers treat instances of Readers
and InputStreams
special by
simply serializing their contents directly to the output stream or writer.
This allows you to embed fully customized serializer output.
Note that if you're serializing Readers and InputStreams, it's up to you to make sure you're producing valid output (in this case JSON).
A more typical scenario where this is useful is by using swaps to convert POJOs to Readers whose contents are determined via the {@link org.apache.juneau.BeanSession#getMediaType()} method. In the following example, we're customizing the JSON output for a particular bean type, but leaving all other renditions as-is:
While parsing into beans, Juneau attempts to determine the class types of bean properties through reflection on the bean property getter or setter. Often this is insufficient if the property type is an interface or abstract class that cannot be instantiated. This is where bean names and dictionaries come into play.
Bean names and dictionaries are used for identifying class types when they cannot be inferred through reflection.
Bean classes are given names through the {@link org.apache.juneau.annotation.Bean#typeName() @Bean(typeName)}
annotation.
These names are then added to the serialized output as virtual
On the parsing side, these type names are resolved to classes through the use of bean dictionaries.
For example, if a bean property is of type Object
, then the serializer will add
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 typeName
annotation to be used as a shortcut for defining element names for
beans.
When serialized as XML, the bean is rendered as:
Bean dictionaries are registered through the following:
The bean dictionary setting can consist of any of the following types:
The
When using the annotation, you'll typically want to define it on an interface class so that it can be inherited by all subclasses.
object, array, number, boolean, null
.
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 {@link org.apache.juneau.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 {@doc PojoCategories parsable} values, even other virtual beans.
Under-the-covers, a virtual bean is simply a proxy interface on top of an existing BeanMap
instance. From a programmatic point-of-view, they're indistinguishable from real beans, and can be
manipulated and serialized like any other bean.
Virtual beans can also be created programmatically using the BeanContext
class:
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 StackOverflowError
to
be thrown (if {@link org.apache.juneau.BeanTraverseContext#BEANTRAVERSE_maxDepth} is not reached
first).
If you still want to use the Juneau serializers on such models, Juneau provides the {@link org.apache.juneau.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 c
field:
{
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 Maps
, Collections
, and primitive
objects.
You can parse into any Map
type (e.g. HashMap
, TreeMap
), but
using {@link org.apache.juneau.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. HashSet
,
LinkedList
) or array (e.g. Object[]
, String[]
,
String[][]
), but using {@link org.apache.juneau.ObjectList} is recommended.
When the map or list type is not specified, or is the abstract Map
, Collection
,
or List
types, the parser will use ObjectMap
and ObjectList
by
default.
For example, given the following JSON:
{
id:
We can parse this into a generic ObjectMap
:
What we end up with is the exact same output.
Even the numbers and booleans are preserved because they are parsed into Number
and
Boolean
objects when parsing into generic models.
{
id:
Once parsed into a generic model, various convenience methods are provided on the ObjectMap
and ObjectList
classes to retrieve values:
As a general rule, parsing into beans is often more efficient than parsing into generic models. And working with beans is often less error prone than working with generic models.
The following parsers can be configured to read continuous streams of objects from the same input stream:
The {@link org.apache.juneau.json.JsonParser} and {@link org.apache.juneau.uon.UonParser} classes can read continuous streams by using the {@link org.apache.juneau.parser.Parser#PARSER_unbuffered 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. "123" + "456" = "123456"
).
For obvious reasons, do not use the following properties when reading continuous streams:
The {@link org.apache.juneau.msgpack.MsgPackParser} class doesn't use any internal buffering to begin with, so it can be used with continuous streams without any special properties.
Juneau serializers have sophisticated support for transforming relative URIs to absolute form.
The 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:
Juneau automatically interprets any {@link java.net.URL} and {@link java.net.URI} objects as URIs and will resolve them accordingly. The {@link org.apache.juneau.annotation.URI @URI} annotation can be used to extend that to other bean properties and class types so that they also get interpreted as URIs. For example:
Juneau was developed independently from Jackson, but shares many of the same features and capabilities. Whereas Jackson was created to work primarily with JSON, Juneau was created to work for multiple languages. Therefore, the terminology and annotations in Juneau are similar, but language-agnostic.
The following charts describe equivalent features between the two libraries:
Jackson | Juneau |
---|---|
|
{@link org.apache.juneau.annotation.BeanProperty @BeanProperty} |
|
{@link org.apache.juneau.annotation.BeanProperty#name() @BeanProperty(name="*")} |
|
{@link org.apache.juneau.annotation.BeanIgnore @BeanIgnore} |
|
{@link org.apache.juneau.annotation.Bean#excludeProperties @Bean(excludeProperties="...")} |
|
No equivalent annotation, but can be controlled via:
{@link org.apache.juneau.BeanContext#BEAN_beanFieldVisibility} {@link org.apache.juneau.BeanContext#BEAN_beanMethodVisibility} Future annotation support planned. |
|
{@link org.apache.juneau.annotation.BeanConstructor @BeanConstructor} |
No equivalent.
Future support planned. |
|
|
Juneau uses swaps to convert non-serializable object to serializable forms:
{@link org.apache.juneau.annotation.Swap @Swap} |
No equivalent annotation, but can be controlled via various settings:
{@link org.apache.juneau.BeanContext} {@link org.apache.juneau.serializer.Serializer} Future annotation support planned. |
|
{@link org.apache.juneau.annotation.Bean#properties @Bean(properties="...")}
{@link org.apache.juneau.annotation.Bean#sort @Bean(sort=x)} |
|
|
Can be replicated using swaps with Reader swapped values.
|
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 and Java arrays | |||
2a |
With standard keys/values
Map keys are group [1, 4a, 6a] objects. Map, Collection, and array values are group [1, 2, 3ac, 4a, 6a] objects. |
|
yes | yes |
2b |
With non-standard keys/values
Map keys are group [2, 3, 4b, 5, 6b, 7] objects. Map, Collection, and array values are group [3b, 4b, 5, 6b, 7] objects. |
|
yes | no |
3 | Java Beans | |||
3a |
With standard properties
These are beans that have one or more properties defined by public getter or public fields. Properties can also be defined as final read-only fields and passed in as constructor args. Property values are group [1, 2, 3ac, 4a, 6a] objects. |
yes | yes | |
3b |
With non-standard properties or not true beans
These include true beans that have one or more properties defined by getter and setter methods or properties, but property types include group [3b, 4b, 5, 6b, 7] objects. This also includes classes that look like beans but aren't true beans. For example, classes that have getters but not setters, or classes without no-arg constructors. |
yes | no | |
3c |
Virtual beans
These are unimplemented bean interfaces with properties of type [1, 2, 3ac, 4a, 6a] objects. Parsers will automatically create interface proxies on top of BeanMap instances. |
yes | yes | |
3d |
Read-only beans without setters
The same as 3a, but without property setters or constructor args. |
yes | no | |
4 |
Swapped objects
These are objects that are not directly serializable, but have {@link org.apache.juneau.transform.PojoSwap 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 {@link org.apache.juneau.transforms.DateSwap.ISO8601DT} class can be used to serialize {@link java.util.Date} objects to ISO8601 strings, and parse them back into {@link java.util.Date} objects. |
|||
4a |
2-way swapped to group [1, 2a, 3ac] objects
For example, a swap that converts a {@code Date} to a {@code String}. |
|
yes | yes |
4b |
1-way swapped to group [1, 2, 3] objects
For example, a swap that converts an {@code Iterator} to a {@code List}. This would be one way, since you cannot reconstruct an {@code Iterator}. |
|
yes | no |
5 |
Readers and InputStreams
Contents are serialized directly to the output stream or writer. Typically used for low-level language-specific replacement of POJOs using per-Media-Type POJO swaps. |
|
yes | no |
6 |
Non-serializable objects with standard methods for converting to a serializable form |
|||
6a |
Classes with a method that converts it to a serializable form:
|
|
yes | yes |
6b |
Classes that only have a method to convert to a serializable form:
|
yes | no | |
7 |
All other objects
Anything that doesn't fall into one of the groups above are simply converted to {@code Strings} using the {@code toString()} method. |
yes | no |
A separate category exists for POJOs that can be converted to and from Strings. These are used in places such as:
As a general rule, all POJOs are converted to Strings using the toString()
method.
However, there is one exception:
POJOs are convertible from Strings using any of the following (matched in the specified order):
create(String)
fromString(String)
fromValue(String)
valueOf(String)
parse(String)
parseString(String)
forName(String)
forString(String)
String
.
Exceptions exist for the following classes:
void .class
) - Uses the primitive wrapper classes for instantiating from Strings.
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 X
.
POJOs are considered convertible from X if it has any of the following (matched in the specified order):
create(X)
from*(X)
X
.
to*()
.
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 | serialize( |
|
Number | Number | serialize(123); |
123
|
Boolean | Boolean | serialize( |
|
Null | Null | serialize( |
|
Beans with properties of any type on this list | Object | serialize( |
{p1:
|
Maps with values of any type on this list | Object | serialize( |
{key1:
|
Collections and arrays of any type on this list | Array | serialize( |
[1,
|
In addition, swaps can be used to convert non-serializable POJOs into serializable forms, such as converting
Calendar
object to ISO8601 strings, or
arrays to Base-64
encoded strings.
The {@link org.apache.juneau.json.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 {@link org.apache.juneau.json.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 {@link org.apache.juneau.json.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 {@link org.apache.juneau.json.annotation.Json @Json} annotation is used to override the behavior of {@link org.apache.juneau.json.JsonSerializer} on individual bean classes or properties.
The annotation can be applied to beans as well as other objects serialized to other types (e.g. strings).
The {@link org.apache.juneau.json.annotation.Json#wrapperAttr() @Json(wrapperAttr)} annotation can be used to wrap beans inside a JSON object with a specified attribute name.
The following shows the JSON representation with and without the annotation present:
Without annotation | With annotation |
---|---|
{
|
{
|
Juneau provides the {@link org.apache.juneau.json.JsonSchemaSerializer} class for generating JSON-Schema
documents that describe the output generated by the {@link org.apache.juneau.json.JsonSerializer} class.
This class shares the same properties as JsonSerializer
.
For convenience the {@link org.apache.juneau.json.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
Object
or superclass/interface value type).
Data type | JSON example | XML |
---|---|---|
Map<String,String> |
{
k1: |
|
Map<String,Number> |
{
k1: 123,
k2: 1.23,
k3: |
|
Map<String,Object> |
{
k1: |
Loose collections and arrays use the element
Data type | JSON example | XML |
---|---|---|
String[] |
[
|
|
Number[] |
[
123,
1.23,
|
|
Object[] |
[
|
|
String[][] |
[
[ |
|
|
[ 123 ] | |
|
[
|
|
List<String> |
[
|
|
List<Number> |
[
123,
1.23,
|
|
List<Object> |
[
|
Data type | JSON example | XML |
---|---|---|
|
{
|
Data type | JSON example | XML |
---|---|---|
|
{
|
The {@link org.apache.juneau.xml.XmlSerializer} class is used to serialize POJOs into XML.
The {@link org.apache.juneau.xml.XmlDocSerializer} class is the same, but serializes a
The XML serializers provide the following settings:
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.xml.XmlParser} class is used to parse XML into POJOs.
The XML parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The {@link org.apache.juneau.annotation.Bean#typeName() @Bean(typeName)} annotation can be used to override the Juneau default name on bean elements. Types names serve two distinct purposes:
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
On bean properties, a
In the following example, a type attribute is used on property 'b' but not property 'a' since
'b' is of type Object
and therefore the bean class cannot be inferred.
Java | Without annotation | With annotation |
---|---|---|
|
string
, number
, boolean
, object
,
array
, and null
are reserved keywords that cannot be used as type names.
Beans with type names are often used in conjunction with the {@link org.apache.juneau.annotation.Bean#beanDictionary() @Bean(beanDictionary)} and {@link org.apache.juneau.annotation.BeanProperty#beanDictionary() @BeanProperty(beanDictionary)} annotations so that the beans can be resolved at parse time. These annotations are not necessary during serialization, but are needed during parsing in order to resolve the bean types.
The following examples show how type names are used under various circumstances.
Pay special attention to when
Java | XML |
---|---|
|
|
|
|
|
Bean type names are also used for resolution when abstract fields are used. The following examples show how they are used in a variety of circumstances.
Java | XML |
---|---|
|
|
|
|
|
|
|
On a side note, characters that cannot be represented in XML 1.0 are encoded using a simple encoding.
Note in the examples below, some characters such as
Java | XML |
---|---|
|
|
|
While it's true that these characters CAN be represented in XML 1.1, it's impossible to parse XML 1.1 text in Java without the XML containing an XML declaration. Unfortunately, this, and the uselessness of the {@link javax.xml.stream.XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES} setting in Java forced us to make some hard design decisions that may not be the most elegant.
The {@link org.apache.juneau.xml.annotation.Xml#childName() @Xml(childName)} annotation can be used to specify the name of XML child elements for bean properties of type collection or array.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: [ |
||
|
{ a: [123,456] } |
The {@link org.apache.juneau.xml.annotation.Xml#format() @Xml(format)} annotation can be used to tweak the XML format of a POJO. The value is set to an enum value of type {@link org.apache.juneau.xml.annotation.XmlFormat}. This annotation can be applied to both classes and bean properties.
The {@link org.apache.juneau.xml.annotation.XmlFormat#ATTR} format can be applied to bean properties to serialize them as XML attributes instead of elements. Note that this only supports properties of simple types (e.g. strings, numbers, booleans).
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ATTRS} format can be applied to bean classes to force all bean properties to be serialized as XML attributes instead of child elements.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ELEMENT} format can be applied to bean properties to override the {@link org.apache.juneau.xml.annotation.XmlFormat#ATTRS} format applied on the bean class.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ATTRS} format can be applied to a single bean
property of type Map<String,Object>
to denote arbitrary XML attribute values on the
element.
These can be mixed with other {@link org.apache.juneau.xml.annotation.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 {@link org.apache.juneau.xml.annotation.XmlFormat#COLLAPSED} format can be applied to bean properties of type array/Collection. This causes the child objects to be serialized directly inside the bean element. This format must be used in conjunction with {@link org.apache.juneau.xml.annotation.Xml#childName() @Xml(childName)} to differentiate which collection the values came from if you plan on parsing the output back into beans. Note that child names must not conflict with other property names.
Data type | JSON example | Without annotation | With annotation |
---|---|---|---|
|
{
a: [ |
The {@link org.apache.juneau.xml.annotation.XmlFormat#ELEMENTS} format can be applied to a single bean property of either a simple type or array/Collection. It allows free-form child elements to be formed. All other properties on the bean MUST be serialized as attributes.
Data type | JSON example | With annotation |
---|---|---|
|
{
a: |
|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#MIXED} format is similar to {@link org.apache.juneau.xml.annotation.XmlFormat#ELEMENTS} except elements names on primitive types (string/number/boolean/null) are stripped from the output. This format particularly useful when combined with bean dictionaries to produce mixed content. The bean dictionary isn't used during serialization, but it is needed during parsing to resolve bean types.
The {@link org.apache.juneau.xml.annotation.XmlFormat#MIXED_PWS} format identical to {@link org.apache.juneau.xml.annotation.XmlFormat#MIXED} except whitespace characters are preserved in the output.
Data type | JSON example | Without annotations | With annotations |
---|---|---|---|
|
{
a: [
|
Whitespace (tabs and newlines) are not added to MIXED child nodes in readable-output mode. This helps ensures strings in the serialized output can be losslessly parsed back into their original forms when they contain whitespace characters. If the {@link javax.xml.stream.XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES} setting was not useless in Java, we could support lossless readable XML for MIXED content. But as of Java 8, it still does not work.
XML suffers from other deficiencies as well that affect MIXED content.
For example,
The examples below show how whitespace is handled under various circumstances:
Data type | XML |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It should be noted that when using
The {@link org.apache.juneau.xml.annotation.XmlFormat#TEXT} format is similar to
{@link org.apache.juneau.xml.annotation.XmlFormat#MIXED} except it's meant for solitary objects that
get serialized as simple child text nodes.
Any object that can be serialize to a String
can be used.
The {@link org.apache.juneau.xml.annotation.XmlFormat#TEXT_PWS} is the same except whitespace is
preserved in the output.
Data type | JSON example | Without annotations | With annotations |
---|---|---|---|
|
{
a: |
The {@link org.apache.juneau.xml.annotation.XmlFormat#XMLTEXT} format is similar to
{@link org.apache.juneau.xml.annotation.XmlFormat#TEXT} except it's meant for strings containing XML
that should be serialized as-is to the document.
Any object that can be serialize to a String
can be used.
During parsing, the element content gets parsed with the rest of the document and then re-serialized to
XML before being set as the property value.
This process may not be perfect (e.g. double quotes may be replaced by single quotes, etc...).
Data type | JSON example | With TEXT annotation | With XMLTEXT annotation |
---|---|---|---|
|
{
a: |
Let's go back to the example of our original Person
bean class, but add some namespace annotations:
The namespace URLs can either be defined as part of the {@link org.apache.juneau.xml.annotation.Xml @Xml} annotation, or can be defined at the package level with the {@link org.apache.juneau.xml.annotation.XmlSchema @XmlSchema} annotation. Below shows it defined at the package level:
Person p =
Now when we run this code, we'll see namespaces added to our output:
Enabling the {@link org.apache.juneau.xml.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 {@link org.apache.juneau.xml.XmlSerializer#XML_autoDetectNamespaces 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 {@link org.apache.juneau.xmlschema.XmlSchemaSerializer} class for generating XML-Schema
documents that describe the output generated by the {@link org.apache.juneau.xml.XmlSerializer} class.
This class shares the same properties as XmlSerializer
.
Since the XML output differs based on settings on the XML serializer class, the XML-Schema serializer
class must have the same property values as the XML serializer class it's describes.
To help facilitate creating an XML Schema serializer with the same properties as the corresponding
XML serializer, the {@link org.apache.juneau.xml.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 {@link org.apache.juneau.xmlschema.XmlSchemaSerializer #getValidator(SerializerSession,Object)} method is provided to create a {@link javax.xml.validation.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 {@link org.apache.juneau.html.HtmlSerializer} class is used to serialize POJOs into HTML.
The {@link org.apache.juneau.html.HtmlDocSerializer} class is the same, but wraps the serialized POJO inside a document template consisting of header, nav, aside, and footer sections.
The HTML serializers provides the following settings:
The following pre-configured serializers are provided for convenience:
The {@link org.apache.juneau.html.HtmlParser} class is used to parse HTML into POJOs. They can also parse the contents produced by {@link org.apache.juneau.html.HtmlDocSerializer}.
The HTML parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The {@link org.apache.juneau.html.annotation.Html @Html} annotation can be used to customize how POJOs are serialized to HTML on a per-class/field/method basis.
The {@link org.apache.juneau.html.annotation.Html#link @Html(link)} annotation adds a hyperlink to a bean property when rendered as HTML.
The {@link org.apache.juneau.html.annotation.Html#anchorText @Html(anchorText)} annotation is used to specify the anchor text of a hyperlink.
The {@link org.apache.juneau.html.annotation.Html#format @Html(format)} annotation is used to specify what format to use for HTML elements.
For example, the HTML beans defined in the {@link org.apache.juneau.dto.html5} package use format=
so that
the beans get serialized as standard XML:
The {@link org.apache.juneau.html.annotation.Html#noTableHeaders @Html(noTableHeaders)} annotation is used to prevent beans from being serialized with table headers.
The {@link org.apache.juneau.html.annotation.Html#noTables @Html(noTables)} annotation is used to force beans to be serialized as trees instead of tables
The {@link org.apache.juneau.html.annotation.Html#render @Html(render)} annotation allows for custom rendering of bean property values when serialized as HTML. Using this class, you can alter the CSS style and HTML content of the bean property.
The following example shows two render classes that customize the appearance of the pctFull
and
status
columns in the code below:
{@link org.apache.juneau.html.HtmlDocSerializer} is an extension of {@link org.apache.juneau.html.HtmlSerializer} that wraps serialized POJOs in a complete HTML document.
This class is used extensively in the creation of POJO-based user interfaces in the REST API.
The {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_template HTMLDOC_template} setting defines a template for the HTML page being generated. The default template is described next.
The {@link org.apache.juneau.html.BasicHtmlDocTemplate} class defines a default template for HTML documents created by {@link org.apache.juneau.html.HtmlDocSerializer}.
The HTML document created by this template consists of the following structure:
Custom page templates can be created by implementing the {@link org.apache.juneau.html.HtmlDocTemplate} interface and associating it with your {@link org.apache.juneau.html.HtmlDocSerializer} using the {@link org.apache.juneau.html.HtmlDocSerializer#HTMLDOC_template HTMLDOC_template} setting.
The interface implementation is open-ended allowing you to define the contents of the page any way you wish.
The {@link org.apache.juneau.html.HtmlSchemaSerializer} class is the HTML-equivalent to the {@link org.apache.juneau.json.JsonSchemaSerializer} class. It's used to generate HTML versions of JSON-Schema documents that describe the output generated by the {@link org.apache.juneau.json.JsonSerializer} class.
The code for creating our POJO model and generating HTML-Schema is shown below:
The result is the HTML table shown below:
type | object | ||||||||||||||||||||||||||||||||||||||||||
properties |
|
Juneau supports converting arbitrary POJOs to and from UON strings using ultra-efficient serializers and parsers. The serializer converts POJOs directly to UON strings without the need for intermediate DOM objects using a highly-efficient state machine. Likewise, the parser creates POJOs directly from UON strings without the need for intermediate DOM objects.
Juneau uses UON (URL-Encoded Object Notation) for representing POJOs. The UON specification can be found here.
The following example shows JSON for a typical bean:
Person 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 {@link org.apache.juneau.uon.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 {@link org.apache.juneau.uon.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 {@link org.apache.juneau.urlencoding.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 {@link org.apache.juneau.urlencoding.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 {@link org.apache.juneau.urlencoding.annotation.UrlEncoding @UrlEncoding} annotation is used to override the behavior of {@link org.apache.juneau.urlencoding.UrlEncodingSerializer} on individual bean classes or properties.
The {@link org.apache.juneau.urlencoding.annotation.UrlEncoding#expandedParams() expandedParams} setting is used to force bean properties of type array or Collection to be expanded into multiple key/value pairings. It's identical in behavior to using the {@link org.apache.juneau.urlencoding.UrlEncodingSerializer#URLENC_expandedParams} and {@link org.apache.juneau.urlencoding.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 {@link org.apache.juneau.msgpack.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 {@link org.apache.juneau.msgpack.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:
The {@link org.apache.juneau.httppart.HttpPartSchema} class is used to define the formatting and validations for a POJO. It's used in conjunction with the serializer and parser to produce and consume HTTP parts based on OpenAPI rules.
Later in the rest-server and rest-client sections, we also describe how the following annotations can be applied to method parameters and class types to define the schema for various HTTP parts:
Unlike the other Juneau serializers and parsers that convert input and output directly to-and-from POJOs,
the OpenAPI serializers and parsers use intermediate objects based on the type
and format
of the schema.
The following table shows the "natural" intermediate type of the object based on the type/format
:
Type | Format | Intermediate Java Type |
---|---|---|
string or empty |
byte |
|
date |
{@link java.util.Calendar} | |
uon |
No intermediate type. (serialized directly to/from POJO) |
|
empty | {@link java.lang.String} | |
boolean |
empty | {@link java.lang.Boolean} |
integer |
int32 |
{@link java.lang.Integer} |
int64 |
{@link java.lang.Long} | |
number |
float |
{@link java.lang.Float} |
double |
{@link java.lang.Double} | |
array |
empty | Arrays of intermediate types on this list. |
uon |
No intermediate type. (serialized directly to/from POJO) |
|
object |
empty | Map<String,Object> |
uon |
No intermediate type. (serialized directly to/from POJO) |
The valid POJO types for serializing/parsing are based on the intermediate types above. As a general rule, any POJOs that are the intermediate type or transformable to or from the intermediate type are valid POJO types.
For example, the following POJO type can be transformed to and from a byte array.
This example shows how that POJO can be converted to a BASE64-encoded string.
In addition to defining format, the schema also allows for validations of the serialized form.
It looks simple, but the implementation is highly sophisticated being able to serialize and parse and validate using complex schemas.
The next sections go more into depth on serializing and parsing various POJO types.
The {@link org.apache.juneau.oapi.OpenApiSerializer} class is used to convert POJOs to HTTP parts.
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
of the schema can
be serialized using the OpenAPI serializer.
Here are the rules of POJO types allowed for various type/format combinations:
Type | Format | Valid parameter types |
---|---|---|
string or empty |
byte |
|
date |
|
|
uon |
|
|
empty |
|
|
boolean |
empty |
|
integer |
int32 |
|
int64 |
|
|
number |
float |
|
double |
|
|
array |
empty |
|
uon |
|
|
object |
empty |
|
uon |
|
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 object
type is not officially part of the OpenAPI standard.
However, Juneau supports serializing Maps and beans to HTTP parts using UON notation.
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) )
SerializeExceptions
if you attempt an impossible conversion.
(e.g. trying to serialize the string "foo" as a boolean).
The {@link org.apache.juneau.oapi.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
of the schema can
be parsed using the OpenAPI parser.
Here are the rules of POJO types allowed for various type/format combinations:
Type | Format | Valid parameter types |
---|---|---|
string or empty |
byte |
|
date |
|
|
uon |
|
|
empty |
|
|
boolean |
empty |
|
integer |
int32 |
|
int64 |
|
|
number |
float |
|
double |
|
|
array |
empty |
|
uon |
|
|
object |
empty |
|
uon |
|
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 object
type is not officially part of the OpenAPI standard, but
Juneau supports parsing HTTP parts in UON notation to Maps and beans.
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 Object
properties would have produced similar results:
We can also parse into Maps as well:
String input =
ParseExceptions
if you attempt an impossible conversion.
(e.g. trying to parse the string "foo" into a boolean).
juneau-marshall-rdf-{@property juneauVersion}.jar
org.apache.juneau.marshall.rdf_{@property juneauVersion}.jar
The juneau-marshall-rdf
library provides additional serializers and parsers for RDF.
These rely on the Apache Jena library to provide support for the following languages:
Juneau supports serializing and parsing arbitrary POJOs to and from the following RDF formats:
The serializers and parsers work identically to those in juneau-marshall
, but are
packaged separately so that you don't need to pull in the Jena dependency unless you need it.
The {@link org.apache.juneau.jena.RdfSerializer} class is the top-level class for all Jena-based serializers.
Language-specific serializers are defined as inner subclasses of the RdfSerializer
class:
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 {@link org.apache.juneau.jena.RdfParser} class is the top-level class for all Jena-based parsers.
Language-specific parsers are defined as inner subclasses of the RdfParser
class:
The RdfParser.Xml
parser handles both regular and abbreviated RDF/XML.
The RDF parser provides the following settings:
The following pre-configured parsers are provided for convenience:
The {@link org.apache.juneau.jena.annotation.Rdf @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 juneau
namespace is used for generic names for objects that don't have namespaces
associated with them.
The juneaubp
namespace is used on bean properties that don't have namespaces associated with
them.
The easiest way to specify namespaces is through annotations.
In this example, we're going to associate the prefix 'per'
to our bean class and all properties
of this class.
We do this by adding the following annotation to our class:
In general, the best approach is to define the namespace URIs at the package level using a
package-info.java
class, like so:
This assigns a default prefix of
Now when we rerun the sample code, we'll get the following:
Namespace auto-detection ({@link org.apache.juneau.xml.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 java.net.URI
or java.net.URL
have special meaning to the
RDF serializer.
They are interpreted as resource identifiers.
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 {@link org.apache.juneau.annotation.URI @URI} annotation can also be used on classes and properties
to identify them as URLs when they're not instances of java.net.URI
or java.net.URL
(not needed if
is already specified).
The following properties would have produced the same output as before.
Note that the
Also take note of the {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriResolution}, {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriRelativity}, and and {@link org.apache.juneau.serializer.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 person/1
is the root node in our tree once in triplet
form, and in some cases it's impossible.
By default, the {@link org.apache.juneau.jena.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 {@link org.apache.juneau.jena.RdfSerializer#RDF_addRootProperty}
was introduced.
When enabled, this adds a special root
attribute to the root node to make it easy to locate
by the parser.
To enable, set the
Now when we rerun the sample code, we'll see the added root
attribute on the root resource.
XML-Schema data-types can be added to non-String
literals through the
{@link org.apache.juneau.jena.RdfSerializer#RDF_addLiteralTypes} setting.
To enable, set the
Now when we rerun the sample code, we'll see the added root
attribute on the root resource.
juneau-dto-{@property juneauVersion}.jar
org.apache.juneau.dto_{@property juneauVersion}.jar
The juneau-dto
library contains several predefined POJOs for generating commonly-used document types.
This section describes support for these POJOs.
The Juneau HTML5 DTOs are simply beans with fluent-style setters that allow you to quickly construct HTML fragments as Java objects. These object can then be serialized to HTML using one of the existing HTML serializers, or to other languages such as JSON using the JSON serializers.
The {@link org.apache.juneau.dto.html5.HtmlBuilder} class is a utility class with predefined static methods that allow you to easily construct DTO instances in a minimal amount of code.
The following examples show how to create 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 {@link org.apache.juneau.html.HtmlParser} class can be used convert these HTML documents back into POJOs.
Other serializers and parsers (e.g. {@link org.apache.juneau.json.JsonSerializer}) can be used to represent these POJOs in languages other than HTML.
{@doc org.apache.juneau.dto.html5#Overview Overview}
{@doc org.apache.juneau.dto.html5#Serialize Generating HTML5}
{@doc org.apache.juneau.dto.html5#Parse Parsing HTML5}
{@doc org.apache.juneau.dto.html5#Templates HTML5 Templates}
The Juneau ATOM feed DTOs are simply beans with fluent-style setters. The following code shows a feed being created programmatically using the {@link org.apache.juneau.dto.atom.AtomBuilder} class.
To serialize this to ATOM, use the {@link org.apache.juneau.xml.XmlSerializer} class:
The {@link org.apache.juneau.xml.XmlParser} class can be used convert these Atom documents back into POJOs.
Other serializers and parsers (e.g. {@link org.apache.juneau.json.JsonSerializer}) can be used to represent these POJOs in languages other than XML.
{@doc org.apache.juneau.dto.atom#Overview Overview}
{@doc org.apache.juneau.dto.atom#Serialize Serializing ATOM feeds}
{@doc org.apache.juneau.dto.atom#AtomJson ATOM/JSON}
{@doc org.apache.juneau.dto.atom#AtomRdfXml ATOM/RDF/XML}
{@doc org.apache.juneau.dto.atom#AtomHtml ATOM/HTML}
{@doc org.apache.juneau.dto.atom#Parse Parsing ATOM feeds}
The Juneau Swagger DTOs are simply beans with fluent-style setters that allow you to quickly construct Swagger documents as Java objects. These object can then be serialized to JSON using one of the existing JSON serializers, or to other languages such as XML or HTML using the other serializers.
The {@link org.apache.juneau.dto.swagger.SwaggerBuilder} class is a utility class with predefined static methods that allow you to easily construct DTO instances in a minimal amount of code.
The following is an example Swagger document from the Swagger website.
{
This document can be generated by the following Java code:
Methods that take in beans and collections of beans can also take in JSON representations of those objects.
Properties can also be accessed via the {@link org.apache.juneau.dto.swagger.SwaggerElement#get(String,Class)}
and {@link org.apache.juneau.dto.swagger.SwaggerElement#set(String,Object)} methods.
These methods can also be used to set and retrieve non-Swagger attributes such as
Swagger docs can be parsed back into Swagger beans using the following code:
Swagger swagger = JsonParser.
The {@link org.apache.juneau.dto.swagger.ui.SwaggerUI} class is a DTO class for generating Swagger user interfaces from {@link org.apache.juneau.dto.swagger.Swagger} beans.
The PetStore
example described later provides an example of auto-generated Swagger JSON:
Using {@link org.apache.juneau.dto.swagger.ui.SwaggerUI}, we're able to render that JSON as a Swagger user interface when the request is asking for HTML:
The class itself is nothing more than a POJO swap that swaps out {@link org.apache.juneau.dto.swagger.Swagger} beans with {@link org.apache.juneau.dto.html5.Div} elements:
The {@link org.apache.juneau.rest.BasicRestServlet} class (describe later) shows how this swap is used in the REST interface to generate the Swagger UI shown above:
juneau-svl-{@property juneauVersion}.jar
org.apache.juneau.svl_{@property juneauVersion}.jar
The
Most variables can be recursively nested within the varKey (e.g.
The {@link org.apache.juneau.svl.VarResolver} class is used to resolve variables. The {@link org.apache.juneau.svl.VarResolver#DEFAULT} resolver is a reusable instance of this class configured with the following basic variables:
$S{key[,default]}
$E{key[,default]}
The following logic variables are also provided:
$IF{arg,then[,else]}
$SW{arg,pattern1:then1[,pattern2:then2...]}
$CO{arg1[,arg2...]}
$PM{arg,pattern}
$PR{arg,pattern,replace}
$PE{arg,pattern,groupIndex}
$NE{arg}
$UC{arg}
$LC{arg}
$LN{arg[,delimiter]}
$ST{arg,start[,end]}
The following shows how variables can be arbitrarily nested...
Variables are defined through the {@link org.apache.juneau.svl.Var} API. The API comes with several predefined variables and is easily extensible.
The following is an example of a variable that performs URL-Encoding on strings.
The following shows the class hierarchy of the {@link org.apache.juneau.svl.Var} class:
The following is the list of default variables defined in all modules:
Module | Class | Pattern |
---|---|---|
juneau-svl | {@link org.apache.juneau.svl.vars.EnvVariablesVar} | $E{key[,default]} |
{@link org.apache.juneau.svl.vars.SystemPropertiesVar} | $S{key[,default]} | |
{@link org.apache.juneau.svl.vars.ArgsVar} | $A{key[,default]} | |
{@link org.apache.juneau.svl.vars.ManifestFileVar} | $MF{key[,default]} | |
{@link org.apache.juneau.svl.vars.IfVar} | $IF{arg,then[,else]} | |
{@link org.apache.juneau.svl.vars.SwitchVar} | $SW{arg,pattern1:then1[,pattern2:then2...]} | |
{@link org.apache.juneau.svl.vars.CoalesceVar} | $CO{arg1[,arg2...]} | |
{@link org.apache.juneau.svl.vars.PatternMatchVar} | $PM{arg,pattern} | |
{@link org.apache.juneau.svl.vars.PatternReplaceVar} | $PR{arg,pattern,replace} | |
{@link org.apache.juneau.svl.vars.PatternExtractVar} | $PE{arg,pattern,groupdIndex} | |
{@link org.apache.juneau.svl.vars.NotEmptyVar} | $NE{arg} | |
{@link org.apache.juneau.svl.vars.UpperCaseVar} | $UC{arg} | |
{@link org.apache.juneau.svl.vars.LowerCaseVar} | $LC{arg} | |
{@link org.apache.juneau.svl.vars.LenVar} | $LN{arg[,delimiter]} | |
{@link org.apache.juneau.svl.vars.SubstringVar} | $ST{arg,start[,end]} | |
juneau-config | {@link org.apache.juneau.config.vars.ConfigVar} | $C{key[,default]} |
juneau-rest-server | {@link org.apache.juneau.rest.vars.FileVar} | $F{path[,default]}} |
{@link org.apache.juneau.rest.vars.ServletInitParamVar} | $I{name[,default]} | |
{@link org.apache.juneau.rest.vars.LocalizationVar} | $L{key[,args...]} | |
{@link org.apache.juneau.rest.vars.RequestAttributeVar} | $RA{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestFormDataVar} | $RF{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestHeaderVar} | $RH{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestHeaderVar} | $RI{key} | |
{@link org.apache.juneau.rest.vars.RequestPathVar} | $RP{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestQueryVar} | $RQ{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.RequestVar} | $R{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.SerializedRequestAttrVar} | $SA{contentType,key[,default]} | |
{@link org.apache.juneau.rest.vars.SwaggerVar} | $SS{key1[,key2...]} | |
{@link org.apache.juneau.rest.vars.UrlVar} | $U{uri}> | |
{@link org.apache.juneau.rest.vars.UrlEncodeVar} | $UE{uriPart} | |
{@link org.apache.juneau.rest.vars.WidgetVar} | $W{name} |
The main class for performing variable resolution is {@link org.apache.juneau.svl.VarResolver}. Two methods are provided for resolving variables:
Var resolvers can rely on the existence of other objects. For example, {@link org.apache.juneau.config.vars.ConfigVar} relies on the existence of a {@link org.apache.juneau.config.Config}. This is accomplished through the following:
The following two classes are identical in behavior except for which objects they can access:
Context and session objects are set through the following methods:
Both kinds of objects are accessible through the following method:
Var resolvers can be cloned and extended by using the {@link org.apache.juneau.svl.VarResolver#builder()} method. Cloning a resolver will copy it's {@link org.apache.juneau.svl.Var} class names and context objects.
$ , { }
'MYPROPERTY'
has the value '$E{MYPROPERTY}'
).
So don't do that!
resolver.resolve("foobar" )
) will simply be a no-op and return the same string.
juneau-config-{@property juneauVersion}.jar
org.apache.juneau.config_{@property juneauVersion}.jar
The juneau-config
library contains a powerful API for creating and using INI-style config files.
Config files are access through the {@link org.apache.juneau.config.Config} class which
are created through the {@link org.apache.juneau.config.ConfigBuilder} class.
Builder creator methods are provided on the Config
class:
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 {@doc PojoCategories 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 {@doc juneau-marshall.JsonDetails.SimplifiedJson 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 getStringArray()
methods allow you to retrieve comma-delimited lists of values:
String[] key1 = c.getStringArray(
String arrays can also be represented in JSON when using the getObject()
methods:
String[] key1 = c.getObject(
Primitive arrays can also be retrieved using the getObject()
methods:
Arrays of POJOs can also be retrieved using the getObject()
methods:
Address[] addresses = c.getObject(
The following methods are provided for accessing maps and collections:
The Type,Type...
arguments allow you to specify the component types for maps and collections.
List
class arguments can be followed by zero or one arguments representing the entry types.
Map
class arguments can be followed by zero or two arguments representing the key and value types.
The arguments can be chained to produce any data structure consisting of maps, collections, or POJOs.
Examples are shown below:
getObject("..." , List.class )
List<?>
getObject("..." , LinkedList.class )
LinkedList<?>
getObject("..." , HashSet.class , Integer.class )
HashSet<Integer>
getObject("..." , Map.class )
Map<?,?>
getObject("..." , HashMap.class )
HashMap<?,?>
getObject("..." , LinkedHashMap.class , String.class , MyBean.class )
LinkedHashMap<String,MyBean>
getObject("..." , HashMap.class , Integer.class , ArrayList.class , MyBean[].class )
LinkedHashMap<Integer,ArrayList<MyBean[]>>
List<Address> addresses = c.getObject(
Oftentimes, it might be useful to parse into the {@link org.apache.juneau.ObjectList} and {@link org.apache.juneau.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:
"Zm9vYmFycw=="
"666F6F62617273"
"66 6F 6F 62 61 72 73"
The binary data format is controlled via the following setting:
For example:
Binary lines can be split up into separate lines for readability:
Binary data line wrapping can be controlled via the following setting:
Config files can contain variables that get resolved dynamically using the previously-described {@link org.apache.juneau.svl.VarResolver} API.
Config c = Config.
By default, Configs
use the {@link org.apache.juneau.svl.VarResolver#DEFAULT} variable resolver
which provides support for the following variables and constructs:
$S{key[,default]}
$E{key[,default]}
$C{key[,default]}
The variable resolver is controlled via the following setting:
Additionally, the following method can be used to retrieve a Config
with a different variable resolver:
The default variable resolver also provides the following logic variables for performing simple logical operations:
$IF{arg,then[,else]}
$SW{arg,pattern1:then1[,pattern2:then2...]}
$CO{arg1[,arg2...]}
$PM{arg,pattern}
$NE{arg}
$UC{arg}
$LC{arg}
The $IF
variable can be used for simple if/else logic:
The $SW
variable can be used for switch blocks based on pattern matching:
The $CO
variable can be used for coalescing of values (finding the first non-null/empty match):
The $PM
variable can be used for calculating boolean values:
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 {@link org.apache.juneau.config.encode.ConfigXorEncoder} which is a simple XOR+Base64 encoder.
Custom encoders can be used to provide your own encoding support by implementing the {@link org.apache.juneau.config.encode.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 {@link org.apache.juneau.config.Config#commit()} method. They can also be encoded immediately by calling {@link org.apache.juneau.config.Config#encodeEntries()}.
Config sections can be retrieved in-bulk using {@link org.apache.juneau.config.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 {@link org.apache.juneau.config.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 {@link org.apache.juneau.config.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 {@link org.apache.juneau.config.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 {@link org.apache.juneau.config.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 setSection
methods:
Changes made to the configuration are transactional in nature. They are kept in memory until you call the {@link org.apache.juneau.config.Config#commit()} method. Until then, you still see the modified values when you call any of the getters, but the modified values exist only in memory.
Changes can be rolled back using the {@link org.apache.juneau.config.Config#rollback()} method.
In general, external file modifications will be detected immediately in the Config
object when a watcher thread is enabled (explained later).
Otherwise, they are detected when a commit is performed.
The Config
object maintains an in-memory record of all changes that have been applied to it
through getters and setters.
When the underlying file changes, the new contents are loaded and the in-memory changes are then
applied to the new configuration.
This provides the benefits of real-time updates of configurations while not losing any changes made in the JVM.
If the commit()
method is called on the Config
objects after the file system
contents have been modified, we will then reload the configuration from the file system, apply the
changes, and then try to save to the file system again (up to 10 times).
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 Serializer
can be used to provide custom serialization of entries
instead of using the predefined serializer.
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 {@link org.apache.juneau.config.Config#commit()} method.
Configuration change events can be listened for using the following methods:
The {@link org.apache.juneau.config.event.ConfigEventListener} interface consists of the following method:
The {@link org.apache.juneau.config.event.ConfigEvent} class provides access to all the information about the updated entry:
The listener method is triggered:
In both cases, the listener is triggered after the changes have been committed.
The following methods are used for serializing Config
objects back into INI files:
Both methods are thread safe.
The Config
class implements the {@link org.apache.juneau.Writable} which means it can be
returned as-is by REST methods to be serialized as INI text.
Configuration files are stored in entities called Stores.
The methods that need to be implemented on a store are:
Read is self-explanatory:
Write is slightly trickier:
The update method is called whenever the stored file gets modified externally:
Two configuration stores are provided by default:
The store is defined on the Config
object via the following setting:
The default store used is {@link org.apache.juneau.config.store.ConfigFileStore#DEFAULT} which defines the execution directory as the file system directory to store and retrieve files.
The {@link org.apache.juneau.config.store.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 {@link org.apache.juneau.config.store.ConfigFileStore} is the typical store used for configuration files. It provides the following configurable settings:
The ConfigStore
API has been written to allow easy development of custom configuration storage classes.
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 {@link org.apache.juneau.PropertyStore}
that's passed to the ConfigStore
:
The ConfigStore
class has the following listener methods:
Note that this is a different listener than {@link org.apache.juneau.config.event.ConfigEventListener}. In this case, we're just listening for changed files:
This listener is used by the Config
class to listen for changes on the file system so that it can be
updated in real-time.
The following settings can be used to create read-only Config
objects:
This causes all methods that make modifications to throw {@link java.lang.UnsupportedOperationException}.
In general, it's good practice to close Config if you're only creating them temporarily so that their listeners get unregistered from the underlying storage APIs.
Each JVM has a system default config. This is a configuration file that serves as the default configuration for the system. It's accessed using the following static methods:
If you do not specify a system default config, one will be automatically searched for. The search is done in the following order:
<jar-name>.cfg
.cfg
. First one matched alphabetically is used.
<jar-name>.cfg
juneau.cfg
default.cfg
Later in the section on REST resources, we describe how to associate configurations with REST resources
using the {@link org.apache.juneau.rest.annotation.RestResource#config() @RestResource(config)} annotation.
The system default configuration can be referenced with the keyword SYSTEM_DEFAULT
like so:
juneau-rest-server-{@property juneauVersion}.jar
org.apache.juneau.rest.server_{@property juneauVersion}.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, {@link org.apache.juneau.rest.RestServlet} is nothing but a specialized {@link javax.servlet.http.HttpServlet}, and the {@link org.apache.juneau.rest.RestRequest} and {@link org.apache.juneau.rest.RestResponse} classes are nothing more than specialized {@link javax.servlet.http.HttpServletRequest} and {@link javax.servlet.http.HttpServletResponse} objects. This allows maximum flexibility for the developer since you can let Juneau handle operations such as serialization, or you can revert to the existing servlet APIs to do low-level processing of requests yourself. It also means you need nothing more than a Servlet container such as Jetty to use the REST framework.
Many of the examples in this document are pulled directly from
A REST resource is simply a Java class annotated with {@link org.apache.juneau.rest.annotation.RestResource}. The most common case is a class that extends {@link org.apache.juneau.rest.RestServlet}, which itself is simply an extension of {@link javax.servlet.http.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 {@link org.apache.juneau.rest.BasicRestServlet} class that we're using here is a subclass of {@link org.apache.juneau.rest.RestServlet} that provides default support for a variety of content types. Implementers can choose to use this class, or create their own subclass of {@link org.apache.juneau.rest.RestServlet} with their own specialized serializers and parsers.
The class hierarchy for the REST servlet class is shown below:
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:
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
public T()
public T(RestContextBuilder)
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 {@link org.apache.juneau.rest.RestServlet} class is the entry point for your REST resources.
It extends directly from
When the servlet
Most developers are not going to be using the
{@link org.apache.juneau.rest.RestServlet} extends HttpServlet
The {@link org.apache.juneau.rest.BasicRestServlet} class is a subclass of {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.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.
Child Resources are REST servlets or objects that are linked to parent resources through the {@link org.apache.juneau.rest.annotation.RestResource#children() @RestResource(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 {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.BasicRestServletGroup} class is nothing more than a subclass of
{@link org.apache.juneau.rest.BasicRestServlet} with a
By default, you can add the {@link org.apache.juneau.rest.annotation.RestResource @RestResource} to any class as long as it has one of the following constructors:
public T()
public T(RestContextBuilder)
The latter constructor can be used to get access to the {@link org.apache.juneau.rest.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:
The {@link org.apache.juneau.rest.annotation.RestResource @RestResource} annotation is the primary way of defining and configuring REST resource classes. The functionality of the class itself is covered in detail in the topics below.
The {@link org.apache.juneau.rest.annotation.RestResource @RestResource} 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.
{@link org.apache.juneau.rest.annotation.RestResource#guards() 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. |
{@link org.apache.juneau.rest.annotation.RestResource#converters() 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. |
{@link org.apache.juneau.rest.annotation.RestResource#beanFilters() beanFilters()} |
Bean filters on child override those on parent class.
{@link org.apache.juneau.rest.Inherit} class can be used to inherit and augment values from parent. {@link org.apache.juneau.rest.None} class can be used to suppress inheriting from parent. |
{@link org.apache.juneau.rest.annotation.RestResource#pojoSwaps() pojoSwaps()} |
POJO swaps on child override those on parent class.
{@link org.apache.juneau.rest.Inherit} class can be used to inherit and augment values from parent. {@link org.apache.juneau.rest.None} class can be used to suppress inheriting from parent. |
{@link org.apache.juneau.rest.annotation.RestResource#properties() 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. |
{@link org.apache.juneau.rest.annotation.RestResource#serializers() serializers()} |
Serializers on child override those on parent class.
{@link org.apache.juneau.rest.Inherit} class can be used to inherit and augment values from parent. {@link org.apache.juneau.rest.None} class can be used to suppress inheriting from parent. Serializers on methods take precedence over those on classes. |
{@link org.apache.juneau.rest.annotation.RestResource#parsers() parsers()} |
Parsers on child override those on parent class.
{@link org.apache.juneau.rest.Inherit} class can be used to inherit and augment values from parent. {@link org.apache.juneau.rest.None} class can be used to suppress inheriting from parent. Parsers on methods take precedence over those on classes. |
{@link org.apache.juneau.rest.annotation.RestResource#responseHandlers() responseHandlers()} | Response handlers on child are combined with those on parent class. |
{@link org.apache.juneau.rest.annotation.RestResource#encoders() encoders()} | Encoders on child are combined with those on parent class. |
{@link org.apache.juneau.rest.annotation.RestResource#defaultRequestHeaders() 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. |
{@link org.apache.juneau.rest.annotation.RestResource#defaultResponseHeaders() 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. |
{@link org.apache.juneau.rest.annotation.RestResource#children() 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. |
{@link org.apache.juneau.rest.annotation.RestResource#path() path()} | Path is searched for in child-to-parent order. |
{@link org.apache.juneau.rest.annotation.RestResource#title() title()} | Label is searched for in child-to-parent order. |
{@link org.apache.juneau.rest.annotation.RestResource#description() description()} | Description is searched for in child-to-parent order. |
{@link org.apache.juneau.rest.annotation.RestResource#config() config()} | Config file is searched for in child-to-parent order. |
{@link org.apache.juneau.rest.annotation.RestResource#staticFiles() 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 {@link org.apache.juneau.rest.RestContext} object is the workhorse class for all of the configuration of a single REST resource class. It's by-far the most important class in the REST API.
Every class annotated with
The annotation should be self-explanatory at this point. The builder allows you to perform all of the same configuration as the annotation programmatically.
The {@link org.apache.juneau.rest.RestContextBuilder} class extends {@link org.apache.juneau.BeanContextBuilder} allowing you to programmatically set any properties defined on that builder class. It also implements {@link javax.servlet.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 {@link org.apache.juneau.rest.annotation.RestMethod @RestMethod} annotation. The annotation allows the framework to identify the available REST methods through reflection.
When the name
and/or path
values are not specified, their values are inferred
from the Java method name.
The HTTP method can be inferred from the Java method by starting the method name with any of the following:
get
put
post
delete
options
head
trace
patch
If path
is not defined, it's inferred from the Java method name (minus the prefix above).
If name
and path
are both specified, the Java method name can be anything.
Java methods can contain any of the following parameters in any order:
RestRequest
.
RestResponse
.
@RestMethod (name="*" )
)
The {@link org.apache.juneau.rest.RestRequest} object is an extension of the
There are many useful methods on this object, but the main ones are shown below:
{@link org.apache.juneau.rest.RestRequest} extends HttpServletRequest
The {@link org.apache.juneau.rest.RestResponse} object is an extension of the
Some important methods on this class are:
{@link org.apache.juneau.rest.RestResponse} extends HttpServletResponse
The {@link org.apache.juneau.rest.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:
The {@link org.apache.juneau.rest.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:
{@link org.apache.juneau.rest.RequestHeaders} extends TreeMap<String,String[]>
The {@link org.apache.juneau.rest.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:
{@link org.apache.juneau.rest.RequestQuery} extends LinkedHashMap<String,String[]>
The {@link org.apache.juneau.rest.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:
{@link org.apache.juneau.rest.RequestFormData} extends LinkedHashMap<String,String[]>
The {@link org.apache.juneau.rest.annotation.RestMethod#path() @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 {@link org.apache.juneau.rest.annotation.RestMethod#priority() @RestMethod(priority)} annotation property. However, in practice this is almost never needed.
Paths that end with
annotation.
On the other hand, paths that don't end with
The following example shows the distinction.
Annotations are provided for easy access to URL parameters with automatic conversion to any {@doc PojoCategories parsable} type.
For example, the following example can process the URL
The {@link org.apache.juneau.rest.RequestPath} object is the API for accessing the matched variables and remainder on the URL path.
Some important methods on this class are:
{@link org.apache.juneau.rest.RequestPath} extends TreeMap<String,String>
The return type can be any serializable POJO as defined in {@doc PojoCategories}.
It can also be
Out-of-the-box, besides POJOs, the following return types are handled as special cases:
This is controlled through the following extensible API:
REST Java methods can generate output in any of the following ways:
The {@link org.apache.juneau.http.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 {@link org.apache.juneau.http.annotation.Response @Response} which allows it to be returned as responses by REST methods.
The {@link org.apache.juneau.http.StreamResource} class is the binary equivalent to the {@link org.apache.juneau.http.ReaderResource} object. In essence, it's a container for binary data with optional response headers.
The class is annotated with {@link org.apache.juneau.http.annotation.Response @Response} which allows it to be returned as responses by REST methods.
{@link org.apache.juneau.rest.RestMatcher RestMatchers} are used to allow multiple Java methods to be tied to the same HTTP method and path, but differentiated by some request attribute such as a specific header value.
The interface for matchers is simple:
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 {@link org.apache.juneau.http.annotation.Response @Response}-annotated objects that are describe in detail later. Without going into details, this is how the {@link org.apache.juneau.rest.response.SeeOther} is defined:
The {@link org.apache.juneau.rest.helper.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.
These exception extend from {@link java.lang.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 {@link org.apache.juneau.rest.helper} package contains several predefined beans to help when constructing REST interfaces.
The {@link org.apache.juneau.rest.helper.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:
{@link org.apache.juneau.rest.helper.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 {@link org.apache.juneau.html.annotation.HtmlLink @HtmlLink} annotation can also be useful for rendering custom hyperlinks:
The {@link org.apache.juneau.dto.LinkString 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 {@link org.apache.juneau.rest.helper.BeanDescription} class provides a simple view of a bean and it's properties.
This example renders the following:
The {@link org.apache.juneau.rest.helper.ChildResourceDescriptions} is a convenience bean for generating a table of child resources.
The {@link org.apache.juneau.rest.BasicRestServletGroup} class uses this to generate router pages:
Note that all it requires is a {@link org.apache.juneau.rest.RestRequest} object and it will generate a router page using reflection against the resource class.
For example, the RootResources
page in the REST examples renders the child resources attached to the root resource:
The RootResources
page consists of the following and extends from the {@link org.apache.juneau.rest.BasicRestServletGroup} class:
The {@link org.apache.juneau.rest.helper.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
.
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 RestClient
API, we inherit all of it's features.
For example, convenience methods for setting POJO filters and properties to customize the behavior of the
serializers and parsers, and the ability to provide your own customized Apache HttpClient
for
handling various scenarios involving authentication and Internet proxies.
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:
@RestMethod (name=RRPC )
annotation on a Java method.
In either case, the proxy communications layer is pure REST. Therefore, in cases where the interface classes are not available on the client side, the same method calls can be made through pure REST calls. This can also aid significantly in debugging, since calls to the remote interface service can be made directly from a browser with no coding involved.
The {@link org.apache.juneau.rest.remote.RrpcServlet} class is a simple specialized servlet with an abstract
getServiceMap()
method to define the server-side POJOs:
The
approach is easier if you only have a single
interface you want to expose.
You simply define a Java method whose return type is an interface, and return the implementation of that
interface:
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 IAddressBook
link shows that you can only invoke methods defined on that
interface, whereas the AddressBook
link shows ALL public methods defined on that class.
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook
Since AddressBook
extends from LinkedList
, you may notice familiar collections
framework methods listed.
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 Content-Type
of the body as text/json
.
We also want the results to be returned as JSON, so we set the Accept
header to
text/json
as well.
When we execute the POST, we should see the following successful response whose body contains the returned
Person
bean serialized to JSON:
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 getPeople()
method on our bean:
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook/getPeople?method=POST
Here we call the findPerson(
method to retrieve a person and get the
returned POJO (in this case as HTML since that's what's in the Accept
header when calling from a
browser):
http://localhost:10000/remote/org.apache.juneau.examples.addressbook.IAddressBook/findPerson(int)?method=POST&body=@(3)
When specifying the POST body as a &body
parameter, the method arguments should be in UON
notation.
See {@link org.apache.juneau.uon.UonSerializer} for more information about this encoding.
Usually you can also pass in JSON if you specify &Content-Type=text/json
in the URL parameters
but passing in unencoded JSON in a URL may not work in all browsers.
Therefore, UON is preferred.
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 {@link org.apache.juneau.oapi.OpenApiParser} and therefore support OpenAPI syntax and validation:
Content-Type
must match
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g. Longs
:
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength
that don't match the input will result in automatic 400 Bad Request
responses.
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 Longs
in the example above, we could also define a 2-dimensional array of POJOs convertible from Longs
:
Or even POJOs that take in arrays of Longs[]
:
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 {@doc juneau-marshall.OpenApiDetails.Parsers OpenAPI Parsers}
Parameters annotated with any of the following are serialized using the registered {@link org.apache.juneau.oapi.OpenApiSerializer} and therefore support OpenAPI syntax and validation:
Accept
must match
For example, the following shows how a pipe-delimited list of comma-delimited numbers (e.g. Longs
:
The following shows the same for a response body:
For more information about the valid parameter types, see {@doc juneau-marshall.OpenApiDetails.Serializers 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 {@link org.apache.juneau.http.annotation.Body @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):
Content-Type
is ignored.
Content-Type
is ignored.
Content-Type
is required to identify correct parser.
public T(Reader in) {...}
public static T create (Reader in) {...}
public static T fromReader (Reader in) {...}
Content-Type
must not be present or match an existing parser so that it's not parsed as a POJO.
public T(InputStream in) {...}
public static T create (InputStream in) {...}
public static T fromInputStream (InputStream in) {...}
Content-Type
must not be present or match an existing parser so that it's not parsed as a POJO.
public T(String in) {...}
public static T create (String in) {...}
public static T fromString (String in) {...}
public static T parse (String in) {...}
public static T parseString (String in) {...}
public static T forName (String in) {...}
public static T forString (String in) {...}
The {@link org.apache.juneau.oapi.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. Longs
:
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength
that don't match the input will result in automatic 400 Bad Request
responses.
For more information about valid parameter types when using OpenAPI parsing, see {@doc juneau-marshall.OpenApiDetails.Parsers OpenAPI Parsers}
The
{@doc DefaultRestSvlVariables} (e.g. "$L{my.localized.variable}") are supported on annotation fields.
The {@link org.apache.juneau.http.annotation.FormData @FormData} annotation is used to retrieve request form post entries.
The most typical scenario is to simply use the value
field to define form data parameter names:
This is functionally equivalent to the following code:
The special name Map
or bean.
The registered {@link org.apache.juneau.rest.RestContext#REST_partParser REST_partParser} is used to convert strings to POJOs and controls what POJO types are supported. By default, this is the {@link org.apache.juneau.oapi.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. Longs
:
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength
that don't match the input will result in automatic 400 Bad Request
responses.
For more information about valid parameter types, see {@doc juneau-marshall.OpenApiDetails.Parsers OpenAPI Parsers}
The
{@doc DefaultRestSvlVariables} (e.g. "$L{my.localized.variable}") are supported on annotation fields.
application/x-www-form-urlencoded POST
posts, since it will trigger the underlying servlet
API to parse the body content as key-value pairs resulting in empty content.
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
The following table shows the behavioral differences between
Body content |
|
|
---|---|---|
a=foo |
||
a= |
||
a |
||
b=foo |
This annotation should not be combined with the {@link org.apache.juneau.http.annotation.Body @Body} annotation or {@link org.apache.juneau.rest.RestRequest#getBody()} method
for application/x-www-form-urlencoded POST
posts, since it will trigger the underlying servlet API to
parse the body content as key-value pairs, resulting in empty content.
The {@link org.apache.juneau.http.annotation.HasQuery @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 {@link org.apache.juneau.http.annotation.Query @Query} annotation is used to retrieve request URL query parameters. It's identical to {@link org.apache.juneau.http.annotation.FormData @FormData}, but only retrieves the parameter from the URL string, not URL-encoded form posts.
Unlike {@link org.apache.juneau.http.annotation.FormData @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 {@link org.apache.juneau.http.annotation.Body @Body} annotation or
{@link org.apache.juneau.rest.RestRequest#getBody()} method for application/x-www-form-urlencoded POST
calls.
The most typical scenario is to simply use the value
field to define query parameter names:
This is functionally equivalent to the following code:
The special name Map
or bean.
The registered {@link org.apache.juneau.rest.RestContext#REST_partParser REST_partParser} is used to convert strings to POJOs and controls what POJO types are supported. By default, this is the {@link org.apache.juneau.oapi.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. Longs
:
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength
that don't match the input will result in automatic 400 Bad Request
responses.
For more information about valid parameter types, see {@doc juneau-marshall.OpenApiDetails.Parsers OpenAPI Parsers}
The
{@doc DefaultRestSvlVariables} (e.g. "$L{my.localized.variable}") are supported on annotation fields.
Identical to {@link org.apache.juneau.http.annotation.HasFormData @HasFormData}, but only checks the existing of the parameter in the URL string, not URL-encoded form posts.
Unlike {@link org.apache.juneau.http.annotation.HasFormData @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 {@link org.apache.juneau.http.annotation.Body @Body} annotation or
{@link org.apache.juneau.rest.RestRequest#getBody()} method for application/x-www-form-urlencoded POST
calls.
This is functionally equivalent to the following code:
The parameter type must be either
The following table shows the behavioral differences between
Query content |
|
|
---|---|---|
?a=foo |
||
?a= |
||
?a |
||
?b=foo |
The {@link org.apache.juneau.http.annotation.Header @Header} annotation is used to retrieve request headers.
The most typical scenario is to simply use the value
field to define header parameter names:
This is functionally equivalent to the following code:
The special name Map
or bean.
The registered {@link org.apache.juneau.rest.RestContext#REST_partParser REST_partParser} is used to convert strings to POJOs and controls what POJO types are supported. By default, this is the {@link org.apache.juneau.oapi.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. Longs
:
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength
that don't match the input will result in automatic 400 Bad Request
responses.
For more information about valid parameter types, see {@doc juneau-marshall.OpenApiDetails.Parsers OpenAPI Parsers}
The
{@doc DefaultRestSvlVariables} (e.g. "$L{my.localized.variable}") are supported on annotation fields.
The {@link org.apache.juneau.http.annotation.Path @Path} annotation is used to retrieve request path parameters.
The most typical scenario is to simply use the value
field to define path parameter names:
This is functionally equivalent to the following code:
Note that the path variable name
The special name Map
or bean.
The registered {@link org.apache.juneau.rest.RestContext#REST_partParser REST_partParser} is used to convert strings to POJOs and controls what POJO types are supported. By default, this is the {@link org.apache.juneau.oapi.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. Longs
:
Input will be converted based on the types and formats defined in the schema definition.
Input validations such as minLength/maxLength
that don't match the input will result in automatic 400 Bad Request
responses.
For more information about valid parameter types, see {@doc juneau-marshall.OpenApiDetails.Parsers OpenAPI Parsers}
The
{@doc DefaultRestSvlVariables} (e.g. "$L{my.localized.variable}") are supported on annotation fields.
The {@link org.apache.juneau.http.annotation.Request @Request} annotation can be applied to a parameter interface type of a
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 {@link org.apache.juneau.http.annotation.Response @Response} annotation is used to identify schema information about an HTTP response.
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 {@link org.apache.juneau.rest.exception.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 Ok
class which is defined like so:
Another example showing how a redirect can be defined:
The
The
The
In the above example, the
The {@link org.apache.juneau.http.annotation.ResponseStatus @ResponseStatus} annotation can be used on
the method of a 200
(the default).
The {@link org.apache.juneau.http.annotation.ResponseHeader @ResponseHeader} annotation can be used on
the method of a
The {@link org.apache.juneau.http.annotation.ResponseBody @ResponseBody} annotation can be used on
the method of a
If a toString()
).
By default, POJOs representing the body of the request are serialized using the Juneau serializer
matching the requesting Accept
header.
The {@link org.apache.juneau.oapi.OpenApiSerializer} class can be used to serialize response bodies using OpenAPI rules.
The following examples show part-schema-based serialization of response bodies:
The
annotation can be used to define the format of the output using OpenAPI-based rules.
The attributes on this annotation are also used to populate the generated Swagger for the method.
For example, in the case of the InvalidLogin
example above, the following Swagger is generated:
When the {@link org.apache.juneau.http.annotation.Response#code() @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 {@link org.apache.juneau.http.annotation.ResponseHeader @ResponseHeader} annotation can be applied to
This annotation can only be applied to parameters of type {@link org.apache.juneau.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 X-Rate-Limit
example above, the following Swagger is generated:
The {@link org.apache.juneau.http.annotation.ResponseStatus @ResponseStatus} annotation annotation can be applied to
This can only be applied to parameters of the {@link org.apache.juneau.Value} class with an {@link java.lang.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.
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 {@link org.apache.juneau.serializer.Serializer} API for defining serializers for serializing response POJOs.
The servlet will pick which serializer to use by matching the request
Serializers can be associated with REST servlets in the following ways:
The following are all equivalent ways of defining serializers used by a resource:
REST resources use the {@link org.apache.juneau.parser.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
Parsers can be associated with REST servlets in the following ways:
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 {@doc ConfigurableProperties})
These properties can be defined for serializers and parsers registered on a REST resource via the following:
The programmatic equivalent to this is:
Properties can also be overridden at the Java method level:
Using the {@link org.apache.juneau.rest.RequestProperties} object:
Properties set via {@link org.apache.juneau.rest.RequestProperties} are session-override
properties that are passed in through {@link org.apache.juneau.serializer.SerializerSessionArgs}
and {@link org.apache.juneau.parser.ParserSessionArgs} and can only be used on configuration settings
marked as Session property:
.
Properties are open-ended and can be used for other purposes. They're made available through the following methods:
The Juneau serializers and parsers can be configured on how to handle POJOs through the use of Transforms. (See {@doc juneau-marshall.Transforms Transforms})
Transforms are associated serializers and parsers registered on a REST resource via the following:
The programmatic equivalent to this is:
As mention earlier {@doc juneau-marshall.URIs 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
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.
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 {@link org.apache.juneau.utils.PojoRest} wrapper class to address nodes in the tree.
Juneau defines the following converters out-of-the-box:
The {@link org.apache.juneau.rest.annotation.RestResource#messages @RestResource(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 {@link java.util.ResourceBundle} or {@link org.apache.juneau.utils.MessageBundle}:
If a resource bundle is shared by multiple servlets, the label and description can be prefixed by the class name:
The {@link org.apache.juneau.rest.annotation.RestResource#encoders @RestResource(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:
There are two distinct groups of variables:
The following is the default list of supported variables.
Module | Class | Pattern | Initialization time | Request time | Examples |
---|---|---|---|---|---|
juneau-svl | {@link org.apache.juneau.svl.vars.EnvVariablesVar} | $E{key[,default]} | yes | yes | $E{PATH} |
{@link org.apache.juneau.svl.vars.SystemPropertiesVar} | $S{key[,default]} | yes | yes | $S{java.home} | |
{@link org.apache.juneau.svl.vars.ArgsVar} | $A{key[,default]} | yes | yes | $A{foo,null} | |
{@link org.apache.juneau.svl.vars.ManifestFileVar} | $MF{key[,default]} | yes | yes | $MF{Main-Class} | |
{@link org.apache.juneau.svl.vars.IfVar} | $IF{arg,then[,else]} | yes | yes | $IF{$S{my.boolean.property},foo,bar} | |
{@link org.apache.juneau.svl.vars.SwitchVar} | $SW{arg,p1:then1[,p2:then2...]} | yes | yes | $SW{$P{os.name},*win*:Windows,*:Something else} | |
{@link org.apache.juneau.svl.vars.CoalesceVar} | $CO{arg1[,arg2...]} | yes | yes | $CO{$S{my.property},$E{my.property},n/a} | |
{@link org.apache.juneau.svl.vars.PatternMatchVar} | $PM{arg,pattern} | yes | yes | $PM{$P{os.name},*win*} | |
{@link org.apache.juneau.svl.vars.NotEmptyVar} | $NE{arg} | yes | yes | $NE{$S{foo}} | |
{@link org.apache.juneau.svl.vars.UpperCaseVar} | $UC{arg} | yes | yes | $UC{$S{foo}} | |
{@link org.apache.juneau.svl.vars.LowerCaseVar} | $LC{arg} | yes | yes | $LC{$S{foo}} | |
juneau-config | {@link org.apache.juneau.config.vars.ConfigVar} | $C{key[,default]} | yes | yes | $C{REST/staticFiles} |
juneau-rest-server | {@link org.apache.juneau.rest.vars.FileVar} | $F{path[,default]}} | yes | yes | $F{resources/MyAsideMessage.html, Oops not found!} |
{@link org.apache.juneau.rest.vars.ServletInitParamVar} | $I{name[,default]} | yes | yes | $I{my.param} | |
{@link org.apache.juneau.rest.vars.LocalizationVar} | $L{key[,args...]} | no | yes | $L{MyMessage,foo,bar} | |
{@link org.apache.juneau.rest.vars.RequestAttributeVar} | $RA{key1[,key2...]} | no | yes | $RA{attrName} | |
{@link org.apache.juneau.rest.vars.RequestFormDataVar} | $RF{key1[,key2...]} | no | yes | $RF{paramName} | |
{@link org.apache.juneau.rest.vars.RequestHeaderVar} | $RH{key1[,key2...]} | no | yes | $RH{Header-Name} | |
{@link org.apache.juneau.rest.vars.RequestPathVar} | $RP{key1[,key2...]} | no | yes | $RP{pathVAr} | |
{@link org.apache.juneau.rest.vars.RequestQueryVar} | $RQ{key1[,key2...]} | no | yes | $RQ{paramName} | |
{@link org.apache.juneau.rest.vars.RequestVar} | $R{key1[,key2...]} | no | yes | $R{contextPath} | |
{@link org.apache.juneau.rest.vars.RestInfoVar} | $RI{key} | no | yes | $RI{externalDocs} | |
{@link org.apache.juneau.rest.vars.SerializedRequestAttrVar} | $SA{contentType,key[,default]} | no | yes | $SA{application/json,$RA{foo}} | |
{@link org.apache.juneau.rest.vars.UrlVar} | $U{uri}> | no | yes | $U{servlet:/foo} | |
{@link org.apache.juneau.rest.vars.UrlEncodeVar} | $UE{uriPart} | yes | yes | $U{servlet:/foo?bar=$UE{$RA{bar}} | |
{@link org.apache.juneau.rest.vars.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 {@link org.apache.juneau.config.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 {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.RestRequest#getConfig()} to access the config file:
You can even add resource bundles into the mix:
The {@link org.apache.juneau.rest.annotation.RestResource#staticFiles @RestResource(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 {@link java.lang.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
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 {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.BasicRestInfoProvider} class is the default implementation of the {@link org.apache.juneau.rest.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 PetStore
example provides an example of auto-generated Swagger JSON:
Using {@link org.apache.juneau.dto.swagger.ui.SwaggerUI}, we're able to render that JSON as a Swagger user interface:
Any subclass of {@link org.apache.juneau.rest.BasicRestServlet} gets an auto-generated Swagger UI when performing an OPTIONS
request with Accept:text/html
.
The underlying mechanics are simple. The {@link org.apache.juneau.rest.BasicRestServlet#getOptions(RestRequest)} method returns a {@link org.apache.juneau.dto.swagger.Swagger} bean consisting of information gathered from annotations and other sources. Then that bean is swapped for a {@link org.apache.juneau.dto.swagger.ui.SwaggerUI} bean when rendered as HTML.
Here's the class that defines the behavior:
Note that to have your resource create Swagger UI, you must either extend from {@link org.apache.juneau.rest.BasicRestServlet} or provide
your own
Let's look at the various parts of the Petstore
application Swagger UI to see how they are defined.
The top part of the page shows general information about the REST interface:
The information is pulled from the {@link org.apache.juneau.rest.annotation.RestResource#swagger() @RestResource(swagger)} annotation.
In this particular case, the Swagger is pulled in from a localized Swagger JSON file located in the
org.apache.juneau.examples.rest.petstore
package using the {@link org.apache.juneau.rest.vars.FileVar $F} variable.
{
Note that the {@link org.apache.juneau.rest.vars.FileVar $F} variable allows for request-locale-sensitive name matching so that you can provide localized Swagger information.
The {@link org.apache.juneau.rest.vars.FileVar $F} variable simply expands to a string to fill the {@link org.apache.juneau.rest.annotation.ResourceSwagger#value() @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 {@doc DefaultRestSvlVariables SVL variables}, so you could for example pull localized strings from resource bundles using {@link org.apache.juneau.rest.vars.LocalizationVar $L} variables.
A third option is to define your Swagger information in your {@link org.apache.juneau.rest.annotation.RestResource#messages @RestResource(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 pet
and store
tag sections are collapsed
and the user
section is not:
Tags are also defined in the
The annotation-only approach is shown here:
swagger=
Tags are associated with operations using the {@link org.apache.juneau.rest.annotation.MethodSwagger#tags() @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 getTopPage()
method in PetStoreResource
is not tagged,
as well as the getOptions()
method inherited from BaseRestServlet
, so these
show up at the top of the page:
The following shows the annotations defined on the GET /pet
operation:
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 type
and collectionFormat
values above are optional and auto-detected based on the
parameter class type if omitted. They're included here for clarity.
The examples will be explained in the next section.
Another option is to specify your parameter information in the parameters
annotation as free-form Simple JSON.
In the case of the PetStoreResource.getPets()
method, we pull this information from a static field
defined in the {@link org.apache.juneau.rest.converters.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 model
select box in the parameters can be expanded to show examples:
The examples for query/form-data/path/header parameters can be defined using the example
attribute on your annotated parameters as shown here:
This value gets converted to an x-examples
attribute in your parameter information:
{
Examples for request bodies includes all supported Content-Type
values:
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:
@ResourceSwagger (value)
/@MethodSwagger (value)
).
When using {@link org.apache.juneau.http.annotation.Body#example() @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 {@link org.apache.juneau.http.annotation.Body#examples() @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 x-examples
entry in your generated Swagger:
Another option is to define these directly in your resource Swagger JSON file, or via {@link org.apache.juneau.rest.annotation.RestResource#swagger() @RestResource(swagger)}/{@link org.apache.juneau.rest.annotation.RestMethod#swagger() @RestMethod(swagger)}.
Juneau also supports auto-generation of JSON-Schema directly from POJO classes. By default, the generated swagger uses to the {@link org.apache.juneau.jsonschema.JsonSchemaGenerator#JSONSCHEMA_addExamplesTo 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 {@link org.apache.juneau.BeanContext#BEAN_examples} property at either the class or method level.
Under the input parameters are listed the possible responses for the resource:
The 200
response is determined by the return type on the method, in this case a collection of Pet
objects:
Note that additional responses can be specified by throwing exceptions annotated with the {@link org.apache.juneau.http.annotation.Response @Response} annotation such as this one:
Like input parameters, the Swagger for responses can be define in multiple locations such as:
The model
select box in the responses can be expanded to show examples:
Examples are provided for any supported Accept
type based on the serializers defined on your
servlet/method.
Examples are pulled from the examples
attribute in the response object of the generated Swagger JSON:
There are several options for defining examples for response bodies:
@ResourceSwagger (value)
/@MethodSwagger (value)
).
The {@link org.apache.juneau.http.annotation.Response#example @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 examples
field.
Direct per-media-type examples can also be defined using the {@link org.apache.juneau.http.annotation.Response#examples @Response(examples)} annotation:
Juneau also supports auto-generation of JSON-Schema directly from POJO classes. By default, the generated swagger uses to the {@link org.apache.juneau.jsonschema.JsonSchemaGenerator#JSONSCHEMA_addExamplesTo 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 {@link org.apache.juneau.BeanContext#BEAN_examples} property at either the class or method level.
Response headers are also rendered in the Swagger UI:
These can be auto-generated from {@link org.apache.juneau.http.annotation.ResponseHeader @ResponseHeader} annotations defined on either method parameters or type classes. The example above shows one of each:
The {@link org.apache.juneau.jsonschema.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 $ref
attributes.
By default, this flag is enabled when extending from {@link org.apache.juneau.rest.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 Order
bean:
{
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: SwaggerUI.css
.
In the microservice template, this file is located in the files/htdocs/styles
directory.
It's a simple straightforward file consisting of less than 350 lines.
This file can be modified to change the look-and-feel of your Swagger UI.
The {@link org.apache.juneau.rest.annotation.HtmlDoc @HtmlDoc} annotation is used to customize the HTML view of your serialized POJOs. It's used in the following locations:
The annotation itself is just a convenience for setting configuration properties set on the {@link org.apache.juneau.html.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 {@link org.apache.juneau.html.HtmlDocTemplate} interface which can be overridden via the {@link org.apache.juneau.rest.annotation.HtmlDoc#template @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 {@link org.apache.juneau.rest.widget.Widget} class allows you to add arbitrary HTML, CSS, and Javascript
to HTML pages.
They are registered in the following locations:
The
The HTML content returned by the {@link org.apache.juneau.rest.widget.Widget#getHtml(RestRequest) getHtml(RestRequest)}
method is added wherever the
The CSS returned by {@link org.apache.juneau.rest.widget.Widget#getScript(RestRequest) getScript(RestRequest)} is added to the style section in the page header.
The Javascript returned by {@link org.apache.juneau.rest.widget.Widget#getScript(RestRequest) getScript(RestRequest)} 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 htdocs
static files
directory in your classpath (see {@link org.apache.juneau.rest.annotation.RestResource#staticFiles() @RestResource(staticFiles)}):
The
The {@link org.apache.juneau.rest.widget} package contains predefined reusable widgets.
{@link org.apache.juneau.rest.widget.MenuItemWidget} is an abstract class for rendering menu items with drop-downs. It defines some simple CSS and Javascript for enabling drop-down menus in the nav section of the page (although nothing keeps you from using it in an arbitrary location in the page).
The script specifies a
Subclasses implement the following two methods:
For example, to render a link that brings up a simple dialog in a div tag:
The HTML content returned by the {@link org.apache.juneau.rest.widget.MenuItemWidget#getHtml(RestRequest) getHtml(RestRequest)} method is added where the
{@link org.apache.juneau.rest.widget.ContentTypeMenuItem} is a predefined Widget that returns back a list of hyperlinks for rendering the contents of a page in a variety of content types.
The variable it resolves is
An example of this widget can be found in the PetStoreResource
in the examples that provides a drop-down menu item for rendering all other supported content types in plain text:
It renders the following popup-box:
{@link org.apache.juneau.rest.widget.QueryMenuItem} is a predefined Widget that returns a menu-item drop-down form for entering search/view/sort arguments.
The variable it resolves is
This widget is designed to be used in conjunction with the {@link org.apache.juneau.rest.converters.Queryable} converter, although implementations can process the query parameters themselves if they wish to do so by using the {@link org.apache.juneau.rest.RequestQuery#getSearchArgs()} method to retrieve the arguments and process the data themselves.
An example of this widget can be found in the PetStoreResource
in the examples that provides
search/view/sort capabilities against the collection of POJOs:
It renders the following popup-box:
Tooltips are provided by hovering over the field names.
When submitted, the form submits a GET request against the current URI with special GET search API query parameters.
(e.g.
{@link org.apache.juneau.rest.widget.ThemeMenuItem} is a predefined Widget that returns back a list of hyperlinks for rendering the contents of a page in the various default styles.
The variable it resolves is
An example of this widget can be found in the PetStoreResource
in the examples that provides
a drop-down menu item for rendering all other supported content types in plain text:
{@link org.apache.juneau.rest.widget.PoweredByJuneau} is a predefined Widget that places a powered-by-Juneau message on a page.
The variable it resolves is
It produces a simple Apache Juneau icon floating on the right.
Typically it's used in the footer of the page, as shown below in the AddressBookResource
from the examples:
It renders the following image:
{@link org.apache.juneau.rest.widget.Tooltip} is a predefined template for adding tooltips to HTML5 bean constructs, typically in menu item widgets.
The following examples shows how tooltips can be added to a menu item widget.
The HTML views of POJOs can somewhat be considered a rudimentary User Interface. In reality, a better term for them would be a Developer Interface as they're meant to be used primarily by developers and not end users. Despite that distinction, it is possible to 'brand' the HTML page to whatever you desire.
The sample root page below includes some default branding for Juneau and Apache:
http://localhost:10000/helloWorld
The Juneau REST framework does not provide specific branding support (i.e. there is no concept of a brand icon). Instead, it just uses the existing open-ended API for defining branding via annotations on your REST classes.
The default annotation values use {@link org.apache.juneau.config.vars.ConfigVar $C} variables to pull in values from an optional external configuration file such as the one shown below:
The take-away here is that the "User Interface" is open-ended, lets you define pretty much anything you want through arbitrary HTML, and allows you either hardcode your interface inside annotations or pull them in via string variables from other places such as external config files.
The sample root page renders in the default "devops" look-and-feel:
http://localhost:10000
The sample root page provides a dropdown widget to try out the other default look-and-feels:
For example, the "light" look-and-feel:
http://localhost:10000/?stylesheet=styles%2Flight.css
And the "dark" look-and-feel:
http://localhost:10000/?stylesheet=styles%2Fdark.css
The stylesheet URL is controlled by the {@link org.apache.juneau.rest.annotation.HtmlDoc#stylesheet() @HtmlDoc(stylesheet)} annotation. The {@link org.apache.juneau.rest.BasicRestServlet} class defines the stylesheet served up as a static file:
The org/apache/juneau/rest/styles/devops.css
.
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 stylesheet
attribute in the request
URL.
The example above show this in use.
In case you're curious about how the menu item works, it's defined via a widget:
The
The following annotations are provided for specifying default header values for requests and responses:
Default headers can also be specified programmatically by overriding the following methods:
The {@link org.apache.juneau.rest.RestContext#REST_logger} property allows you to configure logging for your resource. The interface is shown below:
The {@link org.apache.juneau.rest.RestLogger#logObjects(Level,String,Object[]) logObjects()} method is particularly useful because it allows you to pass in POJOs as arguments that serialized using {@link org.apache.juneau.json.SimpleJsonSerializer#DEFAULT_READABLE}, but only if the message is actually logged.
logger.logObjects(
By default, the Juneau framework uses the built-in Java Logging API for logging. But you can define your own implementation to use any framework you wish.
The {@link org.apache.juneau.rest.RestLogger} instance is accessible via the following:
In addition, the logger can be accessed by passing it as a parameter to your REST java method:
If your resource extends from {@link org.apache.juneau.rest.RestServlet}, you can also use and override the following methods:
By default, a 200 (OK) status is automatically set as the HTTP status when a Java method executes successfully.
Other status codes can be generated by throwing a {@link org.apache.juneau.rest.RestException} with a specific HTTP status code, or calling {@link javax.servlet.http.HttpServletResponse#setStatus(int)}.
Non-OK (200) status codes are automatically triggered by the following conditions:
Unauthorized | A {@link org.apache.juneau.rest.RestGuard guard} prevented the method from being executed | |
Not Found | No matching path patterns were found on any method | |
Method Not Implemented | A path pattern matched, but no Java methods were found for the HTTP method | |
Not Acceptable |
A path pattern matched, but no Java methods were found with a matching serializer for the
|
|
Precondition Failed | A path pattern matched, but no Java methods were found that were not rejected by {@link org.apache.juneau.rest.RestMatcher matchers} | |
Unsupported Media Type |
A path pattern matched, but no Java methods were found with a matching parser for the
|
|
Internal Server Error | The Java method threw an exception other than {@link org.apache.juneau.rest.RestException} |
Through the use of the built-in
For example, the URL
To support overloaded methods, the {@link org.apache.juneau.rest.annotation.RestResource#allowedMethodParams() @RestResource(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 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 {@link org.apache.juneau.rest.annotation.RestResource#allowedMethodParams() @RestResource(allowedMethodParams)} setting. |
&Header-Name=headerValue |
Specify a header value as a GET parameter.
Must be enabled via {@link org.apache.juneau.rest.annotation.RestResource#allowHeaderParams() @RestResource(allowHeaderParams)} setting. |
&body=X |
Pass in the HTTP body content on PUT and POST methods as a UON-encoded GET parameter.
Must be enabled via {@link org.apache.juneau.rest.annotation.RestResource#allowBodyParam() @RestResource(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 {@link org.apache.juneau.rest.mock.MockRest} class is a simple yet powerful interface for creating serverless unit tests for your REST interfaces.
The following shows a self-encapsulated standalone JUnit testcase that tests the functionality of a simple REST interface.
The API consists of the following classes:
The concept of the design is simple. The {@link org.apache.juneau.rest.mock.MockRest} class is used to create instances of {@link org.apache.juneau.rest.mock.MockServletRequest} and {@link org.apache.juneau.rest.mock.MockServletResponse} which are passed directly to the call handler on the resource class {@link org.apache.juneau.rest.RestCallHandler#service(HttpServletRequest,HttpServletResponse)}.
Breaking apart the fluent method call above will help you understand how this works.
The {@link org.apache.juneau.rest.mock.MockRest} class provides the following methods for creating requests:
The {@link org.apache.juneau.rest.mock.MockServletRequest} class provides default implementations for all the methods defined on the {@link javax.servlet.http.HttpServletRequest} in addition to many convenience methods.
The following fluent convenience methods are provided for setting common Accept
and Content-Type
headers.
The following fluent convenience methods are provided for building up your request.
Fluent setters are provided for all common request headers:
The {@link org.apache.juneau.rest.mock.MockServletResponse} class provides default implementations for all the methods defined on the {@link javax.servlet.http.HttpServletResponse} in addition to many convenience methods.
The {@link org.apache.juneau.rest.mock.MockRest} object can also be used with the {@link org.apache.juneau.rest.client.RestClient} class to perform serverless unit testing through the client API of REST resources. This can be useful for testing of interface proxies against REST interfaces (described later).
The example above can be rewritten to use a mock as follows:
The {@link org.apache.juneau.rest.client.RestClientBuilder#mockHttpConnection(MockHttpConnection)} method allows you to pass in a mocked
interface for creating HTTP requests through the client interface.
The method creates a specialized HttpClientConnectionManager
for handling requests by taking information on the
client-side request and populating the {@link org.apache.juneau.rest.mock.MockServletRequest} and {@link org.apache.juneau.rest.mock.MockServletResponse} objects
directly without involving any sockets.
The Juneau REST server API is compatible with dependency injection frameworks such as Spring.
The important class is the {@link org.apache.juneau.rest.RestResourceResolver} class which is used to resolve child servlet/resource implementation classes inside parent contexts. In other words, it's used for resolving {@link org.apache.juneau.rest.annotation.RestResource#children() @RestResource(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 Root
resource with a constructor that takes in our rest resource resolver and
sets it on the config object during initialization.
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.
juneau-rest-server-jaxrs-{@property juneauVersion}.jar
org.apache.juneau.rest.server_{@property juneauVersion}.jar
The juneau-rest-server-jaxrs
library provides an implementation of a MessageBodyReader
and MessageBodyWriter
to allow any of the Juneau serializers and parsers to be used in a
JAX/RS environment.
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 {@link org.apache.juneau.rest.RestServlet} class directly.
What you can do with the Juneau JAX-RS provider classes:
What you can't do with the Juneau JAX-RS provider classes:
The Juneau JAX-RS provider API consists of the following classes:
MessageBodyReader
and MessageBodyWriter
interfaces.
BaseProvider
to specify the serializers/parsers associated with a provider, and optionally filters and properties to
apply to those serializers and parsers.
For the most part, when using these components, you'll either use the existing
juneau-rest-server-springboot-{@property juneauVersion}.jar
org.apache.juneau.rest.server.springboot_{@property juneauVersion}.jar
The juneau-rest-server-springboot
library provides classes to make it easy to integrate
Juneau REST resources with Spring and Spring Boot.
The Juneau integration component for Spring Boot consists of the following classes:
A typical Spring Boot application can use the {@link org.apache.juneau.rest.springboot.JuneauRestInitializer} to find and register Juneau REST servlets via the {@link org.apache.juneau.rest.springboot.annotation.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 {@link org.apache.juneau.rest.springboot.SpringRestResourceResolver} which allows for child resources to be defined as injectable Spring beans.
The {@doc 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.
juneau-rest-client-{@property juneauVersion}.jar
org.apache.juneau.rest.client_{@property juneauVersion}.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.
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:
httpclient-4.5.jar
httpcore-4.4.1.jar
httpmime-4.5.jar
The juneau-rest-client
library can also be used to define interface proxies against 3rd-party REST interfaces.
This is an extremely powerful feature that allows you to quickly define easy-to-use interfaces against
virtually any REST interface.
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 {@link org.apache.juneau.rest.client.remote.RemoteResource @RemoteResource} annotation is used on your interface class to identify it as a REST proxy interface.
The
The {@link org.apache.juneau.rest.client.remote.RemoteResource#path @RemoteResource(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.getRemoteResource(PetStore.
When a relative path is specified, it's relative to the root-url defined on the RestClient
used to instantiate the interface.
RestClient client = RestClient.
When no path is specified, the root-url defined on the RestClient
is used.
RestClient client = RestClient.
The {@link org.apache.juneau.rest.client.remote.RemoteMethod @RemoteMethod} annotation is applied to methods
of
The HTTP method and path are mapped to a Java method using the method
and path
annotations.
The Java method name can be anything.
In such cases, method
and path
annotations are optional if you follow certain naming
conventions on your method that identify the method and path.
For example, the getPet
method below defaults to GET /pet
:
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:
RestClient
based on the Content-Type
of the response.
HttpResponse
- Returns the raw HttpResponse
returned by the inner HttpClient
.
If you're only interested in the HTTP status code of the response, you can use the {@link org.apache.juneau.rest.client.remote.RemoteMethod#returns() returns} annotation with a value of {@link org.apache.juneau.rest.client.remote.RemoteReturn#STATUS STATUS}:
If your RestClient
does not have a parser associated with it, then the value is converted directly from a String using
the rules defined in {@doc PojosConveribleToStrings}.
The {@link org.apache.juneau.http.annotation.Body @Body} annotation can be applied to arguments of
The argument can be any of the following types:
RestClient
.
Content-Type
is set to that of the Serializer
.
Content-Type
is set to Content-Type
is set to NameValuePairs
- Converted to a URL-encoded FORM post.
Content-Type
is set to HttpEntity
- Bypass Juneau serialization and pass HttpEntity directly to HttpClient.
OpenAPI schema based serialization can be used by using the {@link org.apache.juneau.oapi.OpenApiSerializer} class.
See {@doc juneau-marshall.OpenApiDetails.Serializers} for information about supported data types in OpenAPI serialization.
If your RestClient
class does not have a serializer associated with it, the body will automatically be serialized to a
string using the rules defined in {@doc PojosConveribleToStrings}.
The {@link org.apache.juneau.http.annotation.FormData @FormData} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
RestClient
({@link org.apache.juneau.oapi.OpenApiSerializer} by default) or associated via the {@link org.apache.juneau.http.annotation.FormData#serializer() @FormData(serializer)} annotation.
Multi-part arguments (i.e. those with name ==
NameValuePairs
- Converted to a URL-encoded FORM post.
Map
- Converted to key-value pairs.
CharSequence
- Used directly as am See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Query @Query} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
RestClient
({@link org.apache.juneau.oapi.OpenApiSerializer} by default) or associated via the {@link org.apache.juneau.http.annotation.Query#serializer() @Query(serializer)} annotation.
Multi-part arguments (i.e. those with name ==
NameValuePairs
- Serialized as individual query parameters.
Map
- Converted to key-value pairs.
CharSequence
- Serialized directly a query string.
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Header @Header} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
RestClient
({@link org.apache.juneau.oapi.OpenApiSerializer} by default) or associated via the {@link org.apache.juneau.http.annotation.Header#serializer() @Header(serializer)} annotation.
Multi-part arguments (i.e. those with name ==
NameValuePairs
- Serialized as individual headers.
Map
- Converted to key-value pairs.
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Path @Path} annotation can be applied to arguments of
Single-part arguments (i.e. those with name !=
RestClient
({@link org.apache.juneau.oapi.OpenApiSerializer} by default) or associated via the {@link org.apache.juneau.http.annotation.Path#serializer() @Path(serializer)} annotation.
Multi-part arguments (i.e. those with name ==
NameValuePairs
- Serialized as individual query parameters.
Map
- Converted to key-value pairs.
See the link below for information about supported data types in OpenAPI serialization.
The {@link org.apache.juneau.http.annotation.Request @Request} annotation can be applied to a type of a
PetStore store = restClient.getRemoteResource(PetStore.
The
The annotated methods must be no-arg and public. They can be named anything.
Any of the following annotations can be used on the methods:
The behavior and functionality of all of the annotations are the same as if they were used on method arguments directly. This means full support for OpenAPI serialization and validation.
Annotations on methods are inherited from parent classes and interfaces. For example, the request bean above could have defined annotations in an interface to keep them clear from the implementation:
The {@link org.apache.juneau.http.annotation.Response @Response} annotation can be applied to types returned by
The
PetStore store = restClient.getRemoteResource(PetStore.
The annotated methods must be no-arg. They can be named anything.
Any of the following annotations can be used on the methods:
The behavior and functionality of all of the annotations are the same as if they were used on method arguments directly. This means full support for OpenAPI serialization and validation.
A common coding practice is to use the same Java interface to define both your server and client side REST interfaces. The advantage to this approach is that changes that you make to your REST interface can be reflected in both places at the same time, reducing the chances for compatibility mistakes.
What makes this possible is that method-level annotations such as
The general approach is to define your {@link org.apache.juneau.rest.client.remote.RemoteResource @RemoteResource}-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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.RestClientBuilder} and override the {@link org.apache.juneau.rest.client.RestClientBuilder#createHttpClient()} method to provide an authenticated client.
The following example shows how the JazzRestClient
class provides FORM-based
authentication support.
The following example shows how the JazzRestClient
class provides OIDC authentication
support.
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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.RestCall#successPattern(String)} method can be used to look for a SUCCESS message in the output:
The {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.RestCall#getResponseAsString()} or {@link org.apache.juneau.rest.client.RestCall#getResponse(Class)}. HOWEVER, if you want to retrieve the entire text of the response from inside the match methods, use {@link org.apache.juneau.rest.client.RestCall#getCapturedResponse()} since this method will not absorb the response for those other methods.
The {@link org.apache.juneau.rest.client.RestCall} class provides various convenience pipeTo()
methods to pipe output to output streams and writers.
If you want to pipe output without any intermediate buffering, you can use the {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.RestClientBuilder#debug()} method to enable logging for HTTP requests made from the client.
Under-the-covers, this is simply a shortcut for adding the {@link org.apache.juneau.rest.client.RestCallLogger#DEFAULT}
interceptor to the client.
This causes the following output to be generated by the Java org.apache.juneau.rest.client
logger at
=== 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 Debug: true
header value to trigger logging of the request on the
server side as well.
=== 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 {@link org.apache.juneau.rest.client.RestClientBuilder#logTo(Level,Logger)} and {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.RestCallLogger} class and using the {@link org.apache.juneau.rest.client.RestCall#interceptor(RestCallInterceptor)} method.
The {@link org.apache.juneau.rest.client.RestClientBuilder#interceptors(RestCallInterceptor...)} and {@link org.apache.juneau.rest.client.RestCall#interceptor(RestCallInterceptor)} methods can be used to intercept responses during specific connection lifecycle events.
The {@link org.apache.juneau.rest.client.RestCallLogger} class is an example of an interceptor that uses the various lifecycle methods to log HTTP requests.
The {@link org.apache.juneau.rest.mock.MockRest} class is used for performing serverless unit testing of REST interfaces.
The {@link org.apache.juneau.rest.client.RestClientBuilder#mockHttpConnection(MockHttpConnection)} method is used to associate a MockRest
with
a RestClient
to allow for serverless testing of clients.
Mocked connections can also be used for serverless testing of remote resources and interfaces.
The {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.rest.client.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 {@link org.apache.juneau.BeanContext#BEAN_ignoreUnknownBeanProperties} property.
The {@link org.apache.juneau.rest.client.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-microservice-core-{@property juneauVersion}.jar
org.apache.juneau.microservice.core_{@property juneauVersion}.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:
By itself the Microservice API doesn't provided much functionality, but it does provide the basis for the {@doc juneau-microservice-jetty Jetty Microservice} described later.
The most-basic creation of a microservice from an entry-point method is shown below:
The lifecycle methods of the {@link org.apache.juneau.microservice.Microservice} class consists of the following:
A typical implementation of an app with lifecycle methods might look like the following:
If your application consists of a single microservice, you can use the {@link org.apache.juneau.microservice.Microservice#getInstance()} method from anywhere in your code:
The {@link org.apache.juneau.microservice.Microservice#startConsole()} and {@link org.apache.juneau.microservice.Microservice#stopConsole()} control the lifecycle of the console commands. Typically you'll want to control these separately from the app so that you can easily restart your application from the console without affecting the console itself.
The lifecycle methods on the {@link org.apache.juneau.microservice.Microservice} class are purposely left non-final so that subclasses can override them to provide customized behavior.
Command-line arguments can be associated with a microservice using the {@link org.apache.juneau.microservice.MicroserviceBuilder#args(String...)} method.
When specified, the arguments can be retrieved using the {@link org.apache.juneau.microservice.Microservice#getArgs()} method which provides an API for easily accessing command-line arguments using common notation:
Args a = Microservice.
Specifying the command-line arguments also makes them available through {@link org.apache.juneau.svl.vars.ArgsVar $A} SVL variables. These can be used in the configuration file and throughout various Juneau annotations.
The {@link org.apache.juneau.microservice.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:
The manifest file can be retrieved using the the {@link org.apache.juneau.microservice.Microservice#getManifest()} method which provides an API for accessing manifest file entries. This method returns an instance of {@link org.apache.juneau.utils.ManifestFile} which extends from {@link org.apache.juneau.ObjectMap} allowing you to retrieve entries as any data types supported by that class.
ManifestFile mf = Microservice.
The manifest is also used for the {@link org.apache.juneau.svl.vars.ManifestFileVar $MF} SVL variable.
The following methods can be used to define the configuration for your microservice using the powerful Config API:
If you do not specify any of this information, we attempt to resolve it through the following methods:
Main-Config
entry in the manifest file.
If no configuration file is found, and empty in-memory configuration is used.
The {@link org.apache.juneau.microservice.MicroserviceBuilder#configName(String) configName(String)} method allows you to explicitly specify the name of the external configuration file location for your microservice.
Microservice
.
By default, we try to find the file on the file system and then the classpath. If located on the file system, then the configuration is writeable and the microservice can automatically listen for and react to changes in the configuration file on the file system. If located on the classpath, then the configuration can still react to modifications made to it through the Config API, but the changes cannot be persisted since the location prevents the file from being modified.
The {@link org.apache.juneau.microservice.MicroserviceBuilder#configStore(ConfigStore) configStore(ConfigStore)} method can be used to explicitly specify a configuration store. This can include your own custom configuration store, such as one that's implemented in a relational database.
Microservice
.
The {@link org.apache.juneau.microservice.MicroserviceBuilder#config(Config) config(Config)} method can be used to explicitly specify a {@link org.apache.juneau.config.Config} file as the microservice configuration. When this method is used, the above two methods are bypassed entirely.
Config config =
Once the configuration is resolved, it is made as the system default configuration available through the {@link org.apache.juneau.config.Config#getSystemDefault()}.
This in turn allows it to be used by REST resources that reference the system default configuration via the
The {@link org.apache.juneau.microservice.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 {@link org.apache.juneau.microservice.Microservice#onConfigChange(ConfigEvents)} or implementing a listener and using the {@link org.apache.juneau.microservice.MicroserviceListener#onConfigChange(Microservice,ConfigEvents)} methods. These will be described in detail later.
As a convenience, the SystemProperties
section of your configuration file can be used to define system
properties to set during initialization of your microservice:
The Microservice API incorporates the {@doc juneau-svl Simple Variable Resolver} 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 {@doc juneau-microservice-jetty}
By default, support for the following variables are provided:
$S{key[,default]}
- {@link org.apache.juneau.svl.vars.SystemPropertiesVar}
$E{key[,default]}
- {@link org.apache.juneau.svl.vars.EnvVariablesVar}
$A{key[,default]}
- {@link org.apache.juneau.svl.vars.ArgsVar}
$C{key[,default]}
- {@link org.apache.juneau.config.vars.ConfigVar}
$MF{key[,default]}
- {@link org.apache.juneau.svl.vars.ManifestFileVar}
$IF{arg,then[,else]}
- {@link org.apache.juneau.svl.vars.IfVar}
$SW{arg,pattern1:then1[,pattern2:then2...]}
- {@link org.apache.juneau.svl.vars.SwitchVar}
$CO{arg[,arg2...]}
- {@link org.apache.juneau.svl.vars.CoalesceVar}
$PM{arg,pattern}
- {@link org.apache.juneau.svl.vars.PatternMatchVar}
$UC{arg}
- {@link org.apache.juneau.svl.vars.UpperCaseVar}
$LC{arg}
- {@link org.apache.juneau.svl.vars.LowerCaseVar}
$NE{arg}
- {@link org.apache.juneau.svl.vars.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 {@link org.apache.juneau.microservice.MicroserviceBuilder#consoleCommands(ConsoleCommand...) consoleCommands(ConsoleCommand...)} builder method.
The API for defining console commands is shown below:
By default, the console input and output are taken from {@link java.lang.System.in} and {@link java.lang.System.out}. These can be overridden using the {@link org.apache.juneau.microservice.MicroserviceBuilder#console(Scanner,PrintWriter) 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 {@link org.apache.juneau.microservice.MicroserviceBuilder#logger(Logger)} method.
In addition to configuring the built-in Java logging framework, the following convenience methods are also provided on the {@link org.apache.juneau.microservice.Microservice} class for logging.
As mentioned previously, the lifecycle methods for the {@link org.apache.juneau.microservice.Microservice} class are explicitly defined as non-final so that they can be overridden by subclasses.
In addition to this support, an interface for defining event listeners for your microservice:
This listener API can be used for listening for and reacting to configuration changes on the file system.
Note that the {@link org.apache.juneau.microservice.Microservice#onConfigChange(ConfigEvents)} method can also be overridden to react to configuration changes as well:
juneau-microservice-jetty-{@property juneauVersion}.jar
org.apache.juneau.microservice.jetty_{@property juneauVersion}.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 {@doc juneau-microservice-core Core Microservice} classes to produce easy-to-create and easy-to-use microservices in a standard Java 1.8+ environment.
The juneau-microservice-jetty
library consists of the following classes:
The most-basic creation of a Jetty microservice from an entry-point method is shown below:
To review, the {@link org.apache.juneau.microservice.Microservice} class contains the following lifecycle methods:
The {@link org.apache.juneau.microservice.jetty.JettyMicroservice} class which extends from {@link org.apache.juneau.microservice.Microservice} provides the following additional lifecycle methods:
The additional lifecycle methods are typically not called directly, but are exposed to allow subclasses to provide customized behavior for these events. For this reason, these methods are left as non-final so that they can be overridden.
A typical implementation of an app with lifecycle methods might look like the following:
Similar to {@link org.apache.juneau.microservice.Microservice#getInstance()}, the {@link org.apache.juneau.microservice.jetty.JettyMicroservice#getInstance()} also allows easy access to the microservice:
This section describes how to define a top-level REST resource page and deploy it in our microservice. The example is a router page that serves as a jumping off page to child resources.
When deployed, it looks like this in a browser:
http://localhost:10000
If you click the
http://localhost:10000/helloWorld
...which is generated by this class...
The most-common case for deploying the top-level resource is to use the {@link org.apache.juneau.microservice.jetty.JettyMicroserviceBuilder#servlet(Class)} method:
However, there are multiple ways of deploying top-level resources:
jetty.xml
file.
The following predefined resource classes are also provided for easy inclusion into your microservice:
In {@doc 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 {@link org.apache.juneau.config.vars.ConfigVar $C} variable in annotations.
For example, the {@link org.apache.juneau.rest.BasicRestConfig} interface that defines the annotations that control the look-and-feel of
classes that extend from {@link org.apache.juneau.rest.BasicRestServlet} use several $C
variables to externalize values:
These values in turn are pulled from the external configuration file shown below.
Note that the configuration file can also contain $C
variables.
Configuration files can also be accessed programmatically. There are 3 primary ways of getting access to the config file:
Any {@doc DefaultRestSvlVariables initialization-time variables} can be used.
Any {@doc DefaultRestSvlVariables initialization-time variables} can be used.
Additional user-defined variables at the servlet level can be defined by adding a {@link org.apache.juneau.rest.annotation.HookEvent#INIT} hook method and using the {@link org.apache.juneau.rest.RestContextBuilder#vars(Class...)} method.
Any {@doc DefaultRestSvlVariables initialization-time or request-time variables} can be used.
Additional user-defined variables can be defined at this level by overriding the {@link org.apache.juneau.rest.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 jetty.xml
file which can be modified to suite any needs.
The jetty.xml
can be located in either the
Jetty-Config
value in the MANIFEST.MF
file.
SVL variables in the jetty.xml
file are automatically resolved by the microservice.
This allows you to reference values in your configuration file from the jetty.xml
file.
The HTTP port used is controlled via the following:
JettyMicroservice
.
The first available port is then made available through the system property jetty.xml
file.
The {@link org.apache.juneau.microservice.jetty.JettyMicroserviceBuilder#jettyServerFactory(JettyServerFactory)} method is also provided to use your own customized Jetty server.
The Microservice project contains a files/htdocs
folder with predefined stylesheets and
images.
These files can be used to tailor the look-and-feel of your microservice.
http://localhost:10000/helloWorld
The REST configuration section of your microservice configuration file can be used to tailor the header and footer on the pages:
The {@link org.apache.juneau.rest.BasicRestConfig} interface (which defines the default settings for {@link org.apache.juneau.rest.BasicRestServlet} pulls in this information using {@link org.apache.juneau.config.vars.ConfigVar $C} and {@link org.apache.juneau.rest.vars.UrlVar $U} variables:
Note that the files/htdocs
directory is mapped to staticFiles
setting. This allows those files to be served up through the servlet through the URL
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 devops.css
is included in the jar as a default spreadsheet.
If you're testing out changes in the theme stylesheets, you may want to set the following system property that prevents caching of those files so that you don't need to restart the microservice each time a change is made:
This example shows how the {@link org.apache.juneau.microservice.jetty.JettyMicroservice} class
can be extended to implement lifecycle listener methods or override existing methods.
We'll create a new class
Optionally, you can extend the {@link org.apache.juneau.microservice.jetty.JettyMicroserviceBuilder} class as well:
my-jetty-microservice-{@property juneauVersion}.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.
my-jetty-microservice-{@property juneauVersion}.zip
file from the downloads page
(located in the binaries) and import it into your workspace as an existing project:
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 exit
to shut it down.
The
The easiest way to build your microservice is to run the following from the project root.
mvn clean install
Your target
directory should now contain the following files:
my-jetty-microservice-1.0.jar
my-jetty-microservice.cfg
To start from a command line, run the following command from inside your target
directory:
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-{@property juneauVersion}.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 {@link org.apache.juneau.microservice.Microservice} API for our application but instead using the existing Spring Boot API.
Follow these instructions to create a new template project in Eclipse.
my-springboot-microservice-{@property juneauVersion}.zip
file from the downloads page
(located in the binaries) and import it into your workspace as an existing project:
The important elements in this project are:
At this point, you're ready to start the microservice from your workspace.
The
Go to Run -> Run Configurations -> Java Application -> my-springboot-microservice and click Run. In your console view, you should see the following output:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.1.RELEASE) ... INFO: Tomcat started on port(s): 8080 (http) with context path '' Dec 21, 2012 12:30:00 AM org.springframework.boot.StartupInfoLogger logStarted INFO: Started App in 1.999 seconds (JVM running for 2.999)
Now open your browser and point to
http://localhost:5000
The
The easiest way to build your microservice is to run the following from the project root.
mvn clean install
Your target
directory should now contain the following files:
my-springboot-microservice-1.0.jar
To start from a command line, run the following command from inside your target
directory:
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-{@property juneauVersion}.zip
The juneau-examples-core
project contains various code examples for using the core APIs.
Follow these instructions to import the Juneau project into Eclipse.
juneau-examples-core-{@property juneauVersion}.zip
file from the downloads page
(located in the binaries) and import it into your workspace as an existing project:
The following shows the core examples provided:
juneau-examples-rest-{@property juneauVersion}.jar
org.apache.juneau.examples.rest_{@property juneauVersion}.jar
The juneau-examples-rest
project includes various examples of REST resources written
using Juneau.
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:
RootResources
Pointing a browser to the resource shows the following:
http://localhost:10000
The
The
The
Child resources must be annotated with the {@link org.apache.juneau.rest.annotation.RestResource#path() @RestResource(path)} annotation to identify the subpath of the child. Children CAN extend from {@link org.apache.juneau.rest.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 {@link org.apache.juneau.rest.RestServlet}.
We are however implementing {@link org.apache.juneau.rest.BasicRestConfig} which is a no-op
interface that defines a default
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 PetStore
application is an functional application meant to demonstrate the following:
When you click the
http://localhost:10000/petstore
The contents of this page is primarily defined via annotations defined on the
The inner contents of the page are generated from this method which is used to define a jumping-off page for the application:
Note how we used the
The {@link org.apache.juneau.rest.helper.ResourceDescriptions} class used above is a convenience class for creating hyperlinks to child resources.
The application itself is defined in 3 packages:
org.apache.juneau.rest.examples.rest.petstore
org.apache.juneau.rest.examples.rest.petstore.dto
org.apache.juneau.rest.examples.rest.petstore.rest
We also define some static files in the org.apache.juneau.rest.examples.rest.petstore
package:
The
The DTOs are simply beans that combine both JPA and Juneau bean annotations:
The beans are found by JPA by adding them to the JPA persistence file.
The Petstore service is instantiated in our REST interface using a hook. Note that a real-world scenario would likely use some other means such as injection.
The Petstore database is empty by default.
The
http://localhost:10000/petstore/init
You can try loading the Petstore database using direct JPA or via REST calls through a client-side proxy.
The initialize page is rendered using the following methods in our
The direct initialization uses direct JPA to store beans in the database.
The following code in
The REST initialization uses a REST proxy interface to delete and store values in the database:
The
Note that this is the same interface used to define our server-side REST implementation! The annotations defined on the method parameters used for client-side proxies are also inherited by and used for our server-side implementation class.
The advantage to using a common interface for both your server-side and client-side APIs is that you have less of a chance of a mismatch between the server and client side definitions.
Now that we've initialized the contents of our database, we can start exploring the REST interface.
We can start by click the
http://localhost:10000/petstore/pet
Clicking on one of the ID links takes you to a details page:
http://localhost:10000/petstore/pet/1
You'll notice the details page shows bpx=
annotation on the
The hyperlinks and special rendering for
The
http://localhost:10000/petstore/pet/1
For example, selecting
http://localhost:10000/petstore/pet?plainText=true&Accept=application%2Fjson%2Bsimple
Note that we're using the convenience feature for specifying an Accept
header via a query parameter.
The
http://localhost:10000/petstore/pet/1
For example, selecting
http://localhost:10000/petstore/pet?stylesheet=htdocs%2Fthemes%2Flight.css
Both the
The implementation of a menu item contains methods for retrieving the label and HTML5 content of the menu item.
The
http://localhost:10000/petstore/pet
The converter will take the POJOs to be serialized and filter them based on the provided query/view/sort/paging attributes:
http://localhost:10000/petstore/pet?s=name%3DHoppy*&v=species%2Cname&o=name&p=0&l=20
The
http://localhost:10000/petstore/pet
Both the
The
http://localhost:10000/petstore/pet?method=OPTIONS
Since we've defined tags on our annotations, the pet-related operations are all grouped under the
Information for all HTTP parts is automatically generated:
The schema models for POJO models is available in the
Auto-generated examples are available for all supported languages:
For example,
Examples can be derived in a number of ways. In our case, we've defined a static method on our
Similar functionality exists for request bodies as well:
At the bottom of the page is a listing of the POJO models in the app:
The
http://localhost:10000/petstore/photos
The
The contents of the home page simply renders our collection of photo beans:
Clicking on one of the hyperlinks renders the stored image for us:
http://localhost:10000/petstore/photos/cat
The method for retrieving images simply returns a
The
Likewise, the body of requests can also be instances of
The
A custom menu item is provided for uploading new images:
http://localhost:10000/petstore/photos
The menu item is defined as a
The menu item then submits multi-part form posts to the following method:
The
http://localhost:10000/petstore/sql
For example, viewing all of the
http://localhost:10000/petstore/sql?sql=select+*+from+PetstorePet
The
The connection information is pulled from the
The query entry page is rendered using HTML5 beans:
The form then submits its results to the following method using a form input bean:
Note that we could have also used
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
The resource consists of a pre-initialized {@link org.apache.juneau.dto.jsonschema.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 {@link org.apache.juneau.microservice.resources.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 {@link org.apache.juneau.config.Config} class is a serializable POJO, which makes the resource relatively straightforward to implement.
The {@link org.apache.juneau.microservice.resources.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-{@property juneauVersion}.zip
The juneau-examples-rest-jetty
project includes everything you need create a Samples REST
microservice in an Eclipse workspace and build it as an executable jar.
Follow these instructions to import the REST examples project using Jetty into Eclipse.
juneau-examples-rest-jetty-{@property juneauVersion}.zip
file from the downloads page
(located in the binaries) and import it into your workspace as an existing project:
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 exit
to shut it down.
The
The easiest way to build the microservice is to run the following from the project root.
mvn clean install
Your target
directory should now contain the following files:
juneau-examples-rest-jetty-1.0.jar
juneau-examples-rest-jetty.cfg
To start from a command line, run the following command from inside your target
directory:
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-{@property juneauVersion}.zip
The juneau-examples-rest-springboot
library contains the same examples as juneau-examples-rest
but also includes the following:
spring-boot-starter-parent
that allows you to build a fully-shaded executable jar.
Follow these instructions to import the REST examples project using Spring Boot into Eclipse.
juneau-examples-rest-springboot-{@property juneauVersion}.zip
file from the downloads page
(located in the binaries) and import it into your workspace as an existing project:
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-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 target
directory should now contain the following files:
juneau-examples-rest-springboot-1.0.jar
To start from a command line, run the following command from inside your target
directory:
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-SNAPSHOT.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 JsonParser.
).
As long as the Class
object passed into this method is not constructed from user-generated input,
it should be free from demarshalling vulnerabilities.
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 {@link org.apache.juneau.jso.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
application/x-java-serialized-object
input through REST servlets.
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:
7.0.1
, no known security vulnerabilities exist that affect Juneau at this time.
Care must be used when defining new {@link org.apache.juneau.svl.Var Vars} using the SVL API since mistakes could potentially expose system properties, environment variables, or even file system files.
For recap, the SVL support allows you to embed variables of the form
An example of a potential security hole is shown below that could potentially expose any file on a file system through a REST request:
This code is simply echoing the value of the foo
query parameter.
Now say for example that a bad actor passes in the query string $F
variable allows you to resolve the contents of files using SVL, and is provided
by default using the built-in variable resolver returned by the RestRequest
object.
You've potentially just exposed the contents of that file through your REST interface.
In reality, the above security hole does not exist because of the following restrictions:
Vars
have two methods {@link org.apache.juneau.svl.Var#allowNested()} and
{@link org.apache.juneau.svl.Var#allowRecurse()} that can be overridden to prevent recursive processing
of string variables. These are both $R
variable, so the $F
variable in the result will never get processed and instead be treated as plain text.
$F
variable only allows you to retrieve files within the JVM starting directory.
Even though the built-in Juneau variables are safe, special care is needed when defining your own custom variables. If your variable resolves user input in any way, it's HIGHLY recommended that you override the {@link org.apache.juneau.svl.Var#allowNested()} and {@link org.apache.juneau.svl.Var#allowRecurse()} methods to prevent recursive handling of variables.
Denial of service attacks can be alleviated through the {@link org.apache.juneau.rest.annotation.RestResource#maxInput() maxInput()} setting. Arbitrarily-large input will trigger an exception before causing out-of-memory errors. The default value for this setting is 100MB.
Since the parsers do not use intermediate DOMs and instead parse directly into Java objects, deeply nested data structures will almost always trigger stack overflow errors long before memory consumption becomes an issue. However, this is NOT true of the RDF parsers that use an intermediate DOM. If parsing RDF, you may want to consider lowering the max-input value above.
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.
Transform
class, which is associated with the BeanContext
class (and thus the Serializer and Parser classes too) through the BeanContext.addTransforms(Class[])
method.Transform
:
Cast
and BeanFilter
APIs which were considerably more complicated and puts them under a common API.
_class
attributes in parsable output.BeanMap
API.BeanMap
API code.
Reflection is used to determine the class types of property values on beans.
This information is now cached and persisted so that the reflection API calls to determine class types are only performed the first time a bean type is encountered.
RestServlets
.Accept-Encoding: gzip
) and charsets (e.g Accept-Charset: SJIS
) on both incoming and outgoing data.
It's all transparent from a developers perspective.
The developer simply working with POJOs, and all details about content types, encoding, charsets, and so forth are handled by the framework.
OPTIONS
pages for resources.
RestServlet
.
Enum
.
MyBeanList extends LinkedList<MyBean>
).
Juno 5.0.0.1 is a moderate update.
Juno 5.0.0.2 is a minor update.
org.apache.juneau.json
).
Enum
keys, and parsing Enum
strings.ObjectList.toXArray()
methods with a new elements(Class<T> type)
method that's more efficient and avoids creating an unnecessary array.
Juno 5.0.0.3 is a minor update.
Juno 5.0.0.4 is a minor update.
BeanContext.convertToType(Object,Class)
to be able to convert Strings
to classes with
fromString(String)
/valueOf(String)
static methods or T(String)
constructors.
Juno 5.0.0.5 is a major update.
@RestChild
annotation for identifying child resources.traversable
and filterable
attributes added to {@link org.apache.juneau.rest.annotation.RestMethod @RestMethod} annotation.PojoResource
and FilteredRestResource
classes.
resourceUri
attributed added to RdfXmlSerializer
.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.
@Xml.namespaces
annotation.@Xml.nsUri
annotation.@Xml.valAttr
annotation.SerializerContext
ParserContext
@BeanProperty(hidden)
so that they don't get serialized.
ClassType
{@link org.apache.juneau.ClassMeta} API.@Bean.filter
and @BeanProperty.filter
annotations.BeanContext.addTransforms(Class[])
.
@BeanProperty.beanUri
and @BeanProperty.id
annotations.rdf:resource
attributes.
RestResponse.setProperty(String,Object)
method.@RestResource(filters)
- Associate post-formatting filters on a resource level.@RestMethod(filters)
- Associate post-formatting filters on a method level.@Attr
- A parameter or URL variable value as a parsed POJO.@Param
- A query parameter value as a parsed POJO.@PathRemainder
- The remainder after a URL pattern match as a String.@Header
- An HTTP header value as a parsed POJO.@Content
- The HTTP content as a parsed POJO.Juno 5.0.0.8 is a minor update.
Juno 5.0.0.9 is a moderate update.
ObjectMaps
directly to beans.OPTIONS
pages.Readers
and InputStreams
can be specified on @Content
annotated parameters.@HasParam
annotation.Accept
headers to serializers.Juno 5.0.0.10 is a minor update.
RestServletProperties
class that defines all the class-level properties that can be set on the servlet.RestServlet.setProperty(String,Object)
method.RestServletProperties.REST_useStackTraceHashes
property to prevent the same stack trace from being logged multiple times.RestServletProperties.REST_renderResponseStackTraces
property for preventing stack traces in responses for security reasons.RestServlet.onError(HttpServletRequest,HttpServletResponse,RestException,boolean)
and RestServlet.onSuccess(RestRequest,RestResponse,long)
methods for plugging in your own logging and peformance monitoring.RestServlet.getInitParams()
method, since it's now redundant with RestServlet.getProperties()
.Juno 5.0.0.11 is a moderate update.
UrlEncodingRestSerializer
and UrlEncodingRestParser
classes.Accept
and Content-Type
RestServlet.renderError(HttpServletRequest,HttpServletResponse,RestException)
method to allow customized handling of response errors.
Juno 5.0.0.12 is a minor update.
BeanProperty.method
annotation, since it's now unnecessary.
PlainTextRestSerializer
class for serializing Readers
and InputStreams
can now be passed in as @Content
parameters if you need direct access to the HTTP body content without involving the parsers.?debug
parameter.Juno 5.0.0.13 is a minor update.
SerializerContext.SERIALIZER_uriContext
and SerializerContext.SERIALIZER_uriAuthority
serializer properties for specifying values for relative URIs.
java.net.URI
or java.net.URL
.
BeanProperty#uri
annotation to BeanProperty#beanUri
to make it clear that this property represents the URI of the bean itself instead of an arbitrary property containing a URI.
BeanProperty#id
annotation.
SerializerContext.SERIALIZER_uriContext
property set by default to web app context root.SerializerContext.SERIALIZER_uriAuthority
property set by default to the request scheme+hostname+port.Accept-Charset
header in Chrome that prevented HTML output from rendering correctly in that browser.Accept-Charset
handling should now be fully W3C compliant.
Juno 5.0.0.14 is a major update.
The biggest change is that the RestSerializer
, RestParser
, RestSerializerGroup
, and RestParserGroup
classes have been eliminated entirely.
Instead, the existing {@link org.apache.juneau.serializer.Serializer}, {@link org.apache.juneau.parser.Parser}, {@link org.apache.juneau.serializer.SerializerGroup}, and {@link org.apache.juneau.parser.ParserGroup} classes of the core API have been augmented to replace them.
Adoptions will be required if you have previously used these classes.
org.apache.juneau.serializer
package.
Accept-Content
headers.org.apache.juneau.parser
package.
org.apache.juneau.transform
package.
ObjectFilter
).org.apache.juneau.encoders
package.
Accept-Encoding
header values.org.apache.juneau.plaintext
package.
org.apache.juneau.jso
package.
application/x-java-serialized-object
content.org.apache.juneau.soap
package.
text/xml+soap
content.JsonMap
and JsonList
changed to {@link org.apache.juneau.ObjectMap} and {@link org.apache.juneau.ObjectList} to better reflect that they're not limited to just JSON support.
PojoSwap
to {@link org.apache.juneau.utils.PojoQuery} to not confuse it with the new Filter API.
org.apache.juneau.rest.serializers
and org.apache.juneau.rest.parsers
packages.
RestCmdLine
(since it's essentially redundant with CURL).
Juno 5.0.0.15 is a moderate update.
@Produces
annotation in place of ISerializer.getMediaTypes()
for specifying what media types a serializer produces.@Consumes
annotation in place of IParser.getMediaTypes()
for specifying what media types a parser consumes.Juno 5.0.0.16 is a minor update.
@Properties
REST method parameter annotation that can be used to get the runtime properties map through a parameter instead of through {@link org.apache.juneau.rest.RestResponse}.
Juno 5.0.0.17 is a minor update.
IOutputStreamSerializer.serialize()
and IInputStreamParser.parse()
.
Juno 5.0.0.18 is a moderate update.
The biggest change is the introduction of the {@link org.apache.juneau.jena.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 RdfParser
class in the future, so the serializer logic may need to be tweaked to allow POJOs to be reconstituted correctly in the parser.
The RdfXmlSerializer
class should be considered deprecated for now.
However, I'm keeping it around, since it's considerably faster and uses far less memory than the Jena-based serializer since it serializes directly from POJOs to RDF/XML.
It may or may not be removed in the future depending on demand.
Juno 5.0.0.19 is a minor update.
RestServlet.onPreCall(RestRequest)
RestServlet.onPostCall(RestRequest,RestResponse)
SerializerContext.SERIALIZER_trimNullProperties
.Juno 5.0.0.20 is a major update.
XmlSerializerContext.XML_autoDetectNamespaces
default changed to XmlSerializerContext.XML_namespaces
annotation.RestMethod.filters()
annotation for defining POJO filters at the method level.
RestMethod.serializersInherit()
and RestMethod.parsersInherit()
annotations for controlling how serializers and parsers (and associated filters and properties) are inherited from the class.addSerializers
and addParsers
annotations.
RestServletJenaDefault
servlet that includes serialization/parsing support for all Jena-based serializers and parsers.
DefaultJenaProvider
JAX-RS provider that includes serialization/parsing support for all Jena-based serializers and parsers.
RestServletChild
class.RestServlet.createConfigFactory()
RestServlet.createSerializers()
RestServlet.createParsers()
getBeanContext()
/ getSerializers()
/ getParsers()
methods.
RestCall.setDateHeader(String,Object)
method for setting ISO8601 datetime headers.
Juno 5.0.0.21 is a minor update.
HtmlDocSerializerContext.HTMLDOC_navlinks
annotation for addint links to HTML page views.
HtmlDocSerializerContext
for clarity.
RestServlet.addDefaultProperties(ObjectMap,RestRequest)
method for programatically adding properties to the property map per request.
SerializerContext.SERIALIZER_uriAuthority
and SerializerContext.SERIALIZER_uriContext
properties were previously available.
RestServletProperties.REST_servletPath
RestServletProperties.REST_pathInfo
RestServletProperties.REST_method
@Attr
are now automatically URL-decoded.
Juno 5.0.0.22 is a minor update.
@Property .nls()
annotation for specifying localized property values.AddressBookResource
class for an example.
&Content
query parameter was not always parsed correctly.Juno 5.0.0.23 is a minor update.
@Xml
annotation was not being inherited by inner classes.
Juno 5.0.0.24 is a major update.
AtomFeedResource
class added to sample war.
XmlFormat.CONTENT
enum value.XmlContentHandler
class and @Xml.contentHandler
annotation.@Xml .valAttr
annotation since it's now redundant with @Xml (format=CONTENT )
.
Serializer.serialize(Object,Object,SerializerContext)
method.
Juno 5.0.0.25 is a minor update.
SqlQueryResource
class in the sample war for demonstrating the ResultSetList
DTO.
Servlet.init()
so that getProperties()
can be called during servlet initialization.
@Property .type
annotation with support for using system properties as resource properties.
Juno 5.0.0.26 is a minor update.
ChildResourceDescriptions
bean for automatically generating the contents of router resource pages.
@RestMethod .pattern()
to {@link org.apache.juneau.rest.annotation.RestMethod#path() @RestMethod(path)} for naming consistency.
Juno 5.0.0.27 is a moderate update.
OPTIONS
pages through new method ResourceOptions.getChildren()
.
Juno 5.0.0.28 is a moderate update.
OutOfMemoryError
and performance issue caused by incorrect caching of class metadata.
WriterSerializer.serialize(Object,Writer)
convenience method for serializing directly to a writer.Juno 5.0.0.29 is a moderate update.
Bean.subTypeProperty() subtypes
. @Bean (filter=xxx)
with new @Transform
annotation.org.apache.juneau.transforms.DateSwap.ISO8601DTP
org.apache.juneau.transforms.DateSwap.ISO8601DTZP
Juno 5.0.0.30 is a minor update.
Bean.subTypes()
annotation in addition to subTypes
property.
Juno 5.0.0.31 is a moderate update.
Juno 5.0.0.32 is a moderate update.
org.apache.juneau.dto.jsonschema
for information.
org.apache.juneau.parser.Parser.parseMap(Object,int,Class,Class,Class)
org.apache.juneau.parser.Parser.parseCollection(Object,int,Class,Class)
Enums
through overriding toString()
and fromString()
on the enum class.Enum.valueOf()
to convert strings back into Enums
.Object
when the type is erased.
setFoo(Foo f)
, setFoo(Bar b)
), the {@link org.apache.juneau.BeanMap} API would sometime choose the wrong setter as the bean property setter. Accept
GET parameters with &Accept=text/json+simple
) wasn't working anymore.Accept
parameter is supposed to interpret spaces as &Accept=text/json%2Bsimple
.
Juno 5.0.0.33 is a moderate update.
WriterSerializer s = new JsonSerializer();
addNotBeanClassPatterns(String...)
methods throughout API since these are now controlled by {@link org.apache.juneau.BeanContext#BEAN_notBeanPackages_add} / {@link org.apache.juneau.BeanContext#BEAN_notBeanPackages_remove} properties.
RestServletProperties
.
RestServletProperties.REST_trimTrailingUriSlashes
RestRequest.getRequestURI(boolean trimTrailingSlashes)
method which is now redundant with this property.
RestServletProperties.REST_pathInfoBlankForNull
RestRequest.getPathInfo(boolean returnBlankForNull)
method which is now redundant with this property.
maxDepth
setting is reached, and will instead simply ignore content below the specified depth.?x-response-headers={Refresh=1}
HtmlDocSerializerContext.HTML_REFRESH
setting that added a Refresh meta tag to HTML documents, since this can now be controlled through X-Response-Headers
.
PhotosResource
now includes a default entry.
Juno 5.0.0.34 is a moderate update.
RestServlet.createRequestVarResolver(RestRequest)
for more information.
RestRequest.getVarResolver()
RestRequest.getServletURI()
RestRequest.getRequestParentURI()
RestResponse.sendRedirect(CharSequence)
RestServlet.createConfigFactory()
RestServlet.createConverters()
RestServlet.createDefaultRequestHeaders()
RestServlet.createDefaultResponseHeaders()
RestServlet.createEncoders()
RestServlet.createFilters()
RestServlet.createGuards()
RestServlet.createMimetypesFileTypeMap()
RestServlet.createParsers()
RestServlet.createProperties()
RestServlet.createRequestProperties(ObjectMap,RestRequest)
RestServlet.createRequestVarResolver(RestRequest)
RestServlet.createSerializers()
RestServlet.createUrlEncodingParser()
RestServletNls
to use ResourceDescription/MethodDescription
instead of RestResource/RestMethod
RestServletProperties.REST_htDocsFolder
.BeanFilter.setStopClass(Class)
which is the program equivalent to the annotation above.
ResultSetList.handleBlob(Blob)
ResultSetList.handleClob(Clob)
Juno 5.0.0.35 is a minor update.
Juno 5.0.0.36 is a minor update.
org.apache.juneau.urlencoding.UrlEncodingParser.parseArgs(Reader,int,ClassMeta[])
.
name
parameter of ResourceDescription#ResourceDescription(String,String,String)
.
is now automatically URL-encoded so that the name can contain special characters (e.g. BeanContext.getClassMetaFromString(String)
.
Now supports primitive arrays such as Juno 5.1.0.0 is a major update.
HttpClient
that performs
serialization and parsing using Juno parsers, but leaves all the details of the HTTP connection
to the Apache code. org.apache.juneau.rest.client.jazz
package and org.apache.juneau.rest.client.jazz.JazzRestClient
class
for performing REST operations against Jazz servers.org.apache.juneau.urlencoding
package for details.
ExtendedReaderParser
abstract class and moved methods into
{@link org.apache.juneau.parser.ReaderParser} class.
DataFormat
class from API since it was no longer necessary
due to API change above.
ParserStringReader
class.String
input.String
input, and tests show no significant performance differences.
org.apache.juneau.parser.Parser.parse(Object,int,ClassMeta)
convenience method added.
BeanContext.getClassMetaFromString(String)
."long[]"
, and so forth.
ResourceDescription
name parameter is now automatically URL-encoded in links.
http://host/contextRoot/foo%2Fbar
).
BeanContext.setDefaultParser(ReaderParser)
method added for specifying
a default parser to use in a bean context (used when converting beans to Strings
using
BeanContext.convertToType(Object,Class)
.
Old behavior simply used the default JSON serializer in these cases.
RestRequest#getParameter(String,Class)
.
RestRequest.getMapParameter(String,Class,Class,Class)
and
RestRequest.getCollectionParameter(String,Class,Class)}
methods.
Juno 5.1.0.1 is a minor update.
@PathRemainder
.
pathInfo
on child resources.
Juno 5.1.0.2 is a minor update.
&Accept-Language
from being used as a GET parameter.
RestServletProperties.REST_allowMethodParam
to be disabled by default.
Juno 5.1.0.3 is a moderate update.
BeanContext.INCLUDE_BEAN_FIELD_PROPERTIES
and BeanContext.INCLUDE_BEAN_METHOD_PROPERTIES
properties, since ignoring fields and methods
can be accomplished by setting the appropriate properties above to {@link org.apache.juneau.Visibility#NONE NONE}.
Also, the {@link org.apache.juneau.annotation.BeanProperty @BeanProperty} annotation can now be used on non-public fields/getters/setters to override
the default behavior defined by the VISIBILITY
properties identified above. This is a convenient way of identifying protected or
private fields or methods as bean properties. Previously, you could only identify public fields/getters/setters using this annotation.
Introspector
class to determine bean properties. In the previous release, the method for determining bean properties was a mixture of Juno-based and Introspector-based.
Now it's either pure Juno-based or pure Introspector-based. The result is considerably cleaner code and consistent behavior.
@BeanProperty (hidden=true )
annotation
for ignoring bean properties. Can also be used on classes that look like beans so that they're not treated as beans.
Bean.subTypeProperty()
annotation.
The previous behavior was not strictly JSON-compliant since JSON objects are supposed to consist of unordered lists of key/value pairs.
While targeted for JSON, the restriction is also lifted for all other parsers.
<div id ='data' >
element to make it easier to extract the data portion of the page in Javascript in browsers.
$A{...}
(request attributes) and $P{...}
(request parameters) to RestServlet.createRequestVarResolver(RestRequest)
.
Juno 5.1.0.4 is a minor update.
RestServlet.getPath()
method.
SerializerContext.getJavaMethod()
and ParserContext.getJavaMethod()
to allow access to REST methods that invoked the serializers or parsers.
For example, can be used to access additional annotations on REST methods to perform special handing
during serialization or parsing.
BeanContext.addTransforms(Class[])
.
Previously, adding multiple conflicting filters resulted in random behavior.
Now filters are overridden when multiple matching filters are applied.
HtmlDocSerializerContext
properties to be set via Serializer.setProperty(String,Object)
.
Previously, these could only be defined through override properties (e.g. through REST class and method annotations).
Juno 5.1.0.5 is a moderate update.
Redirect
class that simplifies performing redirections in REST methods.
UrlEncodingSerializer.serializeUrlPart(Object)
method.
RestRequest.getServletURIBuilder()
for construcing servlet-based URLs more efficiently.
CoreObject.setProperties(ObjectMap)
on serializer and parser subclasses.
Content-Encoding
andCharacter-Encoding
headers were being set when calling {@link org.apache.juneau.rest.RestResponse#getOutputStream()}.
These should not be set if interacting with the output streams at a low level.
RestResponse.sendRedirect(...)
methods due to the introduction of the Redirect
class.
Juno 5.1.0.6 is a moderate update.
beanContext
parameter was replaced with a PojoSwap#getBeanContext()
method on
the class.
Img
is an example of defining an XHTML element using Juno DTOs.
';'
characters in input so that it can
parse strings of the form TumblrParserResource
in the samples war file showing how to combine the REST client and server APIs into a single
resource in order to download Tumblr blogs and convert the response into any supported response content type.
Juno 5.1.0.7 is a moderate update.
ParserContext.PARSER_debug
and SerializerContext.SERIALIZER_debug
.
settings for logging additional information for debugging problems.
SERIALIZER_ignoreRecursions
setting for explicitly ignoring recursions when
serializing models. Previously, the StackOverflowErrors
. When SerializerContext.getProperties()
and
ParserContext.getProperties()
.
SerializerContext
and ParserContext
since these are now available through context properties, and are typically not used.
application/xml
.
Juno 5.1.0.8 is a moderate update, focused primarily on performance improvements.
estimatedSize
parameter to the {@link org.apache.juneau.parser.Parser} parse methods to
optimize buffering when the input size is known beforehand.
BeanPropertyStore
class that handles creation of {@link org.apache.juneau.BeanContext} objects.
This allows BeanContext
objects to be considered immutable, and therefore cacheable/reusable by the framework.
While this was technically possible to cache these objects beforehand, it relied on a locking mechanism to prevent bean contexts
from being modified after being created. The new mechanism is much more straightforward.
RestClient#createHttpClient()
to allow customized subclasses to construct customized HTTP clients.
DefaultRestClient
class since it's now fully redundant with RestClient
.
RestClient.shutdown()
method for cleaning up the internal HTTP client when you're done using a REST client.
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.
org.apache.juneau
- Core serializers and parsers.
org.apache.juneau.rest
- REST server component.
org.apache.juneau.rest.client
- REST client component.
org.apache.juneau.rest.labels.Link
class moved to Link
.
org.apache.juneau.rest.RestException
in {@link org.apache.juneau.encoders.Encoder} class changed to IOException
.
JazzRestClient
to handle introduction of SSO support in v6.
&plainText
debug feature was broken.
RestRequest
.
Juno 5.1.0.10 is a moderate update.
BeanRuntimeExceptions
weren't being thrown on subsequent calls to {@link org.apache.juneau.BeanContext#getClassMeta(Class)}.
BeanContext.getPrimitiveDefault(Class)
to new {@link org.apache.juneau.ClassMeta#getPrimitiveDefault()} method for performance reasons.
BeanContext.addTransforms(Class[])
that would cause filter order to get messed up.
RuntimeExceptions
to make the serializer easier to use for debugging.
HttpServlet.getParameter(String)
was forcing the underlying servlet code to process the HTTP body itself, preventing the UrlEncodingSerializer
class from being able to parse the content. Updated code no longer inadvertantly calls this method.
RestRequest.getQueryParameter(String)
, RestRequest.hasQueryParameter(String)
, and RestRequest.hasAnyQueryParameters(String[])
methods that only look for parameters in the URL query string to prevent loading and parsing of URL-Encoded form posts.
@QParam
and @HasQParam
annotations for accessing query parameters from the URL query string.
&plainText
parameter can now specify a false value.
RestServlet.onPreCall(RestRequest)
and RestServlet#onPostCall(RestRequest,RestResponse)
methods
since the properties are already accessible through RestRequest.getProperties()
.
RestServletJenaDefault
.
RestResponseEntity
to {@link org.apache.juneau.rest.client.RestRequestEntity}.
RestClient#setProperty(String,Object)
RestClient#setProperties(ObjectMap)
RestClient#addNotBeanClasses(Class[])
RestClient.addTransforms(Class[])
RestClient#addImplClass(Class,Class)
RestClient.shutdown()
to {@link org.apache.juneau.rest.client.RestClient#close()} to mirror change in Apache API.
CodeFormatterResource
for quickly formatting Java and XML code samples in Javadocs.
UrlEncodedFormResource
for showing how to work with URL-Encoded form posts.
Juno 5.1.0.11 is a moderate update.
UonParserContext.UON_whitespaceAware
property for controlling whether whitespace is ignored.
UrlEncodingContext.URLENC_expandedParams
property for controlling whether arrays/Collections
should be serialized/parsed as multi-part parameters.
&key=val1&key=val2
).
JsonSerializerContext.JSON_escapeSolidus
property for controlling whether slash characters should be escaped.
BeanPropertyMeta.add(BeanMap,Object)
method for adding values to Collection and array properties.
@Param(multipart)
and @Query(multipart)
annotations
for handling multi-part GET and POST parameters.
&Content
must now be specified as &content
.
&Method
must now be specified as &method
.
&debug
must now be specified as &debug=true
.
&plainText
must now be specified as &plainText=true
.
¬race
must now be specified as &noTrace=true
.
RestRequest.getParameters(String,Class)
RestRequest#getQueryParameters(String,Class)
Content-Type
through this method was inconsistent with the behavior in WAS/Tomcat.
&noTrace=true
now prevents any errors from being logged in log file.
ServletContext.getContextPath()
always ends with RestServletProperties.REST_allowMethodParam
is now RestServletJenaDefault
.
RestCall.peekInputStream()
allows you to read response bodies without interrupting execution flow.
HttpResponse
object for easier debugging.
RestClient.addListener(RestClientListener)
for registering request/response listeners.
RestClient.setClassLoader(ClassLoader)
method.
JazzRestClient
.
samples.ear
and samples.war
projects
have been replaced with an OSGi bundle with activated servlets in juno.samples
.
Juno 5.1.0.12 is a minor update.
ConfigFile.isEmpty()
method.
( ) , $ = ~
.
RestClientListener
class.
Juno 5.1.0.13 is a minor update.
Link
are now serialized using {@link org.apache.juneau.urlencoding.UrlEncodingSerializer}, so arbitrary POJOs can now be passed as arguments.
org.apache.juneau.transforms.Datefilter.ISO8601DTZP
and org.apache.juneau.transforms.Datefilter.SimpleP
.
HtmlDocSerializerContext.HTMLDOC_nowrap
setting for {@link org.apache.juneau.html.HtmlDocSerializer} class.
Adds ClassCastException
.
New behavior creates an empty array or Collection
.
UrlEncodingSerializer.serializeUrlPart(Object)
method.
RestServletNls
class.
RestCall.setRedirectMaxAttempts(int)
method to prevent endless redirection loops.
RestCall#setRetryable(int,long,RetryOn)
method to automatically retry on failed connection attempts.
RestCallInterceptor.onRetry(RestCall,int,HttpRequest,HttpResponse)
method for listening in on retry attempts.
Juno 5.1.0.14 is a moderate update.
The major addition is support for
, the ability
to invoke server-side POJO methods through client-side proxy interfaces.
Remoteable Services
RestClient.setRemoteableServletUri(String)
RestClient#getRemoteableProxy(Class)
RestServletJenaDefault
.
RestServletProperties.REST_allowMethodParam
has been enhanced to allow you to
explicitly specify which HTTP methods can be used in the &method
parameter.
RestRequest.getParser()
RestRequest.getReaderParser()
Juno 5.1.0.15 is a minor update.
SerializerContext
:
SerializerContext.SERIALIZER_relativeUriBase
SerializerContext.SERIALIZER_absolutePathUriBase
SERIALIZER_uriAuthority
and SERIALIZER_uriContext
properties.
RestServletProperties
:
REST_defaultCharset
REST_servletURI
REST_relativeServletURI
RestRequest.getHeader(String,Class)
RestRequest.getHeader(String,Object,Class)
RestRequest.getHeader(String,Type,Type...)
RestRequest.getQueryParameter(String,Class)
RestRequest.getQueryParameter(String,Object,Class)
RestRequest.getQueryParameter(String,Type,Type...)
RestRequest.getQueryParameter(String,Object,Type,Type...)
RestRequest.getQueryParameters(String,Class)
RestRequest.getQueryParameters(String,Type,Type...)
RestRequest.getFormDataParameter(String,Class)
RestRequest.getFormDataParameter(String,Object,Class)
RestRequest.getFormDataParameters(String,Class)
RestRequest.getFormDataParameter(String,Type,Type...)
RestRequest.getFormDataParameters(String,Type,Type...)
RestRequest.getPathParameter(String,Class)
RestRequest.getPathParameter(String,Type,Type...)
RestRequest.getBody(Class)
RestRequest.getBody(Type,Type...)
Juno 5.1.0.16 is a moderate update.
ClassMeta.getXmlMeta()
ClassMeta.getJsonMeta()
ClassMeta.getHtmlMeta()
ClassMeta.getUrlEncodingMeta()
ClassMeta.getRdfMeta()
@Html(asPlainText)
annotation.
HtmlDocSerializerContext.HTMLDOC_cssImports
property.
SerializerContext.SERIALIZER_sortCollections
and
SerializerContext.SERIALIZER_sortMaps
properties.
RestRequest.getServletParentURI()
method.
$R{servletParentURI}
variable.
ChildResourceDescriptions
.
Juno 5.1.0.17 is a major update.
BeanMap.getFiltered(String)
BeanMap.putFiltered(String,Object)
BeanMapEntry.getFiltered(String)
BeanMapEntry.putFiltered(String,Object)
BeanMapEntry.putFiltered(String,Object)
BeanPropertyMeta.getFiltered()
BeanPropertyMeta.setFiltered(Object)
BeanPropertyMeta.getTransformedClassMeta()
StringVarResolver
now has support for chained resolvers.
StringVarResolver
now resolves variables inside resolved values.
i.e. if a resolved variable value itself contains a variable, it now resolves that variable too.
RestResource.filters()
were being
interpreted as surrogate classes because they have hidden 1-arg constructors due to being inner classes.
RdfProperties.RDF_useXmlNamespaces
property.
XmlParserContext.XML_preserveRootElement
property.
ZipFileListResponseHandler
class.
"[ClassName].ResourceDescription"
is now "[ClassName].label"
.
"[ClassName].MethodDescription.[methodName]"
is now "[ClassName].[methodName]"
.
RestRequest.getQueryParameterMap()
RestRequest.getQueryParameterNames()
RestRequest.getPathInfoUndecoded()
RestRequest.getPathRemainderUndecoded()
RestRequest.getTrimmedRequestURI()
RestRequest.getTrimmedRequestURL()
RestRequest.getServletTitle()
RestRequest.getServletDescription()
RestRequest.getPathRemainder()
now automatically decodes the path remainder.
Use RestRequest.getPathRemainderUndecoded()
to get the unencoded path remainder.
RestRequest.getRequestParentURI()
when servlet is mapped to RestRequest.getServletURI()
when servlet is mapped to $R{contextPath}
- Returns value from {@link org.apache.juneau.rest.RestRequest#getContextPath()}
$R{methodDescription}
- Returns value from {@link org.apache.juneau.rest.RestRequest#getMethodDescription()}
$R{resourceTitle}
- Returns value from RestRequest.getServletTitle()
$R{resourceDescription}
- Returns value from RestRequest.getServletDescription()
$R{trimmedRequestURI}
- Returns value from RestRequest.getTrimmedRequestURI()
$E{var}
- Environment variables.
RestServlet.getDescription(RestRequest)
and RestServlet.getLabel(RestRequest)
.
RestServletJenaDefault
now provide default HTML titles
and descriptions:
RestServletJenaDefault
now provide default descriptions and back links:
and descriptions:
RestServletProperties.REST_trimTrailingUriSlashes
and RestServletProperties.REST_pathInfoBlankForNull
.
RestResource.label()
RestMethod#responses()
Attr.description()
Content.description()
HasParam.description()
HasQParam.description()
Header.description()
Param.description()
QParam.description()
ChildResourceDescriptions
.
/tempDir/upload
showing how to use ServletFileUpload
with multipart form posts.
Juno 5.1.0.18 is a minor update affecting the server component only.
RestMethod.input()
and RestMethod.responses()
annotations.
These replace the various description
annotations added 2 days ago with a simpler design.
RestServlet.getMethodDescription(String,RestRequest)
so that subclasses
can override the method description in the OPTIONS page.
RestServlet.createRequestVarResolver(RestRequest)
so that subclasses
can override and augment the variable resolver.
RestServlet.resolveChild(Class)
and RestServlet.replaceChild(RestServlet)
classes that allows customized resolution of servlet instances (e.g. if services are defined in OSGi).
MethodDescription
back to 5.1.0.16 since it was being used by someone.
Juno 5.1.0.19 is a minor update in terms of core functionality.
But it introduces a
project for building REST microservices and docker containers.
Microservices
StringVarResolver
. RestResponse.getUnbufferedWriter()
method.
x-response-headers
parameter from working correctly.
org.apache.juneau.rest.labels
so that the order of the bean properties are consistent
on all JVMs. On IBM JVMs this is unnecessary because the order of the properties as defined in the class
are stored in the bytecode. Other JVMs such as OpenJRE do not implement this feature causing the bean
properties to be in random order.
ResourceDescription.ResourceDescription(RestRequest,String,String)
constructor.
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
functionality.
ConfigFile
org.apache.juneau.config
API.
ConfigFile
is now thread safe and can be shared across multiple threads.
ConfigMgr
class for managing configuration files.
ObjectMap.remove(Class,String,Object)
method.
EncoderGroup#append(EncoderGroup)
method.
HtmlDocSerializerContext.HTMLDOC_addLinks
configuration property.
Parser.createContext(ObjectMap,Method,Object)
method.
Outer context objects can be passed in to create instances of non-static inner classes.
ReflectionUtils.getResource(Class,String)
method.
RestServlet.getConfig()
RestServlet.createConfigFile()
RestServlet.getResource(String)
RestServlet.getResourceAsString(String)
RestServlet.getResource(Class,String,String)
.
Useful if you want to load predefined POJOs from JSON files in your classpath.
RestServlet.handleNotFound(int,RestRequest,RestResponse)
method for customized handling
of when a resource or method was not found.
RestServlet.handleNotFound(int,RestRequest,RestResponse)
method.
RestRequest.resolveVars(String)
RestRequest.getVarResource(String)
RestRequest.getConfig()
getWriter()
now returns an unnegotiated writer.
getUnbufferedWriter()
has been removed.
RestMethod.inheritEncoders()
annotations.
Allows encoders to be fine-tuned at the method level.
ConfigFile
config files with servlets.
ResourceLink
.
org.apache.juneau.rest.matchers
package for commonly-used {@link org.apache.juneau.rest.RestMatcher RestMatchers}:
Juno 5.2.0.0 is a major update. Major changes have been made to the microservice architecture and config INI file APIs.
org.apache.juneau.config
API.
ConfigFile
:
ConfigFile.getStringArray(String)
,ConfigFile.getStringArray(String,String[])
ConfigFile.getSectionAsBean(String,Class)
- Instantiate a new bean with property values in the specified section..
ConfigFile.writeProperties(String,Object,boolean,Class[])
- Copy the properties in a config file section into properties on an existing bean or POJO.
ConfigFile.getSectionMap(String)
- Get all the resolved values in a section.
ConfigFile.containsNonEmptyValue(String)
ConfigFile.isEncoded(String)
ConfigFile.addListener(ConfigFileListener)
- Listen for modification events on the config file.
ConfigFile.merge(ConfigFile)
- Merge the contents of another config file into this config file.
ConfigFile.getResolving()
, ConfigFile.getResolving(StringVarResolver)
- Return an instance of the config file that resolves string variables.
Much more efficient than the previous design since the same underlying config file object is shared.
ConfigFile.toWritable()
- Wraps the config file in a {@link org.apache.juneau.Writable} interface so that it can be serialized by the REST interface as a plain-text INI file instead of as a serialized POJO.
ConfigFile.getInt(String)
- Now supports ConfigMgr
:
ConfigMgr.create()
, ConfigMgr.create(Reader)
, ConfigMgr.create(File)
ConfigMgr.deleteAll()
Section
:
Section.setParent(ConfigFileImpl)
- Used by parsers to set the config file for this section.
Section.setName(String)
- Used by parsers to set the name for this section.
org.apache.juneau.config.ConfigFileListener
org.apache.juneau.config.SectionListener
org.apache.juneau.config.EntryListener
org.apache.juneau.config.Encoder
methods have access to field names to use them as salt values.
org.apache.juneau.config.XorEncoder
XOR key can be overridden through the fromString(String)
valueOf(String)
(e.g. enums)
parse(String)
(e.g. logging Level
class)
parseString(String)
forName(String)
(e.g. Class
and Charset
classes)
Pair<S,T>
and you try to parse into this
class (e.g. parser.parse(in, Pair.class )
), the unbound type variables
is interpreted as Object
instead of throwing an exception.
AtomicInteger
AtomicLong
BigInteger
BigDecimal
Section
class represents a section
in a config file. It needs to know it's own name and have a link to the ConfigFile
that it belongs to. With these new annotations, config files can be reconstructed using any of the parsers.
StringObject
class that can be used for delayed object serialization.
StringVarMultipart
StringVarWithDefault
IOUtils.pipe(Reader,Writer)
PojoRest.get(Class,String,Object)
StringUtils.parseISO8601Date(String)
StringVar.doResolve(String)
method.
StringVarResolver.DEFAULT
field.
javax.mail.internet.MimeUtility
by implementing our own {@link org.apache.juneau.internal.StringUtils#base64Encode(byte[])} method.
/servletPath/style.css
instead of /servletPath/htdocs/juneau.css
.
This coincides with enhancements made in the server code for specifying styles.
<div class='outerdata'><div class='data' id='data'>...</div></div>
).
Needed for supporting the new devops look-and-feel.
RdfProperties.RDF_looseCollection
loose collections.
NoClassDefFoundErrors
.
UrlEncodingSerializer.DEFAULT_SIMPLE_EXPANDED
serializer.
getMainArg(int)
changed to {@link org.apache.juneau.utils.Args#getArg(int)}.
Non-existent arguments are returned as org.apache.juneau.utils.CharsetUtils
class.
org.apache.juneau.utils.ConcurrentIdentityList
class.
ReaderParser
.
Simplifies the API on the class.
ReaderParser
.
Simplifies the API on the class.
SafeResourceMultiBundle
moved from server component.
StringVarResolver
.
NoClassDefFoundErrors
so that resources that include Jena support can continue to operate even if the Jena libraries are not present.
SSLOpts
org.apache.juneau.rest.client.LaxRedirectStrategy
. Use HTTP Client equivalent.
RestCall#addInterceptor(RestCallInterceptor)
RestCall#addResponsePattern(ResponsePattern)
execute()
.
RestClient.setBasicAuth(String,int,String,String)
RestClient.logTo(Level,Logger)
RestClient.setRootUrl(String)
RestClient.enableSSL(SSLOpts)
RestClient.enableLaxSSL()
RestClient.createHttpClientBuilder()
HttpClientBuilder
:
RestClient.setRedirectStrategy(RedirectStrategy)
RestClient.setDefaultCookieSpecRegistry(Lookup)
RestClient.setRequestExecutor(HttpRequestExecutor)
RestClient.setSSLHostnameVerifier(HostnameVerifier)
RestClient.setPublicSuffixMatcher(PublicSuffixMatcher)
RestClient.setSSLContext(SSLContext)
RestClient.setSSLSocketFactory(LayeredConnectionSocketFactory)
RestClient.setMaxConnTotal(int)
RestClient.setMaxConnPerRoute(int)
RestClient.setDefaultSocketConfig(SocketConfig)
RestClient.setDefaultConnectionConfig(ConnectionConfig)
RestClient.setConnectionTimeToLive(long,TimeUnit)
RestClient.setConnectionManager(HttpClientConnectionManager)
RestClient.setConnectionManagerShared(boolean)
RestClient.setConnectionReuseStrategy(ConnectionReuseStrategy)
RestClient.setKeepAliveStrategy(ConnectionKeepAliveStrategy)
RestClient.setTargetAuthenticationStrategy(AuthenticationStrategy)
RestClient.setProxyAuthenticationStrategy(AuthenticationStrategy)
RestClient.setUserTokenHandler(UserTokenHandler)
RestClient.disableConnectionState()
RestClient.setSchemePortResolver(SchemePortResolver)
RestClient.setUserAgent(String userAgent)
RestClient.setDefaultHeaders(Collection)
RestClient.addInterceptorFirst(HttpResponseInterceptor)
RestClient.addInterceptorLast(HttpResponseInterceptor)
RestClient.addInterceptorFirst(HttpRequestInterceptor)
RestClient.addInterceptorLast(HttpRequestInterceptor)
RestClient.disableCookieManagement()
RestClient.disableContentCompression()
RestClient.disableAuthCaching()
RestClient.setHttpProcessor(HttpProcessor)
RestClient.setRetryHandler(HttpRequestRetryHandler)
RestClient.disableAutomaticRetries()
RestClient.setProxy(HttpHost)
RestClient.setRoutePlanner(HttpRoutePlanner)
RestClient.disableRedirectHandling()
RestClient.setConnectionBackoffStrategy(ConnectionBackoffStrategy)
RestClient.setBackoffManager(BackoffManager)
RestClient.setServiceUnavailableRetryStrategy(ServiceUnavailableRetryStrategy)
RestClient.setDefaultCookieStore(CookieStore)
RestClient.setDefaultCredentialsProvider(CredentialsProvider)
RestClient.setDefaultAuthSchemeRegistry(Lookup)
RestClient.setContentDecoderRegistry(Map)
RestClient.setDefaultRequestConfig(RequestConfig)
RestClient.useSystemProperties()
RestClient.evictExpiredConnections()
RestClient.evictIdleConnections(long,TimeUnit)
JazzRestClient
now supports OIDC authentication.
org.apache.juneau.rest.client.jazz.CertificateStore
org.apache.juneau.rest.client.jazz.ICertificateValidator
org.apache.juneau.rest.client.jazz.ITrustStoreProvider
org.apache.juneau.rest.client.jazz.LenientCertificateValidator
org.apache.juneau.rest.client.jazz.SharedTrustStoreProvider
org.apache.juneau.rest.client.jazz.ValidatingX509TrustManager
ReaderResource
class.
Represents the contents of a text file with convenience methods for resolving
StringVar
variables and adding HTTP response headers.
REST Java methods can return instances of these to serialize Readers
containing text with StringVarResolver
variables in them.
StreamResource
class.
REST Java methods can return instances of these to serialize OutputStreams
.
RestRequest.getReaderResource(String)
- Replaces getVarResource(String)
.
RestRequest.getReaderResource(String,boolean)
RestRequest.getReaderResource(String,boolean,String)
Content-Encoding: identity
when no encoding is used. Some clients don't interpret it correctly.
RestServlet.getChildClasses()
- Programmatic equivalent to {@link org.apache.juneau.rest.annotation.RestResource#children() @RestResource(children)} annotation.
RestServlet.shouldLog(HttpServletRequest,HttpServletResponse,RestException)
RestServlet.shouldLogStackTrace(HttpServletRequest,HttpServletResponse,RestException)
RestServlet.logObjects(Level,String,Object[])
RestServlet.resolveStaticFile(String)
RestServlet.createStyleSheet()
RestServlet.createFavIcon()
RestServlet.createStaticFilesMap()
RestServlet.getConfigMgr()
RestServletJenaDefault
.
These may represent a security risk if not handled correctly, so removed
them as a precaution.
RestServletProperties.REST_htDocsFolder
. Replaced with {@link org.apache.juneau.rest.annotation.RestResource#staticFiles() @RestResource(staticFiles)}.
RestResource.stylesheet()
RestResource.favicon()
org.apache.juneau.rest.jaxrs.JsonProvider
class.
Some JAX-RS implementations use code scanning to find providers, so if you were using DefaultJenaProvider
, it would
pick up JsonProvider
as well. It's easy enough to create your own implementation if needed.
consumes
and produces
fields instead of accept
and contentType
which was confusing.
properties
from OPTIONS pages.
ResourceLink.ResourceLink(String,RestRequest,String,Object[])
constructor.
StreamableHandler
- Allows REST Java methods to return instances of {@link org.apache.juneau.Streamable}.
WritableHandler
- Allows REST Java methods to return instances of {@link org.apache.juneau.Writable}.
RestUtils.trimPathInfo(StringBuffer,String,String)
method.
RestMicroservice
class that implements a microservice
consisting of a REST interface.
org.apache.juneau.microservice.Main
class. This is replaced by
the microservice classes defined above.
Resource
and ResourceGroup
classes now support the following new string variables:
BasicRestServletJena
class if you want your REST interface to support RDF.
org.apache.juneau.microservice.RootResource
org.apache.juneau.microservice.SampleResource
StringVarResolver
- New documentation.
Overview / Samples
- New section.
Juno 5.2.0.1 is a moderate update.
ObjectList.getAt(Class,String)
ObjectMap.getAt(Class,String)
@ThreadSafe
annotation.
ClassFilter
class.
ConfigFile.getResolving(StringVarResolver,boolean)
method.
ConfigFile.getStringVar()
method.
ParserContext.PARSER_trimStrings
property.
SerializerContext.SERIALIZER_trimStrings
property.
Args.getStringVar()}
method.
StringMapVar
class.
StringVars
class with reusable common StringVar
instances.
XmlParserContext.XML_trimWhitespace
changed to 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.
org.apache.juneau.svl
.
SerializerContext.SERIALIZER_addBeanTypeProperties
- Controls whether type properties are serialized.
@Xml(name)
annotation, and the
BeanFilter
class to use final fields.
java.util.Date
).
org.apache.juneau.internal
package.
These internal utility classes are not meant for consumption outside the Juneau codebase.
org.apache.juneau.parser.Parser.createSession(ObjectMap,Method,Object)
Parser.getMediaRanges()
org.apache.juneau.serializer.Serializer.createSession(ObjectMap,Method)
Serializer.getMediaRanges()
ClassFilter
class since it's no longer needed.
BeanContext.convertToType(Object,Class)
.
HtmlSerializerContext.HTML_detectLinksInStrings
- Automatically detect hyperlinks in strings.
HtmlSerializerContext.HTML_lookForLabelParameters
- Specify anchor text by appending &label=MyLabel
to URL.
HtmlSerializerContext.HTML_labelParameter
- Specify what URL parameter to use as the anchor text label.
HtmlSerializerContext.URI_ANCHOR
option for HtmlSerializerContext.HTML_uriAnchorText
.
@Transform
annotation to @Pojo
so that it can be used for various POJO-related behavior, not just associating transforms.
ResourceOptions
and related code.
@RestResource(termsOfService)
/ RestInfoProvider.getTermsOfService(RestRequest)
@RestResource(contact)
/ RestInfoProvider.getContact(RestRequest)
@RestResource(license)
/ RestInfoProvider.getLicense(RestRequest)
@RestResource(version)
/ RestInfoProvider.getVersion(RestRequest)
@RestResource(tags)
/ RestInfoProvider.getTags(RestRequest)
@RestResource(externalDocs)
/ RestInfoProvidergetExternalDocs(RestRequest)
RestInfoProvider.getMethodSummary(String,RestRequest)
RestInfoProvider.getMethodDescription(String,RestRequest)
@RestMethod(externalDocs)
@RestMethod(tags)
@RestMethod(deprecated)
@RestMethod(parameters)
@RestMethod(responses)
RestServletContext.paramFormat
context property.
RestServlet.createProperties()
RestServlet.createBeanContext(ObjectMap,Class[],Class[])
RestServlet.createBeanFilters()
RestServlet.createPojoSwaps()
RestServlet.createParsers(ObjectMap,Class[],Class[])
RestServlet.createUrlEncodingSerializer(ObjectMap,Class[],Class[])
RestServlet.createUrlEncodingParser(ObjectMap,Class[],Class[])
RestServlet.createConverters(ObjectMap)
RestServlet.createDefaultRequestHeaders(ObjectMap)
RestServlet.createDefaultResponseHeaders(ObjectMap)
RestServlet.createEncoders(ObjectMap)
RestServlet.createGuards(ObjectMap)
RestServlet.createMimetypesFileTypeMap(ObjectMap)
RestServlet.createResponseHandlers(ObjectMap)
JazzRestClient
class.
RestClient.setClientVersion(String)
.
Juneau 6.0.1 is a minor update.
ParserContext
.
ParserContext.PARSER_strict
ParserContext.PARSER_inputStreamCharset
ParserContext.PARSER_fileCharset
JsonParserContext.JSON_strictMode
. Replaced by PARSER_strict
.
byte[]
arrays can now be passed to {@link org.apache.juneau.parser.Parser#parse(Object,Class)} for reader-based parsers.
Juneau 6.1.0 is a major update.
In particular, this release cleans up the {@link org.apache.juneau.BeanContext} API to match the {@link org.apache.juneau.PropertyStore}/{@link org.apache.juneau.Context}/{@link org.apache.juneau.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.
org.apache.juneau.xml
documentation.
XmlContentHandler
class.
addJsonTypeAttrs
and addJsonStringTypeAttrs
settings.
XMLEventReader
-based to XMLStreamReader
.
org.apache.juneau.html
documentation.
org.apache.juneau.dto.html5
.
SerializerContext
and ParserContext
now extend directly from {@link org.apache.juneau.BeanContext}.
parseMap()
, parseCollection()
)
by replacing them with two simple methods:
ClassMeta
object.
ClassMeta
objects.
parseMap()
and parseCollection()
methods!
BeanContext.normalizeClassMeta()
method.
toObjectMap()
and fromObjectMap()/T(ObjectMap)
methods with
generalized swap(BeanSession)
/unswap(BeanSession,X)
/T(BeanSession,X)
methods.Swap methods
for information.
org.apache.juneau.dto.atom
documentation.
getMapClassMeta()
/getCollectionClassMeta()
methods.
Juneau Data Transfer Objects (org.apache.juneau.dto)
UonParser.DEFAULT_WS_AWARE
and UrlEncodingParser.DEFAULT_WS_AWARE
parsers.
UonParserContext.UON_whitespaceAware
configuration setting.
UonSerializer.DEFAULT_SIMPLE
, UonSerializer.DEFAULT_SIMPLE_ENCODING
and UrlEncodingSerializer.DEFAULT_SIMPLE
serializers since there is no separate simple mode anymore.
UonParserContext.UON_simpleMode
configuration setting.
OutputStreamSerializer.serializeToHex(Object)
method.
@Bean (subTypeProperty)
and @Bean (subTypes)
annotations
and replaced them with the ability to define subtypes using the existing {@link org.apache.juneau.annotation.Bean#beanDictionary() @Bean(beanDictionary)}
annotation on parent classes and interfaces.
SerializerContext.SERIALIZER_addBeanTypeProperties
setting is now enabled by default.
SERIALIZER_addIndentation
/JSON_addWhitespace
/UON_addWhitespace
properties into a single SerializerContext.SERIALIZER_useWhitespace
setting.
RestRequest.getTimeZone()
method.
ClassMeta
objects and eliminate the need for casts:
RestRequest.getHeader(String,Class)
RestRequest.getHeader(String,Object,Class)
RestRequest.getHeader(String,Type,Type...)
RestRequest.getQueryParameter(String,Class)
RestRequest.getQueryParameter(String,Object,Class)
RestRequest.getQueryParameter(String,Type,Type...)
RestRequest.getQueryParameter(String,Object,Type,Type...)
RestRequest.getQueryParameters(String,Class)
RestRequest.getQueryParameters(String,Type,Type...)
RestRequest.getFormDataParameter(String,Class)
RestRequest.getFormDataParameter(String,Object,Class)
RestRequest.getFormDataParameters(String,Class)
RestRequest.getFormDataParameter(String,Type,Type...)
RestRequest.getFormDataParameters(String,Type,Type...)
RestRequest.getPathParameter(String,Class)
RestRequest.getPathParameter(String,Type,Type...)
RestRequest.getBody(Class)
RestRequest.getBody(Type,Type...)
&plainText=true
specified.
Juneau 6.2.0 is a major update.
ConfigFileBuilder
.
Lockable
interface.
addBeanTypeProperties
setting added to serializers to override the
SerializerContext.SERIALIZER_addBeanTypeProperties
setting
for individual serializers in a serializer group:
HtmlSerializerContext.HTML_addBeanTypeProperties
JsonSerializerContext.JSON_addBeanTypeProperties
MsgPackSerializerContext.MSGPACK_addBeanTypeProperties
UonSerializerContext.UON_addBeanTypeProperties
XmlSerializerContext.#XML_addBeanTypeProperties
RdfSerializerContext.RDF_addBeanTypeProperties
org.apache.juneau.uon
package.
style()
override methods to all elements.
here
and in the $SWITCH
variable for switch block logic.
SerializerContext.SERIALIZER_abridged
.
org.apache.juneau.remoteable
for all remoteable proxy interface annotations.
6 - Remoteable Services
6.1 - Interface proxies against 3rd-party REST interfaces
UrlEncodingSerializerContext.URLENC_paramFormat
.
UrlEncodingSerializerBuilder.paramFormat(String)
UrlEncodingSerializerBuilder.plainTextParams()
RestServlet
should have an equivalent for non-RestServlet
classes.
RestServlet
.
Child resources do not.
RestConfig
- A modifiable configuration of a resource. Subclasses from {@link javax.servlet.ServletConfig}.
RestServlet.init(RestConfig)
- A modifiable configuration of a resource.
RestServlet
classes must have one of the following to allow it to be instantiated:
public T(RestConfig)
constructor.
public T()
constructor.
RestServlet
classes can optionally include the following init methods to gain access to the config and context:
public init(RestConfig)
public init(RestContext)
RestServlet
resources to do the same as subclassing directly from RestServlet
:
@RestResource(pageTitle)
@RestMethod(pageTitle)
@RestResource(pageText)
@RestMethod(pageText)
@RestResource(pageLinks)
@RestMethod(pageLinks)
Typically you're going to simply want to use the title
and description
annotations
which apply to both the page title/text and the swagger doc:
RestResource.stylesheet()
can now take in a comma-delimited list of stylesheet paths.
StreamResource
can now contain multiple sources from a variety of source types (e.g. byte []
arrays, InputStreams
, Files
, etc...)
and is now immutable. It also includes a new StreamResourceBuilder
class.
@RestMethod (name="PROXY" )
annotation on REST methods.
Used to expose interface proxies without the need for RemoteableServlet
.
RestClient
class doX(Object url)
methods now handle HttpClient URIBuilder
instances.
RestClient.getRemoteableProxy(Class,Object)
- For interface proxies defined using @RestMethod (name="PROXY" )
.
RestClient.getRemoteableProxy(Class,Object,Serializer,Parser)
- Same as above, but overrides the serializer and parser defined on the client.
query(String,Object,boolean,PartSerializer)
formData(String,Object,boolean,PartSerializer)
header(String,Object,boolean,PartSerializer)
RestClientBuilder.plainTextParams()
No-Trace: true
header on all requests to prevent
the servlet from logging errors.
Debug: true
header on all requests.
input(Object)
- Now accepts instances of {@link org.apache.juneau.rest.client.NameValuePairs}.
HttpResponse
returned by the inner HttpClient
.
append(String,Object,PartSerializer)
0
s to try a random port.
RestMicroservice
class:
getPort()
getURI()
Juneau 6.3.0 is a major update with significant new functionality for defining proxy interfaces against arbitrary 3rd-party REST interfaces.
2.8 - Virtual Beans
2.13 - Comparison with Jackson
ConfigFile
:
ConfigFile.put(String,String,String,boolean)
ConfigFile.put(String,String,Object,Serializer,boolean,boolean)
ConfigFile.getObject(String,Type,Type...)
ConfigFile.getObject(String,Parser,Type,Type...)
ConfigFile.getObject(String,Class)
ConfigFile.getObject(String,Parser,Class)
ConfigFile.getObject(String,String,Type,Type...)
ConfigFile.getObject(String,String,Parser,Type,Type...)
ConfigFile.getObject(String,String,Class)
ConfigFile.getObject(String,String,Parser,Class)
ConfigFile.getObjectWithDefault(String,Object,Type,Type...)
ConfigFile.getObjectWithDefault(String,Parser,Object,Type,Type...)
ConfigFile.getObjectWithDefault(String,Object,Class)
ConfigFile.getObjectWithDefault(String,Parser,Object,Class)
ConfigFile.getSectionAsInterface(String,Class)
.
Session.getProperty(String)
Session.getProperty(String,String)
Session.getProperty(Class,String)
Session.getProperty(Class,String,Object)
org.apache.juneau.serializer.PartSerializer
interface particularly tailored to HTTP
headers, query parameters, form-data parameters, and path variables.
org.apache.juneau.rest.client.RestClientBuilder.partSerializer(Class)
org.apache.juneau.remoteable.Path.serializer
org.apache.juneau.remoteable.Query.serializer
org.apache.juneau.remoteable.QueryIfNE.serializer
org.apache.juneau.remoteable.FormData.serializer
org.apache.juneau.remoteable.FormDataIfNE.serializer
org.apache.juneau.remoteable.Header.serializer
org.apache.juneau.remoteable.HeaderIfNE.serializer
pages=
links=
SerializerContext.SERIALIZER_uriContext
SerializerContext.SERIALIZER_uriRelativity
SerializerContext.SERIALIZER_uriResolution
SerializerContext.SERIALIZER_maxIndent
UonSerializer.UON_paramFormat
,
and the UON/URL-Encoding serializers will now always serialize all values as plain text.
RestConfig.serializerListener(Class)
RestConfig.parserListener(Class)
RestClientBuilder.listeners(Class,Class)
ParserSession.getInputAsString()
method so that it can be used
in the listeners.
HtmlDocSerializerContext.HTMLDOC_title
HtmlDocSerializerContext.HTMLDOC_description
HtmlDocSerializerContext.HTMLDOC_branding
HtmlDocSerializerContext.HTMLDOC_header
HtmlDocSerializerContext.HTMLDOC_nav
HtmlDocSerializerContext.HTMLDOC_aside
HtmlDocSerializerContext.HTMLDOC_footer
HtmlDocSerializerContext.HTMLDOC_noResultsMessage
HtmlDocSerializerContext.HTMLDOC_cssUrl
HtmlDocSerializerContext.HTMLDOC_css
HtmlDocSerializerContext.HTMLDOC_template
RestRequest
.
RestResponse
.
@RestMethod (name="*" )
)
ConfigFile
- The external config file for the resource.
bpIncludes()
bpExcludes()
@Header(def)
- Default header value.
@Query(def)
- Default query parameter value.
@FormData(def)
- Default form data parameter value.
widgets()
widgets()
RestConfig
:
setHtmlTitle(String)
setHtmlDescription(String)
setHtmlBranding(String)
setHtmlHeader(String)
setHtmlLinks(String)
setHtmlNav(String)
setHtmlAside(String)
setHtmlFooter(String)
setHtmlCss(String)
setHtmlCssUrl(String)
setHtmlNoWrap(boolean)
setHtmlNoResultsMessage(String)
setHtmlTemplate(Class)
setHtmlTemplate(HtmlDocTemplate)
addWidget(Class)
setHtmlTitle(Object)
setHtmlDescription(Object)
setHtmlBranding(Object)
setHtmlHeader(Object)
setHtmlLinks(Object)
setHtmlNav(Object)
setHtmlAside(Object)
setHtmlFooter(Object)
setHtmlCss(Object)
setHtmlCssUrl(Object)
setHtmlNoWrap(boolean)
setHtmlNoResultsMessage(Object)
setHtmlTemplate(Class)
setHtmlTemplate(HtmlDocTemplate)
&plainText=true
parameter now works on byte-based serializers by converting the output to hex.
PoweredByJuneauWidget
ContentTypeLinksColumnWidget
ContentTypeLinksRowWidget
QueryWidget
devops.css
cleaned up.
org.apache.juneau.remoteable.Path
annotation for specifying path variables on remoteable interfaces.
@RequestBean
annotation for specifying beans with remoteable annotations
defined on properties.
NameValuePairs
and beans as input
when using org.apache.juneau.remoteable.FormData
,org.apache.juneau.remoteable.FormDataIfNE
,
org.apache.juneau.remoteable.Query
,org.apache.juneau.remoteable.QueryIfNE
,
org.apache.juneau.remoteable.Header
,org.apache.juneau.remoteable.HeaderIfNE
Juneau 6.3.1 is a minor release.
RemoteMethod.returns()
annotation.
HtmlDocSerializerContext
:
HTMLDOC_script
HTMLDOC_style
- Was HTMLDOC_stylesheet
- Was ResourceFinder
utility class.
Allows you to search for resources up the parent hierarchy chain.
Also allows you to search for localized resources.
HtmlDocSerializerContext
:
PoweredByApacheWidget
-> PoweredByApache
PoweredByJuneauWidget
-> PoweredByJuneau
css()
.
cssUrl()
.
@RestResource(stylesheet)
annotation.
It's no longer needed now that you can easily specify styles via title()
description()
branding()
htmldoc=
DockerRegistryResource
examples shows how it can be used to pull in a localized
file from the classpath to populate the aside section of a page.
htmldoc=
ReaderResource.toCommentStrippedString()
method.
bpIncludes()
and bpExcludes()
annotations on $R
variable: The major change in this release is the project structure.
The library now consists of the following artifacts found in the Maven group "org.apache.juneau"
:
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 | juneau-examples-core |
Core code examples | |
juneau-examples-rest |
REST code examples | ||
Juneau All | juneau-all |
Combination of the following:
|
|
@Pojo
and @BeanProperty(swap)
annotations.
PojoSwaps
, this can be used to provide customized
output for specific content types.
SerializeException
/ParseException
.
getClass()
to retrieve the annotation value could not be called before calling
the super ()
method.
toString()
method.
Swagger.toString()
produces JSON and the HTML5 Form.toString()
produces HTML.
init(RestConfig)
- Use {@link org.apache.juneau.rest.annotation.HookEvent#INIT} instead.
onSuccess(RestRequest, RestResponse, long)
- Use {@link org.apache.juneau.rest.annotation.HookEvent#END_CALL} instead.
onPreCall(RestRequest)
- Use {@link org.apache.juneau.rest.annotation.HookEvent#PRE_CALL} instead.
onPostCall(RestRequest, RestResponse)
- Use {@link org.apache.juneau.rest.annotation.HookEvent#POST_CALL} instead.
RestResource.contextPath()
-
RestContext.REST_allowHeaderParams
setting.
RestResource.allowMethodParam()
-
RestContext.REST_allowMethodParam
setting.
RestContext.REST_allowBodyParam
setting.
RestContext.REST_xxx
setting.
RestContext.REST_useStackTraceHashes
setting.
RestContext.REST_defaultCharset
setting.
RestResource.paramFormat()
-
RestContext.REST_paramFormat
setting.
RestContext.REST_defaultCharset
setting.
RestMethod.paramFormat()
-
RestContext.REST_paramFormat
setting.
?stylesheet
query parameter.
RestServletJenaDefault
class to remove the Jena dependency class on
the juneau-rest-server
artifact.
BasicRestServlet
and add the RDF serializers and
parsers.
jetty.xml
file
for maximum flexibility instead of the hodge-podge of support in the config file.
jetty.xml
file.
RestMicroservice
:
addServlet(Servlet,String)
addServletAttribute(String,Object)
getServer()
getInstance()
getPort()
getContextPath()
getProtocol()
getHostName()
JettyLogger
for directing Jetty logging to the
java.util.logging framework.
DebugResource
for viewing and generating
Jetty thread dumps through REST calls.
This release ups the Java prerequisite to Java 7.
org.apache.juneau.dto.Link
renamed to {@link org.apache.juneau.dto.LinkString}.
Helps avoid confusion since there are other Link classes in the library.
@HtmlDoc (links)
renamed to {@link org.apache.juneau.rest.annotation.HtmlDoc#navlinks() navlinks}.
@HtmlDoc (favIcon)
.
@HtmlDoc (head)
, you can define them using:
head={
RestResponse/RestConfig/RestContext
classes and moved it into the new {@link org.apache.juneau.rest.HtmlDocBuilder} class.
This release is a minor update. It includes the following prereq updates:
create()
methods for builders on serializers and parsers.
ConfigFile.create()
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.
createConsoleCommands()
Version 7.1.0 is a major update with major implementation refactoring across all aspects of the product.
Context
classes (e.g. JsonSerializerContext
) have been folded into
their respective serializer or parser classes (e.g. JsonSerializer
).
JsonSerializer
is now:
Context
objects are thread-safe and read-only.
JsonSerializerSession
is now:
JsonSerializerBuilder
is now:
Context
objects.
PropertyStore
class was overly-complicated with many read/write
locks to ensure thread-safety.
PropertyStore
objects
that can be used as hash keys.
PartSerializer
/PartParser
classes
have been replaced with the following all located in the new org.apache.juneau.httppart
package:
UonPartSerializer
SimpleUonPartSerializer
UonPartParser
SimplePartParser
ContextBuilder.property(String,Object)
renamed to {@link org.apache.juneau.ContextBuilder#set(String,Object)}.
ResourceFinder
class has been replaced with the following:
RestServletDefault
renamed to {@link org.apache.juneau.rest.BasicRestServlet}.
RestServletGroupDefault
renamed to {@link org.apache.juneau.rest.BasicRestServletGroup}.
HttpServletRequest.getAttribute(String)
.
RestConfig
class into {@link org.apache.juneau.rest.RestContextBuilder}.
REST_contextPath
RestMatcherReflecting
class.
@RestResource.allowMethodParam
renamed to {@link org.apache.juneau.rest.annotation.RestResource#allowedMethodParams}.
@RestMethod.serializersInherit
and @RestMethod.parsersInherit
replaced with
simplified @RestMethod(inherit)
.
Map<String,Object>
instead of Map<String,String>
.
Map<String,Object>
instead of Map<String,String>
.
Map<String,Object>
instead of Map<String,String>
.
getResource(String,Locale)
renamed to {@link org.apache.juneau.rest.RestContext#getClasspathResource(String,Locale) getClasspathResource(String,Locale)}
getResourceAsString(String,Locale)
renamed to {@link org.apache.juneau.rest.RestContext#getClasspathResourceAsString(String,Locale) getClasspathResourceAsString(String,Locale)}
getResource(Class,MediaType,String,Locale)
renamed to {@link org.apache.juneau.rest.RestContext#getClasspathResource(Class,MediaType,String,Locale) getClasspathResourceAsString(Class,MediaType,String,Locale)}
Map<String,Object>
instead of Map<String,String>
.
Map<String,Object>
instead of Map<String,String>
.
getSupportedMediaTypes()
replaced with
{@link org.apache.juneau.rest.RestRequest#getConsumes() getConsumes()} and
{@link org.apache.juneau.rest.RestRequest#getProduces() getProduces()}.
getReaderResource(String,boolean,MediaType)
renamed to
getClasspathReaderResource(String,boolean,MediaType)
getReaderResource(String,boolean)
renamed to
{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String,boolean) getClasspathReaderResource(String,boolean)}
getReaderResource(String)
renamed to
{@link org.apache.juneau.rest.RestRequest#getClasspathReaderResource(String) getClasspathReaderResource(String)}
RestCallHandler
split up into {@link org.apache.juneau.rest.RestCallHandler} and
{@link org.apache.juneau.rest.BasicRestCallHandler}
RestInfoProvider
split up into {@link org.apache.juneau.rest.RestInfoProvider} and
{@link org.apache.juneau.rest.BasicRestInfoProvider}
RestLogger
split up into {@link org.apache.juneau.rest.RestLogger},
{@link org.apache.juneau.rest.BasicRestLogger} and {@link org.apache.juneau.rest.NoOpRestLogger}
RestResourceResolverSimple
renamed to {@link org.apache.juneau.rest.BasicRestResourceResolver}
@Messages
and @Properties
REST java method parameter
annotations.
MessageBundle
and
RestRequestProperties
as unannotated parameters.
ReaderResourceBuilder
StreamResourceBuilder
FinishableServletOutputStream
and {@link org.apache.juneau.rest.RestResponse#getNegotiatedWriter()}
now returns a FinishablePrintWriter
that allows you to finish the output
without closing the stream.
finish()
instead of close()
on the stream.
Resource
class):
PartSerializer
with {@link org.apache.juneau.httppart.HttpPartSerializer}.
SimpleUonPartSerializer
which will
serialize strings as plain-text and collections/arrays as comma-delimited lists.
Closeable
interface.
Resource
and ResourceGroup
classes removed.
ResourceJena
and ResourceJenaGroup
classes renamed to
BasicRestServletJena
and BasicRestServletJenaGroup
.
7.2.1 is a major release that introduces several significant new features:
ExternalDocs
Items
Schema
SubItems
RemoteInterfaceServlet
or REST @Query ("*" )
, @FormData ("*" )
, @Header ("*" )
, @Path ("*" )
@Bean (typeName)
was not being detected on non-bean POJO classes.
@BeanProperty (name)
but forget to
add it to your @Bean (properties)
annotation.
@Html (asXml)
and @Html (asPlainText)
replaced with
{@link org.apache.juneau.html.annotation.Html#format() @Html(format)}.
@Html (format=XML )
.
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---
toString()
method.
getX()
and isX()
method, the getX()
method takes precedence.
@JsonSchema
annotation.
NullPointerException
when serializing beans with a dyna-property (i.e. @Bean ("*" )
)
which returns a @Bean ("*" )
) using a method that returns a collection of extra keys.
JsonSerializer.Simple
is JsonSerializer.Simple
class has been moved into the top-level {@link org.apache.juneau.json.SimpleJsonSerializer} class.
RdfSerializer.Xml
-> {@link org.apache.juneau.jena.RdfXmlSerializer}
RdfSerializer.XmlAbbrev
-> {@link org.apache.juneau.jena.RdfXmlAbbrevSerializer}
RdfSerializer.N3
-> {@link org.apache.juneau.jena.N3Serializer}
RdfSerializer.NTriple
-> {@link org.apache.juneau.jena.NTripleSerializer}
RdfSerializer.Turtle
-> {@link org.apache.juneau.jena.TurtleSerializer}
RdfParser.Xml
-> {@link org.apache.juneau.jena.RdfXmlParser}
RdfParser.N3
-> {@link org.apache.juneau.jena.N3Parser}
RdfParser.NTriple
-> {@link org.apache.juneau.jena.NTripleParser}
RdfParser.Turtle
-> {@link org.apache.juneau.jena.TurtleParser}
new Select().disabled(true )
will produce <select disabled='disabled'>
UriResolver
when request path info had special characters.
text/html+schema
instead of text/html
for schema documents).
RemoteableServlet
class has been moved and renamed to RemoteInterfaceServlet
.
RemoteInterfaceServlet
now provides a form page for invoking remote interface methods in a browser.
@HtmlDoc (script)
when serialized which could cause script lines to become commented out.
@Path ("/*" )
to access the path remainder which includes all the new OpenAPI parsing support.
ReaderResource
ReaderResourceBuilder
StreamResource
StreamResourceBuilder
nav()
are free-form HTML that gets rendered immediately after the navigation links.
$F
variable can now be used as a initialization time variable.
AtomFeedResource
example pulls a bean example from a file on the classpath:
$F
variable to retrieve localized versions of files (since you're not within
the scope of a client request.
RestResource.nowrap()
annotation has been changed to a string with a default value of @RestMethod (inherit)
annotation has been removed and replaced with the following classes:
Inherit
class.
RequestPathMatch
class has been renamed to {@link org.apache.juneau.rest.RequestPath}.
RedirectToServletRoot
class has been renamed to {@link org.apache.juneau.rest.helper.SeeOtherRoot}.
Accept
and Content-Type
headers:
RestCall.execute()
method.
RestCall.input(Object)
method renamed to {@link org.apache.juneau.rest.client.RestCall#body(Object)} to match OpenAPI terminology.
RestClient
and RestClientBuilder
protected so that they can be subclassed.
RestClient.getRemoteableProxy()
methods have been split into separate methods for Remote Interfaces and Remote Resources:
RestClient.getRemoteInterface(Class)
RestClient.getRemoteInterface(Class,Object)
RestClient.getRemoteInterface(Class,Object,Serializer,Parser)
files
:
files
directory in the working directory,
and you have access to modify the CSS theme files.
SwaggerUI.css
file controls the look-and-feel of the Swagger UI, so you can make
modification there as well.
BasicRestConfig
interface (which defines the default settings for the BasicRestServlet
class)
now looks like this...
PoweredByApache
widget which used to serve as a page footer has been eliminated.
This release contains mostly bug fixes. Code changes have been made to preserve binary backwards compatibility with 7.1.0.
@JsonSchema
annotation has been merged with the {@link org.apache.juneau.jsonschema.annotation.Schema @Schema} annotation.
ReaderResource
and StreamResource
classes have been moved to the org.apache.juneau.http
package in juneau-marshall
. This allows them to be used as return types in remote REST interfaces.
juneau-svl
package.
RemoteInterfaceServlet
class has been renamed to {@link org.apache.juneau.rest.remote.RrpcServlet}.
@RestMethod (name="PROXY" )
has been changed to @RestMethod (name="RRPC" )
.
RestClient.getRemoteInterface()
method has been renamed to {@link org.apache.juneau.rest.client.RestClient#getRrpcInterface(Class)}.
@RemoteMethod (path)
values containing '/' characters were erroneously being encoded.
This release contains minor bug fixes and general improvements to the PetStore sample application.
This release primarily cleans up deprecated APIs from the 7.2.0 release.
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:
juneau-rest
juneau-rest-client
juneau-rest-server
juneau-rest-server-jaxrs
juneau-rest-server-rdf
juneau-rest-server-springboot
- New Spring Boot integration support classes.
juneau-microservice
juneau-microservice-core
- New. Contains base {@link org.apache.juneau.microservice.Microservice} class.
juneau-microservice-jetty
- New. Contains new {@link org.apache.juneau.microservice.jetty.JettyMicroservice} class.
juneau-my-jetty-microservice
- New. Template starter project for Jetty-based microservices.
juneau-my-springboot-microservice
- New. Template starter project for Spring-Boot-based microservices.
juneau-examples
juneau-core
juneau-microservice-rest
- Now contains only servlet example classes. No Jetty configuration.
juneau-microservice-rest-jetty
- Servlet examples deployed using Jetty.
juneau-microservice-rest-springboot
- Servlet examples deployed using Spring Boot.
RestMicroservice
.
app.json
and Procfile
files for deploying examples into Heroku.