Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #53725

Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema

Newsgroups comp.lang.java.programmer
From Ross Finlayson <ross.a.finlayson@gmail.com>
Subject Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema
Date 2024-04-19 09:30 -0700
Message-ID <2imdnULqbvFsBr_7nZ2dnZfqn_qdnZ2d@giganews.com> (permalink)

Show all headers | View raw


Signature API

ccc.api
ccc.api.exception
ccc.api.fault

ccc.api.client

ccc.springsup.web

ccc.serdes


Api ApiMapping

ApiFlt ApiHardFlt
OtherCauseFault CovariantFault

Pth Hdr Qpa Req Rsp Flt Err


The other day I was writing a Spring Boot server,
and the implementation of the Api looks like this

For a service Srv, group of APIs Grp, and API Api

public interface Srv {
	interface Grp {
		interface Api extends ccc.api.Api {

			static class Pth{}
			static class Qpa{}
			static class Hdr{}
			static class Req{}
			static class Rsp{}
			static class Flt{}
			static class Err{}


			Rsp api(
				@Valid @NotNull Pth pth,
				@Valid @NotNull Qpa qpa,
				@Valid @NotNull Hdr hdr,
				@Valid @NotNull Req req
			) throws ApiFlt;

		}
	}

}

The validation annotations have to be on the root type,
so, they go on the Api.

public interface SrvMapping {
	interface Grp {
		@RequestMapping
		interface Api extends Srv.Grp.Api, ApiMapping {

			@Override
			@GetMapping("/api/{pth}")
			@ResponseBody
			Rsp api(
				@PathVariable Pth pth,
				@QueryParam Qpa qpa,
				@RequestHeader Hdr hdr,
				@RequestBody Req req
			) throws ApiFlt;

		}
	}
}

The request mapping and also the openapi or documentation
interfaces have to be on the interface the controller implements,
and they don't mean anything to the Api, so they go on the ApiMapping.

@Controller
public class SrvController implements SrvMapping.Grp.Api
{
			@Override
			Srv.Grp.Rsp api(
				Srv.Grp.Pth pth,
				Srv.Grp.Qpa qpa,
				Srv.Grp.Hdr hdr,
				Srv.Grp.Req req
			) throws ApiFlt;
}

The controller just implements the Api, Spring Web wires
it up and Openapi documents it up.

And it results sort of thusly a very simple organization of APIs.

public interface Srv {

	interface Grp1 {
		interface Api1 { /* */ }
		interface Api2 { /* */ }
		interface Api3 { /* */ }
		interface Api4 { /* */ }
	}
	interface Grp@ {
		interface Api1 { /* */ }
		interface Api2 { /* */ }
		interface Api3 { /* */ }
		interface Api4 { /* */ }
	}
	/* */
}

The key is that given those, the Api and ApiMapping an entire
Signature, of the mapping, sort of results, with adding this
sort of ApiStatusMapping, to relate the HTTP status codes,
with, the covariant return bodies, then it's sort of all one thing.

("a functional interface may still have multiple default methods ...")

The idea is that the interfaces carry very well down from these.

For most often usual "APIs" these days it'd often look:

@Data
static class MyGrp {

}

public interface Srv {
	interface Grp {
		interface Get1 extends ccc.api.Api {

			static class Pth{ String myGrpId;}
			static class Req extends MyGrp{};
			static class Rsp{}
			static class Flt{}
			static class Err{}

			Rsp get1(
				@Valid @NotNull Pth pth,
				@Valid @NotNull Req req
			) throws ApiFlt;

		}
		interface Put1 extends ccc.api.Api {

			static class Pth{ String myGrpId;}
			static class Req extends MyGrp{}
			static class Rsp{}
			static class Flt{}
			static class Err{}

			Rsp put1(
				@Valid @NotNull Pth pth,
				@Valid @NotNull Req req
			) throws ApiFlt;

		}
	}

}


public interface SrvMapping {
	interface Grp {
		@RequestMapping
		interface Api extends Srv.Grp.Get1, ApiMapping {

