Skip to content
Jasper Nalbach edited this page Jun 27, 2017 · 6 revisions

This page refers to las2peer 0.6.1 and up.

The las2peer WebConnector and REST Mapper allow easy implementation of las2peer services with a RESTful interface. The REST Mapper is the library that services use for implementing a REST interface. The WebConnector communicates with the REST Mapper using las2peer's RMI calls.

Unlike as previous versions, REST calls are now processed by Jersey, the reference implementation of the JAX-RS standard for defining RESTful services in Java. Additionally, Swagger is used for generating documentations automatically.

For examples of RESTful services, please refer to the las2peer Examples on GitHub. The las2peer Template Project implements all neccessary methods for a simple RESTful service.

Compatibility

Currently, these libraries are used:

  • JAX-RS 2.0.1
  • Jersey 2.23.2
  • Swagger 2.0 (swagger-jersey2-jaxrs 1.5.10)

Implementing a RESTful service

Note that this section refers to las2peer 0.7. Prior to this version, the implementation of the initResources() method was enforced.

The service should extend the abstract RESTService class. Starting with las2peer 0.7, the service class itself is registered as RESTful resource at the root path / of the service. However, this behaviour can be customized by overriding the initResources() method.

The initResources() method should be used for setting up JAX-RS Resources. The resource configuration cannot be updated at any later point. For more details see below.

Jersey Integration

For a complete guide please refer to the Jersey documentation, especially to this chapter.

A service consists of resources that need to be defined and then registered in the service.

Defining Resources

A resource is a plain Java object and may be look like this:

@Path("/resource")
public class Resource {
  @GET
  @Path("/hello")
  public String getOk() {
  	return "Hello";
  }

  @GET
  @Path("/create/{id}")
  public Response createSomething(@PathParam("id") String id) {
    if (id.equals("favouriteNumber")) {
    	return Response.status(Response.Status.NOT_FOUND).entity("Not found!").build();
    }
    String json = "{number: 7}";
    return Response.ok(json, MediaType.APPLICATION_JSON).build();
  }
}

Resources can also be defined as member classes, but they need to be static:

@ServicePath("service")
public class ExampleService extends RESTService {
  @Override
  protected void initResources() {
    getResourceConfig().register(Resource.class);
  }

  @Path("/resource")
  public static class Resource {
    @GET
    @Path("/hello")
    public String getOk() {
      return "Hello";
    }
  }
}

Or the service itself can be used as resource (default):

@ServicePath("service")
public class ExampleService extends RESTService {
  @GET
  @Path("/hello")
  public String getOk() {
    return "Hello";
  }
}

It is also possible to get the plain request body:

@POST
@Path("/body")
@Consumes("text/plain")
public String getBody(String body) {
  return body;
}

However, Jersey has more advanced features for deserializing request bodys.

Registering Resources

By default, the service class itself is registered as a RESTful resource at the root path / relative to the service path (see below). Internally, it is a singleton resource, which means that the lifecycle is not handled by Jersey.

This behaviour can be overridden using initResources().

@Override
protected void initResources() {
  getResourceConfig().register(Resource.class);
}

getResourceConfig() returns a ResourceConfig from Jersey and should be used as shown above.

It has some extended features such as automatically detecting resources in a package. The following example reads all resources found in the ExampleService's package:

getResourceConfig().packages("i5.las2peer.services.exampleService");

The REST Mapper sets up the ResourceConfig to use the current service's class loader.

If you don't want to use the ResourceConfig object, you can set up a custom JAX-RS Application:

@Override
protected void initResources() {
  setApplication(new Application() {
    @Override
    public Set<Class<?>> getClasses() {
      Set<Class<?>> classes = new HashSet<>();
      classes.add(Resource.class);
      return classes;
    }
  });
}

Please note that you only can use either setApplication(Application) or getResourceConfig(). Also, one of these methods must be used to set up the resources.

SecurityContext

The REST Mapper has a simple integration of the JAX-RS SecurityContext with las2peer's security mechanisms:

  • Invocations done by the anonymous agent are treated as unauthenticated, this means that getUserPrincipal() returns null.

  • Authenticated agents have the role authenticated. Users have the authenticated_user role, all other agents have the authenticated_bot role.

To see how to use this, have a look on this example:

@ServicePath("service")
public class TestService extends RESTService {
  @Override
  protected void initResources() {
    getResourceConfig().register(Resource.class);
    // needed for @PermitAll and @RolesAllowed annotations:
    getResourceConfig().register(RolesAllowedDynamicFeature.class);
  }

  @Path("/")
  @PermitAll
  public static class Resource {
    @GET
    @Path("/everyone")
    @Produces(MediaType.TEXT_PLAIN)
    public String getEveryone(@Context SecurityContext securityContext) {
      // available to all users (thanks to @PermitAll)
    }

    @RolesAllowed("authenticated")
    @GET
    @Path("/authenticated")
    public String getAuthenticated() {
      // only available to authenticated users
    }
  }
}

Swagger

Once you have created your RESTful las2peer service, you should provide good documentation for external developers. las2peer uses Swagger for this purpose. With a couple of additional annotations to your service, las2peer auto-generates Swagger documents and hosts them along with your service.

Annotations

In addition to the JAX-RS annotations, Swagger provides some own annotations for documentation. Please refer to our examples and to Swagger's documentation for more information.

Please notice that only resources annotated with @Api are listed in the output.

Deployment

Note: In las2peer 0.6.0/0.6.1, the service path must not contain /. This section refers to las2peer 0.6.2.

The last step is to specify where your service should be accessible through the WebConnector.

Services can define a base path where the service is deployed. For example, if the service is deployed at my/service/, the service methods will be available at https://my.web.connector/my/service/.

The service is determined by the service's base path, the rest of the request URI is processed by Jersey. All paths in the service are relative to this path.

The alias can be set by annotating the RESTService class with @ServicePath(String):

@ServicePath("path/to/my/service")
public class ExampleService extends RESTService {
  @Override
  protected void initResources() {
    ...
  }
  ...
}

But there are some restrictions on the base paths:

  • The alias must not contain any character not allowed in URIs.
  • No PathParams are possible in the service's base path.
  • A service path cannot be a prefix of another service path. For example, services at service and service/sub are invalid. In this case, only the first service registered would be available via the WebConnector.

Using a RESTful service

All service methods are available at paths beginning with /service/path/.... In this case, any service version could be choosen by the WebConnector (probably a newer one). You can also specify a version using /service/path/v[version]/..., where [version] is replaced by a version as described here.

The version is determined using the same algorithm used for remote method invocations, it is not guaranteed that the called service version is always the same. You should specify the version to make sure to get a consistent API. For this purpose, we encourage every service to use semantic versioning.

Swagger

The swagger definitions are available at /service/path/swagger.json respecively /service/path/v[version]/swagger.json. For example: http://localhost:8080/service/swagger.json

You can use this definition in Swagger UI.

Authenticated Requests

The WebConnector sets up the default OIDC provider (https://api.learning-layers.eu/o/oauth2) as security definition and sets up Swagger to provide authentication for each method.

To use OpenID Connect in Swagger UI, you need to register a client at the default provider (see https://github.com/learning-layers/openid-connect-button for instructions). Then download Swagger UI (version 2.2.x, 3.0.0 does not support OIDC anymore), open dist/index.html, find these lines in the code and fill in clientId, clientSecret and appName:

if(typeof initOAuth == "function") {
  initOAuth({
    clientId: "your-client-id",
    clientSecret: "your-client-secret-if-required",
    realm: "your-realms",
    appName: "your-app-name",
    scopeSeparator: ",",
    additionalQueryStringParams: {}
  });
}

Now you can test authenticated requests directly from Swagger UI!

Clone this wiki locally