Skip to content

Creating custom Domain Specific Interface DSI

Mario Kušek edited this page Oct 15, 2018 · 1 revision

DomainSpecificInterface

Using Generic DomainSpecificInterface

The idea of DomainSpecificInterface is to enable the usage of specific enabler services to external users or other applications. The example of one implemented DomainSpecificInterface can be found in the following directory: https://github.com/symbiote-h2020/DomainSpecificInterfaceSMEUR

Creating DomainSpecificInterface

1. Creating new SpringBoot project

It needs following dependencies: Config Client, Eureka Discovery, Zipkin Client, Feign, Amqp, WEB, Data REST

Current version of SpringBoot that is used is: 1.5.14.

2. Adding symbIoTe dependencies to build.gradle

SymbIoTe dependencies used in the DomainSpecificInterface component are optional. The component does not necessary need to have them, but it is recommended to use the SymbIoTeLibraries data models if possible. SymbioteLibraries dependency is added with the following line:

compile('com.github.symbiote-h2020:SymbIoTeLibraries:5.+') { changing = true }

Also, if other services are implemented as RapPlugins, DomainSpecificInterface will receive a response from those entities as RapPluginOkResponse, so RapPlugin dependency should also be added as following (only the current version of RapPlugin should be updated):

compile('com.github.symbiote-h2020:ResourceAccessProxyPlugin:0.3.1')

in order to add previously mentioned dependencies from jitpack it is necessary to put in build.gradle following lines as well:

allprojects {
	repositories {
		jcenter()
		maven { url "https://jitpack.io" }
	}
}

3. Setting configuration

Configuration needs to be put in bootstrap.properties or YML file. An example is here:

spring.application.name=DomainSpecificInterfaceSMEUR
spring.cloud.config.uri=http://localhost:8888
logging.file=logs/DomainSpecificInterfaceSMEUR.log

rabbit.host=localhost
rabbit.username=preferredUsername
rabbit.password=preferredPassword

The first line is defining the name of this specific DomainSpecificInterface. Under this name properties are loaded from config server. The second line is location of config server. This is the case when config server is run in local machine which is suitable for development. If you are running it in some other machine change this line accordingly. Afterwards is rabbitMQ configuration for connection to the rabbitMQ server. This should also be updated according to the used rabbitMQ server location and credentials.

4. Creating DomainSpecificInterface component

DomainSpecificInterface is an external REST interface that should provide the usage of enabler services to the external users. Internal communication with enabler components that offer specific services is accomplished using the RabbitMQ messaging concept, and communication with external users is based on HTTP REST concept.

To enable RabbitMQ communication, DomainSpecificInterface component needs to establish connection with the RabbitMQ server first:

	public Connection getConnection() throws IOException, TimeoutException {
		if (connection == null) {
			ConnectionFactory factory = new ConnectionFactory();
			factory.setHost(this.rabbitHost);
			factory.setUsername(this.rabbitUsername);
			factory.setPassword(this.rabbitPassword);
			this.connection = factory.newConnection();
		}
		return this.connection;
	}

DomainSpecificInterface receives user requests which are then forwarded to specific services and the response is afterwards forwarded to the user, so the component itself does not need any queue bindings in the beginning. All internal communication is initiated from DomainSpecificInterface where the RPC concept is used, so the component itself only uses temporary queues for receiving responses from certain service components. Examples of RPC communication are given in continuation:

public Object sendRpcMessage(String exchange, String routingKey, Object obj) {
		log.info("Sending RPC message...");

		String correlationId = UUID.randomUUID().toString();
		rabbitTemplate.setReplyTimeout(60000);
		Object receivedObj = rabbitTemplate.convertSendAndReceive(exchange, routingKey, obj,
				new CorrelationData(correlationId));
		if (receivedObj == null) {
			log.info("Received null or Timeout!");
			return null;
		}

		log.info("RPC Response received obj: " + receivedObj);

		return receivedObj;
	}

Example where JSON formatting is used:

	public Object sendRpcMessageJSON(String exchange, String routingKey, Object obj) {
		log.info("Sending RPC message");

		String correlationId = UUID.randomUUID().toString();
		rabbitTemplate.setReplyTimeout(60000);
		ObjectMapper mapper = new ObjectMapper();
		mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter(mapper);
		rabbitTemplate.setMessageConverter(messageConverter);
		Object receivedObj = rabbitTemplate.convertSendAndReceive(exchange, routingKey, obj,
				new CorrelationData(correlationId));
		if (receivedObj == null) {
			log.info("Received null or Timeout!");
			return null;
		}

		log.info("RPC Response received obj: " + receivedObj);
		rabbitTemplate.setMessageConverter(new SimpleMessageConverter()); // set theSimpleMessageConverter again
		return receivedObj;
	}

The other and also the main necessary component of DomainSpecificInterface is the REST interface, where processing of different user requests is implemented. To create a class that represents a REST interface annotation @RestContoller should be added in front of it:

@CrossOrigin
@RestController
public class DomainSpecificInterfaceRestController {

Afterwards, methods that are used as endpoints for receiving user requests are implemented depending on certain purpose. In front each endpoint method annotation can be added to specify different HTTP request parameters:

@RequestMapping(value = "/smeur/poi", method = RequestMethod.GET)

This annotation specifies that the method will be invoked if HTTP-GET request is received with the location path /smeur/poi.

public ResponseEntity<?> poiLatLon(@RequestParam(value = "lat") double lat, @RequestParam(value = "lon") double lon,
			@RequestParam(value = "r") double r, @RequestParam(value = "amenity") String amenity)

Path parameters from HTTP request are received using the @RequestParam annotation as shown in the example above, and it is also possible to use request body parameters within other HTTP requests by using the annotation @RequestBody.

Internal communication depends on the implementation of other components. If a component is implemented as a RapPlugin (which is recommended) then a RPC message should be sent on the corresponding exchange ("plugin-exchange"), with a proper routing key (depending on the name of the plugin). The RapPlugin expects to receive specific messages, and to invoke a service ResourceAccessSetMessage should be sent in JSON format. This message contains:

  • ResourceInfo list - identifier of a service should be placed;
  • String parameter - parameters for the specific service should be provided in the JSON format.

Also, RapPlugin should return RapPluginOkResponse so communication should be formatted according to that:

		PoiSearchRequest poiReq = new PoiSearchRequest(lat, lon, 2.0, "all");

		RapPluginOkResponse receivedOK = (RapPluginOkResponse) rabbitManager.sendRpcMessage(poiExchangeName,
				poiRoutingKey, om.writeValueAsString(
						new ResourceAccessSetMessage(resourceInfoList, om.writeValueAsString(Arrays.asList(poiReq)))));

In the example above, PoiSearchRequest object which is a parameter for RapPlugin PoI service is created, and afterwards it is incorporated as a String in a ResourceAccessSetMessage. This message is sent on the proper exchange with the specific routing key to properly invoke the requested service.

If the specific service component is not implemented as a RapPlugin, then the communication should be adapted to the specific conditions (chosen request object message should be sent to the proper exchange with the proper routing key, and the response is proccessed also depending on the chosen model) e.g.:

GrcRequest request = new GrcRequest(from, to, transport, optimisation);

List<GrcResponse> k = (List<GrcResponse>) rabbitManager.sendRpcMessage(ExchangeName, RoutingKeyName,
				messageConverter.toMessage(request, null));

In the example above GrcRequest object is sent to the exchange with the given name and with the proper routing key, and a List of GrcRsponse objects is expected as a response to the sent message.