			@Override
			@GetMapping("/grp/{myGrpId}")
			@ResponseBody
			Rsp get1(
				@PathVariable("myGrpId") Pth pth,
				@RequestBody Req req
			) throws ApiFlt;

		}
		@RequestMapping
		interface Api extends Srv.Grp.Put1, ApiMapping {

			@Override
			@PutMapping("/grp/{myGrpId}")
			@ResponseBody
			Rsp put1(
				@PathVariable("myGrpId") String myGrpId,
				@RequestBody Req req
			) throws ApiFlt;

		}
	}
}


Then, an issue involves that the Pth Hdr Qpa Req are distinct
types, among all the APIs, though they just extend the common
types.  Then, those usually being the outside of the document,
so, not "in" the document as it were, all the members just get
copied among those, interchangeably, while the types in the
signatures, keep each API's types distinct.  It results quite a few
types yet that also makes it great as each simply can be referred
to by its class constant, and it totally makes it so that common
behavior is a simple matter of extension.

Relating the HTTP status codes to the resulting response bodies,
is for the covariant return types, or fault, and how to make a
convention in the exceptions,


The Signature, Api + ApiMapping, then, basically encapsulates everything 
there is to make an HTTP request and interpret an HTTP response 
according to the Api validation indicating values and ranges
and ApiMapping mapping indicating HTTP request and response
semantics.

Then, the covariant response semantics, is to get figured out,
to make it so that the exception types have a neat little convention
to extract according to response status and headers, the response
body, besides the usual case the success case.

For responses with no content, it's figured to have a distinguished
Rsp type or just to return void.

For common types then also is for common semantics, then as
with regards to those resulting the "schema" as what it is,
though that this is only about "interface first" as it were
with not much care except in the Java ecosystem, then as
with regards to Spring Web semantics and, JAX-RS semantics, say.

Api
ApiMapping <- Spring Web
ApiRsmapping <- JAX-RS Jersey


Hdr
Pth
Mtx // "MatrixVariable"
Qpa
Req
Rsp
Err
Cov1
Cov2

Then, how to associate status with the Covariant responses,
is sort of for annotations and repeated annotation, that relate
statuses, to, response bodies, then of the various kinds of response
bodies, then to sort of adopt the OneOf of the "Variant" return
types, among those.

class CovariantFault extends ApiFlt

@Success( status = 200)
@Success(status = 201, result = Accepted201.class)
@Success(status = 204, result = NoContent204.class)

@Covariant(status = 400, result = Error.class)
@Covariant(range = "4xx", result = Error.class)
class ErrorFault extends CovariantFault { static class covariantClass = 
Error.class;}

@Covariant(status = 404, result = NotFound404.class)
class NotFoundException extends NovariantFault {}

@Covariant(range = "5xx", result = RetryOrCircuitBreak.class)
class BackendException extends CovariantFault {}

The idea is to sort of just inspect the interface for its members,
then find off of those the status.  Then, inspect the interface
for its exceptions, and make what results for those the covariant
return values, and whatever other cases result from invocation.

@ResultSuccess(status = 200) @ResultCovariant(result = {Cov1.class, 
Cov2.class})

@ResultCovariant(status = 400, range = "4xx", result = Error.class, 
exception = ErrorFlt.class)


public interface Srv {
	interface Grp {
		interface Api extends ccc.api.Api {

			static class Pth{}
			static class Qpa{}
			static class Hdr{}
			static class Req{}
			static class Rsp{}
			static class Flt{}
			static class Err extends Error {}
			static class ErrFlt extends ErrorFault{}
			static class Cov extends Error {}
			static class CovFlt extends CovariantFault{ static class 
covariantClass = Cov.class;}


			Rsp api(
				@Valid @NotNull Pth pth,
				@Valid @NotNull Qpa qpa,
				@Valid @NotNull Hdr hdr,
				@Valid @NotNull Req req
			) throws ApiFlt, ErrFlt, CovFlt;

		}
	}

}


For aligning with OpenApi, then there's a default range named "default", 
then whether the 2xx, 3xx, 4xx, 5xx ranges are named like 3XX or 3xx. 
(Upper case, like '5XX'.)  Then the idea would be that usual sorts of 
unit tests check that the annotation match for the ApiMapping among the 
Jersey or Spring RequestMappings, the OpenApi documentation and type 
declarations, then those all being defined simply enough as a usual sort 
of HTTP Api with Pth, Hdr, Qpa, Req, Rsp, then Flt, Err, Cov, and so on.


Anyways I got it running this way and it works pretty good,
works just great with plain vanilla framework, and today's.

Then, for the types, is sort of to indicate the Inner and Outer or
Inward and Outer types, and for branching, with the idea then
that the relations of types are defined first by inheritance for
the composition of their contents, then as with regards to
the document, basically for JSON and XML whether the document
has a root in it like XML or is a branching like JSON (or that most
usually it's a "JSON object" vis-a-vis plain values in JSON).

This gets into two major cases, where there are alternatives
on the outside, and where there are alternatives in the members,
with regards to "any" and these kinds of things.

Basically this seems for whenever extending a class, to provide
it its own type for differentiation and declaration, whether it
still is to be considered that it "is" the parent class, so that,
it's really of a copy-constructor in semantics, in terms of
assigning it values of the parent object,

Superclass superclass = subclass ; // is assignable

Subclass subclass = superclass ; // not assignable


Then, the idea of declaring "Subclass" and "Superclass",
or "Sub" and "Sup", is to make it so that it results sort
of the thing.

I.e., the object that only extends to establish its own
type in the interface and signature, is to have some sort
idiom to result that what it extends, is assignable to it,
that this would be overloading "=" assignment, in a sense,
for the superclass, as an instance of (the values in the structure
of) the subclass.

Superclass superclassInstance = subclassInstance; // is assignable

So, the idea is to implement a method named sup,
on these extensions, that accepts a superclass,
and returns an instance of the same-shape sub-class
for assignment.

Subclass subclassInstance = Subclass.sub(superclassInstance);

I.e., the "is-a" relation is reflective, or for, "asA", ..., "ofA".

Subclass subclassInstance = Subclass.ofA(superclassInstance);


For openApi then it seems for populating subtypes, but mostly
is the idea is that the Api Signature annotations will indicate
what openapi annotations get create, then to employ those.


class org.PojoXsdDefinedType {}

class MyPojo extends PojoXsdDefinedType {}

The idea is that inside the framework, each Api has its own type,
but on the wire, the XML-style with the document in the root,
or the JSON-style with the document projected into the root,
are two different styles, and the Serializer and Deserializer of
the bodies or payloads, make for marking for an object what
is its payload, then that the framework makes and returns the
things, so it is simple and thorough overall, while not much
encumbered or intrusive, and not very tightly-coupled, except
of course all defined well according to types.

Java has single inheritance of classes and multiple inheritance
of interfaces, with regards to patterns like diamond and so
on.  Pretty much all the usual surround of Pojos are classes,
vis-a-vis Poji's or plain-old-Java-interfaces.


class MyGrp {
	int id; // notOnTheCreate
	String name;
	List<Integer> memberIndividuals;
	List<Integer> memberGroups;

	String onlyOnTheCreate;
	String onlyOnTheUpdate;
	String onlyOnTheGet;
}

The idea is that often the POJO will have various fields in it,
and the validation or constraints, only apply with regards to
being a request or response, and they can't be differentiated
exactly and only by the type.

class MyGrp {
	int id; // prefer Integer or boxed types everywhere, so, validation
	String name;
	List<Integer> memberIndividualIds;
	List<Integer> memberGroupIds;

}

class MyGrpCreateReq extends MyGrp {
	String onlyOnTheCreate;
}
class MyGrpUpdateReq extends MyGrp {
	String onlyOnTheUpdate;
}

class MyGrpGetRsp extends MyGrp {
	String onlyOnTheGet;
}


There isn't a way to make a sub-class override the validation 
constraints, about jakarta.validation nee javax.validation, where it's 
figured exactly that the XSD constraints and the javax.validation 
constraints go on the one POJO definition once then are to be same and 
re-used throughout.

(Anybody know a good XJC plugin that translates the XSD constraints
exactly to javax.validation constraints?  There's this krasa bit I haven't.)

So, Java can't implement the "Diamond" in this sense, or, subclasses
of course inherit all members of superclasses, about what it results
then to just leave the constraints off the fields of the superclass
that are different in subclasses, with regards to "Mixins", that usually 
the goal would be to add a Mixin of onlyOnTheRequest and
onlyOnTheResponse apiece, ..., then with regards to that the
Api methods are in these types, and then that the external
Signature thoroughly encloses what all's in all the Apis among
a bunch of Apis, according to these ApiMappings, for XML and JSON.

About Mixins, the most usual sort of thing is BeanUtil copyProperties,
figuring that here that's just an associative-array of objects pretty much,

<SUB,SUP super SUB> Mixin.copyInto(SUB sub, Class<SUP super SUB> 
supClass, SUP sup);

<SUB, SUP super SUB> SUB Mixin.construct(Class<SUB> subClass, SUP sup);


About Xjc, basically the idea is this:  a very slim xjc output, fields 
only, then that adds lombok annotations to the output classes (yeah, I 
know), so that the JAX/B binding annotations are on the fields, and then 
the lombok annotations go on the classes, then perhaps with some of the 
jackson annotations on the fields/classes, but mostly the 
javax.validation annotations on the fields/classes.

The XJC has a cool convention on List members, doesn't generate setters
by default, and always initializes empty (and, non-null) list members.
The is about the "otherwise behaviorless" structure, what the behavior
of access to list members should be, List or Map, or as with regards
to "getters" and "setters", "adders" and "putters".  A usual idea is
that "if it's null it's null", very usual.

It's sort of like "isn't there something since XJC to translate XSD 
schemas into suitable POJO's for JAX/B", and it's like, "XJC is about 
one of the greatest things with regards to anything to do with Java, 
XML, and JAX/B". XJC and JCodeModel are pretty much great.

https://github.com/kohsuke/jaxb/blob/master/jaxb-ri/xjc/src/main/java/com/sun/tools/xjc/ModelLoader.java


Then, it sort of seems like for making a slimmed-down inspired-by-Xjc 
sort of thing, yet, there's still for any sort of WSDL using wscompile 
or this sort of thing, about, XML and XSD, and SOAP and WS, ..., which 
is very usual.


It's pretty much POJO's everywhere, though I'd be a big fan of POJI's,
but pretty much it sort of results that for plain-old-data (and no 
behavior at all except as structured data in values), POJO's everywhere, 
..., here with the goals of declarative type-safety and validation, 
while, not exploding the number of types, and, surfacing the relations 
of inheritance of types, out of what results the framework into schema.

POJO's everywhere pretty much have "and it must be private fields and
getters/setters everywhere because the static analysis first thinks so", 
should be that whatever results from Schema the derived POJOs,
and vice-versa, should mostly work off the fields and their annotations, 
what with the getters and setters then being gratuitous in the 
definition.  (This is that any non-static member field is an otherwise 
behaviorless property, and that any conversions or coercions are either 
only boxing in the language or outside on the serialization and 
validation, type conversion and coercion.)

There's mostly only one framework serializer in Spring about JSON,
and it's FasterXML Jackson, while for XML, mostly one would rely
on JAX/B annotations to reflect the richer structure and schema of XML.
In Spring Web, there's also to be considered the 
MethodArgumentResolvers, about the corner case of headers and query 
parameters whose keys aren't usual Java beans identifiers, and clubbing 
those into the one or multiple Hdr or Qpa API Signature parameters, 
above, because the usual RequestParam and RequestHeader don't have one 
and MultiValueMap and so on, and KeyValuePairArray and KvpLiteral or the 
least-needful to model the API Mapping.

That Java had a plain "Struct" type, ..., though the "record" class 
object is pretty first-class, ..., and most Bean-like property copiers 
will discover the getters of the properties thusly, ..., has mostly that 
the getters are named "get" and the setters named "set", ..., that 
there's something to be said for POJI's on records as immutables then 
just adding setters as the property name field1(String 1 f1).

Then again some people want to replace _every single instance of String_ 
with a strongly typed language of the String as a CharSequence, .... 
Or, at least I do, with an overall approach to Strings, Enums, and in 
CharSequences.

Schema is one thing that seems missing the most in all the modern-ish
HTTP-JSON-RESTy world, which is good and bad.  Luckily the whole XML
with XSD schemas really set a great example of how it should be done,
or rather, what use-cases it should fulfill.  So, it's to be expected 
that at some point JSON schema finally gets into the toolchain, because
what's derived should be derived.

So, about POJOs, they're pretty much fungible, and pretty much 
ubiquitous.  While that may be so, the construct-and-build and 
stream-and-read use-cases are pretty much entirely different, for given 
types, what one would hope would result POJI's, that basically for a 
given POJO, it has two POJI's, the immutable part of the getters and the 
mutable part of the setters.


class Pojo{
	String p;

	String getP() { return p; }
	void setP(String p) }{ this.p = p;}
}

interface PojiGet {
	String p();
}

interface PojiSet {
	void p(String p);
}

class PojoPojied implements PojoGet, PojoSet {

}

record PojoRecord implements PojoGet {
	String p;
}

The serialization and deserialization frameworks pretty much expect
their various no-arg and all-arg constructors, about the other great
boojum of structured data in an associate-array class property world,
property order.

A usual sort of idea is to go through a world of POJOs that exist,
because they're everywhere and model all the things, and make
a "derived" framework of Pojis the getters and setters for them,
of a sort of "Imm" and "Mut" as it were, and "Get" and "Set",
and making constructors and property order and builders then
for whether builders result mutable or immutable, these kinds of
things.


class Pojo {
	String p;
	String getP() { return p;}
	void setP(String p) { this.p = p; }
}

interface PojiGetter {
	P p();
}

interface PojiSetter {
	void p(String p);
}

interface PojiImmutable extends Setter {
	default void p(String p) { throw new UnsupportedOperationException(); }
}

interface PojiMutable extends Setter {
	// eg, override some of the otherwise immutable
}


class PojoPoji implements PojiGetter, PojiSetter {

	String p;

	PojoPoji(Pojo pojo) {
		this.p =
	}

	@Override
	String p() { return p;}

	@Override
	void p(String p) { this.p = p; }
}

