© Bakhtiar Zein/Shutterstock.com
RESTful APIs mittels OData

OData - die Serverseite


Im ersten Teil dieser zweiteiligen Artikelreihe [1] habe ich den OData-Standard für RESTful APIs vorgestellt. Mittels OData ist die Objektadressierung, Suche, Filterung oder die Abbildung eines Objekts definiert. In diesem Teil möchte ich nun zeigen, wie solche OData-Services erstellt werden können. Dazu stelle ich die Bibliothek Apache Olingo und den SAP-Frontend-Server vor.

Ein API-Standard bringt nichts, wenn es keine Implementierungen davon gibt. Tatsächlich ist es eigentlich erst ein Standard, wenn es mehrere Implementierungen gibt. Das ist bei OData der Fall; eine Übersicht über die Implementierungen für Java, .NET, JavaScript, C++ und weitere Plattformen ist auf der OData-Seite zu finden [2]. Ich stelle nachfolgend Apache Olingo [3] vor – eine Bibliothek, um in Java einen OData-Service zu implementieren, die auch von großen Unternehmen wie SAP eingesetzt wird. Die Bibliotheksversionen sind sich im Großen und Ganzen sehr ähnlich. Ich stelle deshalb das Grundprinzip vor; Details und Tutorials gibt es auf deren Homepage.

Apache Olingo

Es gibt drei Versionen von Apache Olingo: eine für die OData-Implementation der Version 2, eine für die Version 4 und eine JavaScript-Bibliothek. Als Erstes stelle ich das Grundprinzip anhand der Bibliothek in Version 4 vor. Weiter unten gehe ich dann noch auf die Version 2 ein.

In den Beispielen auf der Olingo-Homepage läuft der Service auf einem Tomcat-Server. Für das Build-Management wird Maven verwendet. Ich nutze als Codebeispiel das Beispiel aus der Olingo-Dokumentation, dort ist auch eine Click-by-Click-Anleitung dabei. Deshalb gehe ich nicht weiter auf Tomcat und Maven ein, sondern beschränke mich hier auf die OData-spezifischen Aspekte. Unser Beispielservice beinhaltet Produkte. Dazu schauen wir uns die Metadaten an (Listing 1).

Listing 1

<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0"> <edmx:DataServices> <Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="OData.Demo"> <EntityType Name="Product"> <Key> <PropertyRef Name="ID"/> </Key> <Property Name="ID" Type="Edm.Int32"/> <Property Name="Name" Type="Edm.String"/> <Property Name="Description" Type="Edm.String"/> </EntityType> <EntityContainer Name="Container"> <EntitySet Name="Products" EntityType="OData.Demo.Product"/> </EntityContainer> </Schema> </edmx:DataServices> </edmx:Edmx>

In der Regel selektieren wir ein Set von Entitäten oder eine spezifische Entität mit einem Schlüssel. Bei dieser Selektion gibt es zwar Selektionsparameter wie z. B. Filter, aber schlussendlich sind die Entität und deren Set das zentrale Herzstück. Ein weiterer Angelpunkt sind die Metadaten der Entitäten. Also müssen wir diese zwei wesentlichen Teile irgendwo implementieren. Starten wir mit der Servicedefinition. Dazu gibt es die Klasse CsdlAbstractEdmProvider (Abb. 1).

friberg_odata_1.tif_fmt1.jpgAbb. 1: Klasse „CsdlAbstractEdmProvider“

Wir erstellen eine Klasse, die von CsdlAbstractEdmProvider erbt und die in Abbildung 1 markierten Methoden implementiert:

  • getEntityType(): Hier definieren wir, welche Attribute die Entität hat (Metadaten) (Listing 2).

  • getEntitySet(): Diese Methode definiert das Entitätenset (Metadaten) (Listing 3).

  • getSchemas(): Ist die Wurzel des Service, beinhaltet also die Entitäten (Listing 4).

  • getEntityContainerInfo(): Definiert, was ausgegeben werden soll, wenn nur der Service ohne weitere Angaben aufgerufen wird (<url>/odata.svc/) (Listing 5).

  • getEntityContainer(): Das ist das Wurzelelement, das alle Entitätensets beinhaltet (Listing 6).

Listing 2

@Override public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) { if(entityTypeName.equals(ET_PRODUCT_FQN)){ //create EntityType properties CsdlProperty id = new CsdlProperty().setName("ID").setType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName()); CsdlProperty name = new CsdlProperty().setName("Name").setType(EdmPrimitiveTypeKind.String.getFullQualifiedName()); CsdlProperty description = new CsdlProperty().setName("Description").setType(EdmPrimitiveTypeKind.String.getFullQualifiedName()); // create CsdlPropertyRef for Key element CsdlPropertyRef propertyRef = new CsdlPropertyRef(); propertyRef.setName("ID"); // configure EntityType CsdlEntityType entityType = new CsdlEntityType(); entityType.setName(ET_PRODUCT_NAME); entityType.setProperties(Arrays.asList(id, name , description)); entityType.setKey(Collections.singletonList(propertyRef)); return entityType; } return null; }

Listing 3

@Override public CsdlEntitySet getEntitySet(FullQualifiedName entityContainer, String entitySetName) { if(entityContainer.equals(CONTAINER)){ if(entitySetName.equals(ES_PRODUCTS_NAME)){ CsdlEntitySet entitySet = new CsdlEntitySet(); entitySet.setName(ES_PRODUCTS_NAME); entitySet.setType(ET_PRODUCT_FQN); return entitySet; } } return null; }

Listing 4

@Override public List<CsdlSchema> getSchemas() { // create Schema CsdlSchema schema = new CsdlSchema(); schema.setNamespace(NAMESPACE); // add EntityTypes List<CsdlEntityType> entityTypes = new ArrayList<CsdlEntityType>(); entityTypes.add(getEntityType(ET_PRODUCT_FQN)); schema.setEntityTypes(entityTypes); // add EntityContainer schema.setEntityContainer(getEntityContainer()); // finally List<CsdlSchema> schemas = new ArrayList<CsdlSchema>(); schemas.add(schema); return schemas; }

Listing 5

@Override public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerName) { if(entityContainerName == null || entityContainerName.equals(CONTAINER)){ CsdlEntityContainerInfo entityContainerInfo = new CsdlEntityContainerInfo(); entityContainerInfo.setContainerName(CONTAINER); return entityContainerInfo; } return null; }

Listing 6

@Override public CsdlEntityContainer getEntityContainer() { // create EntitySets List<Csd...

Neugierig geworden?

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang