Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Commit

Permalink
Adding Java bindings. Fixes #65
Browse files Browse the repository at this point in the history
  • Loading branch information
antoniogarrote committed May 4, 2017
1 parent 9d88589 commit 0e36090
Show file tree
Hide file tree
Showing 44 changed files with 2,367 additions and 8 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,12 @@ $ lein jar
$ lein npm install # this is only required on the first run
$ lein node
```

The output NPM package will be generated at `output/node`.

### Java Bindings

The programming guide the Java Bindings can be found [here](doc/java.md).
Javadoc for the bindings can be consulted [here](https://raml-org.github.io/api-modeling-framework/doc/java/apidocs/index.html).

### API Modeling Framework Clojurescript/Web library

Expand Down
137 changes: 137 additions & 0 deletions doc/java.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# AMF Java Programming Guide

Java wrapping code for the AMF library can be found in the `org.raml.amf` namespace.
This package provides a Java friendly interface on top of the native Clojure code of the library.

## Compiling

At this moment we don't provide any artifacts in any repository to use AMF or these Java bindings, so they must be built manually with the help of Leiningen.
In order to build the library, you fist need to build an 'ubejar' with AMF. To accomplish this, from the main AMF project directory run the following instruction:

``` bash
$ lein uberjar
```
After Leiningen has finished you should have a standalone jar for AMF and all its dependencies in the `target/api-modeling-framework-0.1.2-SNAPSHOT-standalone.jar` location. Version might be different from the one at the moment of writing this documentation.

Once the jar for AMF has been generated, we can generate the Java bindings jar. For that change to the `java` directory in the AMF project and use maven:

``` bash
mvn package
```

After Maven has finished you should have an additional jar in the `target/amf-java-0.1.2-SNAPSHOT.jar` location with the Java bindings. Add both jars into your project to use the AMF Java bindings.

## Parsing

Parsers can be found in the `org.raml.amf.parsers` package of the project. They can be build using the factories in `org.raml.amf.AMF`

``` java
DocumentModel model = AMF.RAMLParser().parseFile(new URL("http://test.com/worldmusic/api.raml"));
```

Parsers are include for RAML, OpenAPI and the JSON-LD serialisation of the AMF model.

Parsers can accept options, including a hash-map of URLs to local directories that will be used to resolve references in the parsed documents.

For instance, in the next snippet all remote references to the URLs prefixed by `http://test.com/worldmusic` will be resolved looking into the local directory `/Users/antoniogarrote/world-music-api`.

``` java
HashMap<String,String> cacheDirs = new HashMap<>();
cacheDirs.put("http://test.com/worldmusic","/Users/antoniogarrote/vocabs/world-music-api");
ParsingOptions options = new ParsingOptions().setCacheDirs(cacheDirs);

DocumentDocument model = (Document) AMF.RAMLParser().parseFile(new URL("http://test.com/worldmusic/api.raml"), options);
```
The original parsed text can be retrieved using the `rawText` method.

## Navigating the Document Model
The parsing process will return an instance of one of the subclasses of `DocumentModel`.
Depending on what is the parsed file, a `Document`, a `Fragment` or a `Module` instance will be returned.

No matter what is the actual Document Model class, the returned model will also include references to all linked documents in the model.

These references can be listed using the `references` method, and new instances of `DocumentModel` can be built for these references using the `modelForReference` method:

``` java
for (URL ref : model.references()) {
DocumentModel refModel = model.modelForReference(ref);
System.out.println("Found a reference model: " + refModel);
}
```

## Applying resolution

To run the resolution algorithm and combine all the documents from the Document Model into a single Domain Model description, the method `resolve` can be invoked.

``` java
DocumentModel resolvedModel = model.resolve();
```

## Accessing the Domain Model

The parsed Domain Model can be retrieved from the Document Model instance using the appropriate accessor.

Fragments return the encoded Domain Model element using the `encodes` method from the `org.raml.amf.core.document.EncodesDomainModel` interface.
Modules returns the list of declared Domain Model elements using the `declares` method from the `org.raml.amf.core.document.DeclaresDomainModel` interface.
Documents can use both methods to retrieve the top level encoded element and the list of declared elements in the root element.

``` java
if (model instanceof EncodesDomainModel) {
System.out.println(model.encodes());
}

if (targetModel instanceof DeclaresDomainModel) {
List<DomainModel> declarations = model.declares();
for(DomainModel decl : declarations) {
System.out.println(decl);
}
}
```

## Navigating and mutating the Domain Model