record PojoRecordPoji implement PojiGetter {

	String p;

	PojoRecordPoji(Pojo pojo) {
		this(pojo.getP()); // propertyOrder for the initializer, ....
	}
}

So, it's sort of the idea of an APT tool, to find everything marked 
@lombok.Data, and derive and generate these things, then carry along the 
relevant @Json... annotations.

The property order is about the thing to get figured out, there's a
plethora of annotations relating it in java.beans, java.xml.bind,
the Jackson annotations, what Lombok makes, variously, all
should mean about same, and where it is so that -parameters
isn't necessarily so and that at runtime there isn't necessarily
byte-code introspection, only the language and runtime's
guaranteed reflections according to class, and record, and
java.lang.reflect, and java.lang.annotation.

Property order and one or the other of getP/setP or p-and/or-p.

Pretty much bog-standard and everywhere, ....


So, first there's the idea that HTTP APIs have a standard Signature,
pretty much a normative signature, then about what gets involved
as directly on the data structures to indicate their type relations,
anything extra the plain composition of their values as plain
old data objects, and "Beans" in a sense yet to distinguish from
both "J2EE Beans" and "Spring Beans", variously, and their notions
of the property editor and magic of J2EE beans vis-a-vis the module
instance name/type association of Spring Beans, just Pojo's and
Poji's, then plain getter/setter Pojo's and get/imm/mut Poji's.

Schema, ..., in the language.

Back to comp.lang.java.programmer | Previous | NextNext in thread | Find similar


Thread

Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema Ross Finlayson <ross.a.finlayson@gmail.com> - 2024-04-19 09:30 -0700
  Re: Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema Ross Finlayson <ross.a.finlayson@gmail.com> - 2024-04-19 10:22 -0700
    Re: Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema Ross Finlayson <ross.a.finlayson@gmail.com> - 2024-04-19 11:49 -0700
      Re: Java HTTP API Signatures, schemas, XML and JSON, Pojo's and Poji's, Java and Spring and schema Ross Finlayson <ross.a.finlayson@gmail.com> - 2024-04-19 13:48 -0700

csiph-web