I
- "implementation" (Iced) class for this schemaS
- reference to self: this should always be the same class as being declared. For example:
public class TimelineV3 extends Schema<Timeline, TimelineV3>
public abstract class Schema<I extends Iced,S extends Schema<I,S>> extends Iced
Schema is a primary interface of the REST APIs: all endpoints consume some schema object as an input, and produce another schema object as an output (though some endpoints may return nothing).
Schemas, as an external interface, are required to be stable: fields may not be renamed or removed, their types or meaning may not change, etc. It is allowed to add new fields to a schema, provided that they are optional, and that their default values correspond to the old behavior of the endpoint. If these requirements cannot be met, then a new version of a schema class must be created.
Many schemas are in direct correspondence with H2O objects. For example, JobV3 schema represents the Job object.
These "representative" Iced objects are called "implementation" or "impl", and are parametrized with type I.
Such representation is necessary in order to ensure stability of the interface: even as Job class evolves, the
interface of JobV3 schema must not. In the simplest case, when there is 1-to-1 correspondence between fields in
the impl class and in the schema, we use reflection magic to copy those fields over. The reflection magic is smart
enough to perform simple field name translations, and even certain type translations (like Keyed objects into Keys).
If there is no such correspondence, then special type adapters must be written. Right now this is done by
overriding the fillImpl
and fillFromImpl
methods. Usually they will
want to call super to get the default behavior, and then modify the results a bit (e.g., to map differently-named
fields, or to compute field values). Transient and static fields are ignored by the reflection magic.
There are also schemas that do not correspond to any H2O object. These are mostly the input schemas (schemas used for inputs of api requests). Such schemas should be "implemented" by Iced.
All schemas are expected to be self-documenting, in the sense that all fields within those schemas should carry
detailed documentation about their meaning, as well as any additional hints about the field's usage. These should
be annotated using the @API
interface. If a schema contains a complicated object, then that object
itself should derive from Schema, so that its fields can also be properly documented. However if the internal
object is sufficiently simple (say, a Map), then it may be sufficient to document it as a whole and have it derived
from Iced, not from Schema.
Schema names (getSimpleName()) must be unique within an application. During Schema discovery and registration there are checks to ensure this. Also there should be at most one Schema class per implementation class per version (with the exception of schemas that are backed by Iced).
For V3 Schemas each field had a "direction" (input / output / both), which allowed us to use the same schema as both input and output for an endpoint. This is no longer possible in V4: two separate schema classes for input / output should be created.
Handler
creates an input schema from the body/parameters of the HTTP request (using
fillFromParms()
, and passes it on to the corresponding handler method.
Each handler method may modify the input schema and return it as the output schema (common for V3 endpoints, should be avoided in V4).
Alternatively, a handler method may create a new output schema object from scratch, or from an existing implementation object.
Most Java developers need not be concerned with the details that follow, because the framework will make these calls as necessary.
Some use cases:
To find and create an instance of the appropriate schema for an Iced object, with the given version or the highest previous version:
Schema s = Schema.schema(6, impl);
To create a schema object and fill it from an existing impl object (the common case):
S schema = MySchemaClass(version).fillFromImpl(impl);or more generally:
S schema = Schema(version, impl).fillFromImpl(impl);To create an impl object and fill it from an existing schema object (the common case):
I impl = schema.createImpl(); // create an empty impl object and any empty children schema.fillImpl(impl); // fill the empty impl object and any children from the Schema and its childrenor
I impl = schema.createAndFillImpl(); // helper which does schema.fillImpl(schema.createImpl())
Schemas that are used for HTTP requests are filled with the default values of their impl class, and then any present HTTP parameters override those default values.
To create a schema object filled from the default values of its impl class and then overridden by HTTP request params:
S schema = MySchemaClass(version); I defaults = schema.createImpl(); schema.fillFromImpl(defaults); schema.fillFromParms(parms);or more tersely:
S schema = MySchemaClass(version).fillFromImpl(schema.createImpl()).fillFromParms(parms);
Constructor and Description |
---|
Schema()
Default constructor; triggers lazy schema registration.
|
Schema(I impl)
Create a new Schema instance from an existing impl object.
|
Modifier and Type | Method and Description |
---|---|
I |
createAndFillImpl()
Convenience helper which creates and fills an impl object from this schema.
|
I |
createImpl()
Create an appropriate implementation object and any child objects but does not fill them.
|
static int |
extractVersionFromSchemaName(java.lang.String clz_name)
Extract the version number from the schema class name.
|
S |
fillFromImpl(I impl)
Fill this Schema from the given implementation object.
|
protected S |
fillFromImpl(I impl,
java.lang.String[] fieldsToSkip) |
S |
fillFromParms(java.util.Properties parms)
Fill this Schema object from a set of parameters.
|
S |
fillFromParms(java.util.Properties parms,
boolean checkRequiredFields)
Fill this Schema from a set of (generally HTTP) parameters.
|
I |
fillImpl(I impl)
Fill an impl object and any children from this schema and its children.
|
protected I |
fillImpl(I impl,
java.lang.String[] fieldsToSkip) |
java.lang.Class<I> |
getImplClass()
Return the class of the implementation type parameter I for this Schema.
|
static java.lang.Class<? extends Iced> |
getImplClass(java.lang.Class<? extends Schema> clz)
Return the class of the implementation type parameter I for the
given Schema class.
|
java.lang.String |
getSchemaName() |
java.lang.String |
getSchemaType() |
int |
getSchemaVersion()
Get the version number of this schema, for example 3 or 99.
|
protected void |
init_meta() |
java.lang.StringBuffer |
markdown(boolean include_input_fields,
boolean include_output_fields)
Generate Markdown documentation for this Schema possibly including only the input or output fields.
|
java.lang.StringBuffer |
markdown(SchemaMetadata meta,
boolean include_input_fields,
boolean include_output_fields)
Generate Markdown documentation for this Schema, given we already have the metadata constructed.
|
static <T extends Schema> |
newInstance(java.lang.Class<T> clz)
Returns a new Schema instance.
|
protected static Schema |
newInstance(java.lang.String schema_name)
For a given schema_name (e.g., "FrameV2") return an appropriate new schema object (e.g., a water.api.Framev2).
|
static <T extends Schema> |
setField(T o,
java.lang.reflect.Field f,
java.lang.String key,
java.lang.String value,
boolean required,
java.lang.Class thisSchemaClass)
Safe method to set the field on given schema object
|
void |
setSchemaType_doNotCall(java.lang.String s) |
asBytes, clone, copyOver, frozenType, read, readExternal, readJSON, reloadFromBytes, toJsonString, write, writeExternal, writeJSON
public Schema()
H2OFailException
- if there is a name collision or
there is more than one schema which maps to the same Iced classpublic Schema(I impl)
impl
- protected void init_meta()
public static int extractVersionFromSchemaName(java.lang.String clz_name)
public int getSchemaVersion()
public java.lang.String getSchemaName()
public java.lang.String getSchemaType()
public void setSchemaType_doNotCall(java.lang.String s)
public I createImpl()
For objects without children this method does all the required work. For objects with children the subclass will need to override, e.g. by calling super.createImpl() and then calling createImpl() on its children.
Note that impl objects for schemas which override this method don't need to have a default constructor (e.g., a Keyed object constructor can still create and set the Key), but they must not fill any fields which can be filled later from the schema.
TODO: We could handle the common case of children with the same field names here by finding all of our fields that are themselves Schemas.
public I fillImpl(I impl)
public final I createAndFillImpl()
public S fillFromImpl(I impl)
public static java.lang.Class<? extends Iced> getImplClass(java.lang.Class<? extends Schema> clz)
public java.lang.Class<I> getImplClass()
public S fillFromParms(java.util.Properties parms)
parms
- parameters - set of tuples (parameter name, parameter value)fillFromParms(Properties, boolean)
public S fillFromParms(java.util.Properties parms, boolean checkRequiredFields)
Using reflection this process determines the type of the target field and conforms the types if possible. For example, if the field is a Keyed type the name (ID) will be looked up in the DKV and mapped appropriately.
The process ignores parameters which are not fields in the schema, and it verifies that all fields marked as required are present in the parameters list.
It also does various sanity checks for broken Schemas, for example fields must not be private, and since input fields get filled here they must not be final.
parms
- Properties map of parameter valuescheckRequiredFields
- perform check for missing required fieldsH2OIllegalArgumentException
- for bad/missing parameterspublic static <T extends Schema> void setField(T o, java.lang.reflect.Field f, java.lang.String key, java.lang.String value, boolean required, java.lang.Class thisSchemaClass) throws java.lang.IllegalAccessException
o
- schema object to modifyf
- field to modifykey
- name of field to modifyvalue
- string-based representation of value to setrequired
- is field required by APIthisSchemaClass
- class of schema handling this (can be null)java.lang.IllegalAccessException
public static <T extends Schema> T newInstance(java.lang.Class<T> clz)
protected static Schema newInstance(java.lang.String schema_name)
public java.lang.StringBuffer markdown(boolean include_input_fields, boolean include_output_fields)
H2ONotFoundArgumentException
- if reflection on a field failspublic java.lang.StringBuffer markdown(SchemaMetadata meta, boolean include_input_fields, boolean include_output_fields)
H2ONotFoundArgumentException
- if reflection on a field fails