Commit 30ecb3b3 authored by Martti Käärik's avatar Martti Käärik
Browse files

Updated HTTP adapter to support extended TRI data.

parent f54d5b9f
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ public class DefaultAdapter implements Validator, Reporter, RuntimeHelper {
	@Override
	public void setVerdict(Verdict verdict) {
		// TODO Auto-generated method stub
		System.out.println("Set verdict to " + verdict.name);
		System.out.println("Set verdict to " + verdict.getName());
	}

}
+106 −20
Original line number Diff line number Diff line
@@ -2,6 +2,9 @@ package org.etsi.mts.tdl.execution.java.adapters.http;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
@@ -20,14 +23,17 @@ import org.etsi.mts.tdl.execution.java.rt.core.PojoData;
import org.etsi.mts.tdl.execution.java.tri.Argument;
import org.etsi.mts.tdl.execution.java.tri.Connection;
import org.etsi.mts.tdl.execution.java.tri.Data;
import org.etsi.mts.tdl.execution.java.tri.ElementAnnotation;
import org.etsi.mts.tdl.execution.java.tri.Mapping;
import org.etsi.mts.tdl.execution.java.tri.Procedure;
import org.etsi.mts.tdl.execution.java.tri.Reporter;
import org.etsi.mts.tdl.execution.java.tri.SystemAdapter;
import org.etsi.mts.tdl.execution.java.tri.Type;
import org.etsi.mts.tdl.execution.java.tri.Validator;
import org.etsi.mts.tdl.execution.java.tri.Value;
import org.openapitools.jackson.nullable.JsonNullableModule;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@@ -35,14 +41,13 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

public class HttpSystemAdapter implements SystemAdapter {

	private Validator validator;
	private Reporter reporter;
	protected Validator validator;
	protected Reporter reporter;

	private HttpClient.Builder builder;
	private HttpClient client;
	private ObjectMapper mapper;


	private String baseUri = "https://example.com";
	private List<HttpHeader> defaultHeaders = new ArrayList<HttpHeader>();

@@ -60,8 +65,8 @@ public class HttpSystemAdapter implements SystemAdapter {
	}

	/**
	 * Set headers that are applied to all requests, such as authentication.
	 * The default implementation adds by default:
	 * Set headers that are applied to all requests, such as authentication. The
	 * default implementation adds by default:
	 * <ul>
	 * <li>Content-Type: application/json</li>
	 * <li>Accept: application/json</li>
@@ -111,6 +116,7 @@ public class HttpSystemAdapter implements SystemAdapter {
	}

	private void handleError(Throwable t) {
		t.printStackTrace();
		reporter.runtimeError(t);
		throw new RuntimeException(t);
	}
@@ -125,7 +131,7 @@ public class HttpSystemAdapter implements SystemAdapter {

	@Override
	public void send(Data message, Connection connection) {
		HttpRequestData httpData = (HttpRequestData) message.getValue();
		HttpRequestData httpData = getRequestData(message);

		HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
		applyDefaults(requestBuilder);
@@ -170,9 +176,9 @@ public class HttpSystemAdapter implements SystemAdapter {
			requestBuilder.uri(uri);

			// TODO logging
			reporter.comment("Outgoing: " + httpData.method.toString() + " | " + uri + " | " + mapper.writeValueAsString(httpData.body));
			reporter.comment("Outgoing (headers): " + httpData.method.toString() + " | " + uri);

			byte[] body = mapper.writeValueAsBytes(httpData.body);
			byte[] body = encodeBody(httpData.body);
			requestBuilder.method(httpData.method.toString(), HttpRequest.BodyPublishers.ofByteArray(body));

			for (HttpHeader header : httpData.headers)
@@ -183,14 +189,14 @@ public class HttpSystemAdapter implements SystemAdapter {

			responseFuture.whenCompleteAsync(this::handleResponse);

		} catch (JsonProcessingException e) {
		} catch (IOException e) {
			handleError(e);
		}
	}

	@Override
	public Data receive(Data expected, Connection connection) throws InterruptedException, AssertionError {
		HttpResponseData expectedHttpData = expected != null ? (HttpResponseData) expected.getValue() : null;
		HttpResponseData expectedHttpData = expected != null ? getResponseData(expected) : null;

		synchronized (unhandledResponses) {
			HttpResponse<String> response = unhandledResponses.peek();
@@ -215,8 +221,7 @@ public class HttpSystemAdapter implements SystemAdapter {
				Data received = new PojoData<>(receivedHttpData);
				if (expected != null) {
					if (expectedHttpData.body != null) {
						receivedHttpData.body = this.mapper.readValue(response.body(),
								expectedHttpData.body.getClass());
						receivedHttpData.body = decodeBody(response.body(), expectedHttpData.body.getClass());
					}
					if (this.validator.matches(expected, received)) {
						unhandledResponses.remove();
@@ -229,7 +234,7 @@ public class HttpSystemAdapter implements SystemAdapter {
					return received;
				}

			} catch (JsonProcessingException e) {
			} catch (IOException e) {
				handleError(e);
			}
		}
@@ -237,6 +242,15 @@ public class HttpSystemAdapter implements SystemAdapter {
		return null;
	}

	protected byte[] encodeBody(Object body) throws IOException {
		reporter.comment("Outgoing (body): " + mapper.writeValueAsString(body));
		return mapper.writeValueAsBytes(body);
	}

	protected Object decodeBody(String body, Class<?> type) throws IOException {
		return this.mapper.readValue(body, type);
	}

	@Override
	public Data call(Procedure operation, Argument[] arguments, Data expectedReturn, Data expectedException,
			Connection connection) {
@@ -252,4 +266,76 @@ public class HttpSystemAdapter implements SystemAdapter {
	public void replyCall(Procedure operation, Data returnValue, Data exception, Connection connection) {
		throw new UnsupportedOperationException("Procedure-based communication is not supported by this adapter.");
	}

	protected HttpRequestData getRequestData(Data message) {
		if (message.getValue() instanceof HttpRequestData)
			return (HttpRequestData) message.getValue();
		if (message.getType() instanceof Type) {
			HttpRequestData req = (HttpRequestData) getMappedObject(message);
			return req;
		}
		throw new RuntimeException("Request data in unsupported format");
	}

	private Object getMappedObject(Data<Type, Value> data) {
		Type t = (Type) data.getType();
		Value v = data.getValue();

		if (!t.isStructure() && !t.isCollection() && v.getValue() != null)
			return v.getValue();

		Mapping m = t.getMapping();
		if (m == null || !m.getMappingName().equalsIgnoreCase("Java"))
			return data;

		Mapping rs = m.getResource();
		boolean isMapped = false;
		for (ElementAnnotation a : rs.annotations) {
			if (a.key.equals("Tdl::MappingName") && a.value.equals("Java")) {
				isMapped = true;
				break;
			}
		}

		if (!isMapped)
			return null;

		String cName = rs.getUri() + "." + m.getUri();
		Object obj = null;
		try {
			Class<?> cl = getClass().getClassLoader().loadClass(cName);
			if (cl.isEnum()) {
				String literal = null;
				if (v.getValue() != null)
					literal = v.getValue().toString();
				else if (v.getMapping() != null)
					literal = v.getMapping().getUri();
				obj = Enum.valueOf((Class<Enum>) cl, literal);
			} else
				obj = cl.getDeclaredConstructor().newInstance();

			if (t.isStructure()) {
				for (String pName : t.getParameters()) {
					Data<Type, Value> pData = data.getValue().getParameter(pName);
					if (pData != null) {
						Object pObj = getMappedObject(pData);
						Field pField = cl.getDeclaredField(m.getParameter(pName).getUri());
						pField.set(obj, pObj);
					}
				}
			}
//			TODO isCollection()
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException | NoSuchMethodException | SecurityException | NoSuchFieldException e) {
			handleError(e);
		}
		return obj;
	}

	protected HttpResponseData getResponseData(Data message) {
		if (message.getValue() instanceof HttpResponseData)
			return (HttpResponseData) message.getValue();
//		XXX
		return null;
	}
}
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ public class ReceiverHub {
		this.systemAdapter = systemAdapter;
		this.connection = connection;

		this.thread = new Thread(() -> run(), "Receive from SystemAdapter on " + connection.name);
		this.thread = new Thread(() -> run(), "Receive from SystemAdapter on " + connection.getName());
		this.thread.setDaemon(true);
		this.thread.start();
	}
+6 −6
Original line number Diff line number Diff line
@@ -109,16 +109,16 @@ public class TestControl {
		for (Connection c : connections) {
			int testerEP = -1;
			for (int i = 0; i <= 1; i++)
				if (c.endPoints[i].component.name.equals(testerComponentName)
						&& c.endPoints[i].gate.name.equals(testerGateName)) {
				if (c.endPoints[i].component.getName().equals(testerComponentName)
						&& c.endPoints[i].gate.getName().equals(testerGateName)) {
					testerEP = i;
					break;
				}
			if (testerEP == -1)
				continue;
			int targetEP = testerEP == 0 ? 1 : 0;
			if (c.endPoints[targetEP].component.name.equals(remoteComponentName)
					&& c.endPoints[targetEP].gate.name.equals(remoteGateName))
			if (c.endPoints[targetEP].component.getName().equals(remoteComponentName)
					&& c.endPoints[targetEP].gate.getName().equals(remoteGateName))
				return c;
		}
		throw new RuntimeException(String.format("Unknown connection: from %s.%s to %s.%s", testerComponentName,
@@ -128,8 +128,8 @@ public class TestControl {
	public GateReference getGateReference(String testerComponentName, String testerGateName) {
		for (Connection c : connections) {
			for (int i = 0; i <= 1; i++)
				if (c.endPoints[i].component.name.equals(testerComponentName)
						&& c.endPoints[i].gate.name.equals(testerGateName)) {
				if (c.endPoints[i].component.getName().equals(testerComponentName)
						&& c.endPoints[i].gate.getName().equals(testerGateName)) {
					return c.endPoints[i];
				}
		}