The Domain Model includes Java bean classes for all elements in the AMF Domain Model.
These getters and setters can be used to navigate and mutate the model. Please, refer to the [documentation](https://raml-org.github.io/api-modeling-framework/doc/java/apidocs/index.html) for more details.

``` java
APIDocumentation api = (APIDocumentation) model.encodes();

for (EndPoint endpoint : api.getEndpoints()) {
endpoint.setName("Modified " + endpoint.getName());
}
```

## Serialisation

AMF includes generators capable of serialising the AMF model back into one of the supported syntaxes. The method `generateString` can be used to generate a String representation, and the method `generateFile` can be used to dump the serialised model directly into a file.
Factory methods for each generator can be found in the `org.raml.amf.AMF` class.


``` java
// Generating RAML
// Generate can accept just the model
String generated = AMF.RAMLGenerator().generateString(targetModel);
System.out.println(generated);

// Generating OpenAPI
// It can also accept a destination File/URL for the model
generated = AMF.OpenAPIGenerator().generateString(
new File("world_music.json"),
targetModel
);
System.out.println(generated);

// Generating JSON-LD
// Finally it can also accept a set of generation options
generated = AMF.JSONLDGenerator().generateString(
new File("world_music.jsonld"),
targetModel,
new GenerationOptions()
.setFullgraph(true)
.setSourceMapGeneration(true));
System.out.println(generated);
```

Two options are available when generating JSON-LD documents.
`setFullGraph` will nest the JSON-LD graphs for the referenced documents in the model to be serialised, otherwise only URIs will be generated.
`setSourceMapGeneration` enables or disables the generation of source maps JSON-LD information in the output.
66 changes: 66 additions & 0 deletions java/src/org/raml/amf/AMF.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.raml.amf;

import org.raml.amf.generators.AMFJSONLDGenerator;
import org.raml.amf.generators.OpenAPIGenerator;
import org.raml.amf.generators.RAMLGenerator;
import org.raml.amf.parsers.AMFJSONLDParser;
import org.raml.amf.parsers.OpenAPIParser;
import org.raml.amf.parsers.RAMLParser;

/**
* Created by antoniogarrote on 04/05/2017.
*/

/**
* Facade class providing access to the main IO facilities in the library
*/
public class AMF {

/**
* Builds a RAML to AMF parser
* @return
*/
public static RAMLParser RAMLParser() {
return new RAMLParser();
}

/**
* Builds an OpenAPI to AMF parser
* @return
*/
public static OpenAPIParser OpenAPIParser() {
return new OpenAPIParser();
}

/**
* Builds a AMF encoded JSON-LD to AMF parser
* @return
*/
public static AMFJSONLDParser JSONLDParser() {
return new AMFJSONLDParser();
}

/**
* Builds a AMF to RAML generator
* @return
*/
public static RAMLGenerator RAMLGenerator() {
return new RAMLGenerator();
}

/**
* Builds a AMF to OpenAPI generator
* @return
*/
public static OpenAPIGenerator OpenAPIGenerator() {
return new OpenAPIGenerator();
}

/**
* Builds a AMF to JSON-LD generator
* @return
*/
public static AMFJSONLDGenerator JSONLDGenerator() {
return new AMFJSONLDGenerator();
}
}
33 changes: 33 additions & 0 deletions java/src/org/raml/amf/core/Model.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.raml.amf.core;

/**
* Created by antoniogarrote on 04/05/2017.
*/

import org.raml.amf.core.exceptions.InvalidModelException;
import org.raml.amf.utils.Clojure;

/**
* Base class for all AMF parsed models, provides methods to inspect and manipulate the model
*/
public abstract class Model {

static {
Clojure.require(Clojure.API_MODELING_FRAMEWORK_CORE);
}

protected Object rawModel;

protected Model(Object rawModel) {
if (rawModel instanceof Exception) {
throw new InvalidModelException((Exception) rawModel);
}
this.rawModel = rawModel;
}

/**
* Returns the raw Clojure data structure for this instance data
* @return
*/
public abstract Object clojureModel();
}
16 changes: 16 additions & 0 deletions java/src/org/raml/amf/core/document/DeclaresDomainModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.raml.amf.core.document;

import org.raml.amf.core.domain.DomainModel;

import java.util.List;

/**
* Created by antoniogarrote on 04/05/2017.
*/
public interface DeclaresDomainModel {
/**
* Declared DomainElements that can be re-used from other documents.
* @return List of domain elements.
*/
public List<DomainModel> declares();
}
54 changes: 54 additions & 0 deletions java/src/org/raml/amf/core/document/Document.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.raml.amf.core.document;

import clojure.lang.IFn;
import org.raml.amf.core.domain.DomainModel;
import org.raml.amf.core.exceptions.InvalidModelException;
import org.raml.amf.utils.Clojure;

import java.util.ArrayList;
import java.util.List;

/**
* Created by antoniogarrote on 04/05/2017.
*/

/**
* AMF Documents encode the main element of a description in a particular Domain Model
* For example, in RAML/HTTP, the main domain element is an APIDescription.
*
* Since AMF Documents encode Domain elements they behave like Fragments
* AMF Documents can also contains declarations of domain elements to be used in the description of the domain.
* From this point of view Documents also behave like Modules.
*/
public class Document extends DocumentModel implements EncodesDomainModel, DeclaresDomainModel {
public Document(Object rawModel) {
super(rawModel);
}

/**
* Encoded domain element. It's considered to be the root element of a stand-alone description, not a domain element
* to be re-used and reference
* @return DomainElement encoded in the document.
* @throws InvalidModelException
*/
public DomainModel encodes() throws InvalidModelException {
IFn getFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, "encodes");
return DomainModel.fromRawModel(getFn.invoke(this.clojureModel()));
}

/**
* List of domain elements declared in the document to be referenced in the encoded element.
* They are supposed to be private to the description and not meant to be re-used as in Modules.
* @return
*/
public List<DomainModel> declares() {
IFn getFn = Clojure.var(Clojure.API_MODELING_FRAMEWORK_MODEL_DOCUMENT, "declares");
List parsedElements = Clojure.toJavaList((List) getFn.invoke(this.clojureModel()));
ArrayList<DomainModel> declared = new ArrayList<>();
for(Object parsed : parsedElements) {
declared.add(DomainModel.fromRawModel(parsed));
}

return declared;
}
}
Loading

0 comments on commit 0e36090

Please sign in to comment.