Skip to content
Snippets Groups Projects
Commit afb85eac authored by Hallvard Trætteberg's avatar Hallvard Trætteberg
Browse files

Mer dokumentasjon

parent 00b21109
No related branches found
No related tags found
No related merge requests found
Pipeline #50587 passed
...@@ -8,22 +8,45 @@ Et REST-API er på en måte et objekt (eller sett med objekter) som leses og/ell ...@@ -8,22 +8,45 @@ Et REST-API er på en måte et objekt (eller sett med objekter) som leses og/ell
Denne modulen inneholder bare logikken til "REST-tjeneste"-objektet (**LatLongsService**), mens oppsettet av serveren ligger i [restserver-modulen](../restserver/README.md). En slik oppdeling gjør løsningen ryddigere (selv om det øker antall moduler). Denne modulen inneholder bare logikken til "REST-tjeneste"-objektet (**LatLongsService**), mens oppsettet av serveren ligger i [restserver-modulen](../restserver/README.md). En slik oppdeling gjør løsningen ryddigere (selv om det øker antall moduler).
REST-API-et vårt er programmert iht. [JAX-RS-standarden](https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services), som lar en angi koblingen mellom HTTP og metodene på REST-tjeneste-objektet vha. Java-annotasjoner. F.eks. angir @Path-annotasjonen på **LatLongsService**-klassen at vår tjeneste er knyttet til **latLongs**-prefikset. Altså vil vår tjeneste aktiveres ved å angi **latLongs** først i stien til URL-en (men bak stien til serveren). Anta f.eks. at stien til serveren er "http://min-server:8080/". Da vil stien til vår tjeneste være "http://min-server:8080/latLongs". REST-API-et vårt er programmert iht. [JAX-RS-standarden](https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services), som lar en angi koblingen mellom HTTP og metodene på REST-tjeneste-objektet vha. Java-annotasjoner. Tjenesteobjektet trenger faktisk ikke implementere noe spesifikt grensesnitt eller arve fra noen spesifikk superklasse, slik en servlet må. F.eks. angir @Path-annotasjonen på **LatLongsService**-klassen at vår tjeneste er knyttet til **latLongs**-prefikset. Altså vil vår tjeneste aktiveres ved å angi **latLongs** først i stien til URL-en (men bak stien til serveren). Anta f.eks. at stien til serveren er "http://min-server:8080/". Da vil stien til vår tjeneste være "http://min-server:8080/latLongs".
Som nevnt er JAX-RS *annotasjonsdrevet*. Det betyr at Java-annotasjoner ved hver metode angir detaljer ved forespørslen som trigger metoden. F.eks. vil en metode annotert med **@GET** bare kalles når HTTP-metoden er "GET". En kan bruke **@Path** for å angi et påkrevd sti-segment og **@PathParam** for å bruke dette sti-segmentet som argument til metoden. Som nevnt er JAX-RS *annotasjonsdrevet*. Det betyr at Java-annotasjoner ved hver metode angir detaljer ved forespørslen som trigger metoden. F.eks. vil en metode annotert med **@GET** bare kalles når HTTP-metoden er "GET". En kan bruke **@Path** for å angi et påkrevd sti-segment og **@PathParam** for å bruke dette sti-segmentet som argument til metoden.
Hvis metoden bruker ekstra data sendt i tillegg til URL-en, så angis formatet med **@Consumes**-annotasjonen. Tilsvarende angis kodingen av resultatet med **@Produces**-annotasjonen. Vi bruker json-kodete data, så derfor bruker vi **MediaType.APPLICATION_JSON** (konstant for "application/json") som argument for begge disse. Hvis metoden bruker ekstra data sendt i tillegg til URL-en, så angis formatet med **@Consumes**-annotasjonen. Tilsvarende angis kodingen av resultatet med **@Produces**-annotasjonen. Vi bruker json-kodete data, så derfor bruker vi **MediaType.APPLICATION_JSON** (konstant for "application/json") som argument for begge disse.
Vårt REST-API gir tilgang til *ett* **LatLongs**-objekt og tilbyr fem metoder: Vårt REST-API gir tilgang til *ett* **LatLongs**-objekt, som refereres til av **latLongs**-felter. **@Inject**-annotasjonen er vesentlig, den signaliserer at feltet (kan/skal) settes "automagisk" *utenifra* til et objekt av den angitte typen. Hvordan dette skjer styres av web-serveren som tjenesteklassen vår kjører på, se [restserver-modulen](../restserver/README.md). Slik "injeksjon" er en vanlig teknikk for å gjøre en klasse mer uavhengig av sammenhengen den inngår i.
REST-API-et tilbyr fem metoder:
* lese innholdet (alle **LatLong**-objektene) - GET til **tjeneste-URL** * lese innholdet (alle **LatLong**-objektene) - GET til **tjeneste-URL**
* lese innholdet til et spesifikt **LatLong**-element (angitt med posisjonen **num**) - GET (**@GET**) til **tjeneste-URL/num** (**@Path** og **@PathParam**) * lese innholdet til et spesifikt **LatLong**-element (angitt med posisjonen **num**) - GET (**@GET**) til **tjeneste-URL/num** (**@Path** og **@PathParam**)
* legge til LatLongs-objekter i spesifikk posisjon - POST (**@POST**) av json-kodet LatLong-array til **tjeneste-URL/num** (**@Path** og **@PathParam**) * legge til LatLongs-objekter - POST (**@POST**) av json-kodet LatLong-array til **tjeneste-URL**
* endre LatLong-objekt i spesifikk posisjon - PUT (**@PUT**) av json-kodet LatLong-objekt til **tjeneste-URL/num** (**@Path** og **@PathParam**) * endre LatLong-objekt i spesifikk posisjon - PUT (**@PUT**) av json-kodet LatLong-objekt til **tjeneste-URL/num** (**@Path** og **@PathParam**)
* fjerne LatLong-objekt i spesifikk posisjon - DELETE (**@DELETE**) til **tjeneste-URL/num** (**@Path** og **@PathParam**) * fjerne LatLong-objekt i spesifikk posisjon - DELETE (**@DELETE**) til **tjeneste-URL/num** (**@Path** og **@PathParam**)
Merk at navnet på implementasjonsmetoden i **LatLongsService**-klassen ikke spiller noen rolle, det er annotasjonene som er viktige. Merk at navnet på implementasjonsmetoden i **LatLongsService**-klassen ikke spiller noen rolle, det er annotasjonene som er viktige.
Her er et eksempel på forløp, hvor det antas at **LatLongs**-objektet er tomt i starten:
```plantuml
client -> LatLongsService: GET /latLongs
LatLongsService --> client: [ ]
client -> LatLongsService: POST /latLongs ~[[63.1, 11.2], [63.2, 11.0]]
LatLongsService --> client: 0
client -> LatLongsService: GET /latLongs/1
LatLongsService --> client: { "latitude": 63.2, "longitude": 11.0 }
client -> LatLongsService: PUT /latLongs/0 { "latitude": 64.0, "longitude": 9.1 }
LatLongsService --> client: 0
client -> LatLongsService: GET /latLongs
LatLongsService --> client: [ { "latitude": 64.0, "longitude": 9.1 }, { "latitude": 63.2, "longitude": 11.0 } ]
```
Først er altså **LatLongs**-objektet er tomt. Det legges så til to **LatLong**-objekter, som havner i posisjon 0, før **LatLong**-objekter i posisjon 1 hentes. Så endres **LatLong**-objektet i posisjon 0, før alle hentes. Legg merke til at en json-*array* med to tall, f.eks. **[63.1, 11.2]**, kan deserialiseres (leses) som et **LatLong**-objekt, men at det serialiseres (skrives) som et json-*objekt*, f.eks. **{ "latitude": 64.0, "longitude": 9.1 }**.
## JSON-koding av data ## JSON-koding av data
Når vi bruker **@Consumes** og **@Produces** med **MediaType.APPLICATION_JSON** som argument, så må vi også konfigurere tjenesten slik at kodingen av json-data bruker vår logikk, altså den som er programmert i [**core**-modulen](../core/README.md). Dette gjøres i **LatLongObjectMapperProvider**-klassen. Det er ikke så mye å si om den klassen, annet enn at den bruker **LatLongsModule**-klassen fra core-modulen. Når vi bruker **@Consumes** og **@Produces** med **MediaType.APPLICATION_JSON** som argument, så må vi også konfigurere tjenesten slik at kodingen av json-data bruker vår logikk, altså den som er programmert i [core-modulen](../core/README.md). Ellers kan det blir forskjell på logikken i JavaFX-app-en som sender forespørsler og og mottar svar, og REST-tjenesten, som mottar forespørsel og sender svar. Konfigureringen gjøres i **LatLongObjectMapperProvider**-klassen. Det er ikke så mye å si om den klassen, annet enn at den bruker den samme **LatLongsModule**-klassen fra core-modulen, som JavaFX-app-en bruker.
## Bygging med Gradle
REST-API-koden er satt opp som et prosjekt av typen **java-library**, siden det ikke er noe selvstendig program. Vi har avhengighet til JSON-biblioteket Jackson og JAX-RS-standarden. Vi trenger imidlertid ikke noen avhengighet til en implementasjon av denne standarden, den "byrden" dyttes over på [restserver-modulen](../restserver/README.md).
...@@ -67,3 +67,18 @@ public class LatLongsService { ...@@ -67,3 +67,18 @@ public class LatLongsService {
return latLongs.removeLatLong(num); return latLongs.removeLatLong(num);
} }
} }
/**
* @startuml
* client -> LatLongsService: GET /latLongs
* LatLongsService --> client: [ ]
* client -> LatLongsService: POST /latLongs ~[[63.1, 11.2], [63.2, 11.0]]
* LatLongsService --> client: 0
* client -> LatLongsService: GET /latLongs/1
* LatLongsService --> client: { "latitude": 63.2, "longitude": 11.0 }
* client -> LatLongsService: PUT /latLongs/0 { "latitude": 64.0, "longitude": 9.1 }
* LatLongsService --> client: 0
* client -> LatLongsService: GET /latLongs
* LatLongsService --> client: [ { "latitude": 64.0, "longitude": 9.1 }, { "latitude": 63.2, "longitude": 11.0 } ]
* @enduml
**/
...@@ -4,4 +4,27 @@ Dette prosjektet inneholder web-serveren med REST-api-et for [simpleexample2](.. ...@@ -4,4 +4,27 @@ Dette prosjektet inneholder web-serveren med REST-api-et for [simpleexample2](..
## Web-serveren ## Web-serveren
REST-tjenestelogikken i [restapi-modulen](../restapi/README.md) trenger en web-server for å bli gjort tilgjengelig. Vi må naturlig nok ha en web-server med støtte for [JAX-RS-standarden](https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services), siden det er den REST-API-logikken er programmert iht. Det er flere å velge mellom, vi har valgt å bruke [Jersey](https://eclipse-ee4j.github.io/jersey/), som er åpen kildekode (og nylig overført til Eclipse-stiftelsen).
Jersey er i seg selv ikke en komplett web-server, men bygger bro mellom en underliggende HTTP-tjener og JAX-RS-baserte REST-API-klasser. Derfor trenger vi også en ren HTTP-tjener, og av mange alternativer bruker vi her bruker vi [grizzly2](https://javaee.github.io/grizzly/).
Koden består av to "små" klasser:
* **LatLongConfig** registrerer klassene som utgjør og støtter REST-tjenesten vår.
* **LatLongGrizzlyApp** inneholder oppstartskode.
### LatLongConfig
Konfigurasjonsteknikken består i å oppgi hvilke klasser og evt. objekter som "samarbeider" om REST-tjenesten. Hvilken rolle disse spiller bestemmes av typene, Jersey sørger for å instansiere dem og koble instansene sammen. Detaljene trenger vi ikke å kjenne til, vi må bare vite hvilke klasser vi må registrere. Den viktigste er REST-tjenesteklassen **LatLongsService**, mens de to andre er støtteklasser.
Vi registrerer også et *objekt*, dette er det ene **LatLongs**-objektet som REST-tjenesten opererer på. Siden typen stemmer overens med det **@Inject**-annoterte *latLongs*-feltet i **LatLongsService**-klassen, vil det bli "injisert" (automagisk satt) og på den måten gjøres tilgjengelig for alle REST-API-metodene.
### LatLongGrizzlyApp
Denne klassen brukes bare ved oppstart av serveren, f.eks. hvis den kjøres som en selvstendig applikasjon. Klassen har egne metoder for å start og stoppe serveren, og disse brukes av den varianten av JavaFX som bruker REST-API-et. En slik server kjører som en uavhengig "tråd", slik at serveren ikke nødvendigvis er kommet helt i gang når koden vår får referansen til det nyopprettede **HttpServer**-objektet (returnert av kallet til **GrizzlyHttpServerFactory.createHttpServer**). Derfor har **startServer**-metoden bygget inn muligheten til å vente på at serveren svarer før metoden returnerer.
## Bygging med gradle ## Bygging med gradle
Avhengighetene viser at vi bruker en rekke rammeverk og biblioteker. Detaljene har vi lest oss frem til i [dokumentasjonen til Jersey](https://jersey.github.io/documentation/latest/deployment.html#deployment.http).
Versjonsnumrene er viktige, siden alt må spille sammen. For å gjøre det ryddigere er variabler med versjonsnumrene satt i en **ext**-blokk og angitt i identifikasjonsstrengen med $-notasjonen. Merk at for at versjonsvariabelverdiene skal bli "skutt inn" i strengen, så må en bruke " og ikke ' rundt strengen.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment