-
Notifications
You must be signed in to change notification settings - Fork 215
/
ScriptedIncomingMapping.java
133 lines (114 loc) · 5.77 KB
/
ScriptedIncomingMapping.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
* Copyright (c) 2017 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.connectivity.service.mapping.javascript;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
import org.eclipse.ditto.base.model.exceptions.DittoJsonException;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.connectivity.api.ExternalMessage;
import org.eclipse.ditto.connectivity.model.MessageMappingFailedException;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.protocol.Adaptable;
import org.eclipse.ditto.protocol.ProtocolFactory;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.NativeJSON;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.typedarrays.NativeArrayBuffer;
/**
* Mapping function for incoming messages based on JavaScript.
*/
public class ScriptedIncomingMapping implements MappingFunction<ExternalMessage, List<Adaptable>> {
private static final String EXTERNAL_MESSAGE_HEADERS = "headers";
private static final String EXTERNAL_MESSAGE_CONTENT_TYPE = "contentType";
private static final String EXTERNAL_MESSAGE_TEXT_PAYLOAD = "textPayload";
private static final String EXTERNAL_MESSAGE_BYTE_PAYLOAD = "bytePayload";
private static final String INCOMING_FUNCTION_NAME = "mapToDittoProtocolMsgWrapper";
@Nullable
private ContextFactory contextFactory;
@Nullable
private Scriptable scope;
ScriptedIncomingMapping(@Nullable final ContextFactory contextFactory, @Nullable final Scriptable scope) {
this.contextFactory = contextFactory;
this.scope = scope;
}
@Override
public List<Adaptable> apply(final ExternalMessage message) {
try {
return contextFactory.call(cx -> {
final NativeObject externalMessage = mapExternalMessageToNativeObject(message);
final org.mozilla.javascript.Function
mapToDittoProtocolMsgWrapper =
(org.mozilla.javascript.Function) scope.get(INCOMING_FUNCTION_NAME, scope);
final Object result =
mapToDittoProtocolMsgWrapper.call(cx, scope, scope, new Object[]{externalMessage});
if (result == null) {
// return empty list if result is null
return Collections.emptyList();
} else if (result instanceof NativeArray nativeArray) {
// array handling
final List<Adaptable> list = new ArrayList<>();
for (Object idxObj : nativeArray.getIds()) {
int index = (Integer) idxObj;
final Object element = nativeArray.get(index, null);
list.add(getAdaptableFromObject(cx, element));
}
return list;
}
return Collections.singletonList(getAdaptableFromObject(cx, result));
});
} catch (final RhinoException e) {
throw buildMessageMappingFailedException(e, message.findContentType().orElse(""),
DittoHeaders.of(message.getHeaders()));
} catch (final Throwable e) {
throw MessageMappingFailedException.newBuilder(message.findContentType().orElse(null))
.description(e.getMessage())
.dittoHeaders(DittoHeaders.of(message.getHeaders()))
.cause(e)
.build();
}
}
static NativeObject mapExternalMessageToNativeObject(final ExternalMessage message) {
final NativeObject headersObj = new NativeObject();
message.getHeaders().forEach((key, value) -> headersObj.put(key, headersObj, value));
final NativeArrayBuffer bytePayload =
message.getBytePayload()
.map(bb -> {
final NativeArrayBuffer nativeArrayBuffer = new NativeArrayBuffer(bb.remaining());
bb.get(nativeArrayBuffer.getBuffer());
return nativeArrayBuffer;
})
.orElse(null);
final String contentType = message.getHeaders().get(ExternalMessage.CONTENT_TYPE_HEADER);
final String textPayload = message.getTextPayload().orElse(null);
final NativeObject externalMessage = new NativeObject();
externalMessage.put(EXTERNAL_MESSAGE_HEADERS, externalMessage, headersObj);
externalMessage.put(EXTERNAL_MESSAGE_TEXT_PAYLOAD, externalMessage, textPayload);
externalMessage.put(EXTERNAL_MESSAGE_BYTE_PAYLOAD, externalMessage, bytePayload);
externalMessage.put(EXTERNAL_MESSAGE_CONTENT_TYPE, externalMessage, contentType);
return externalMessage;
}
private Adaptable getAdaptableFromObject(final Context cx, final Object result) {
final String dittoProtocolJsonStr = (String) NativeJSON.stringify(cx, scope, result, null, null);
return DittoJsonException.wrapJsonRuntimeException(() -> {
final JsonObject jsonObject = JsonFactory.readFrom(dittoProtocolJsonStr).asObject();
return ProtocolFactory.jsonifiableAdaptableFromJson(jsonObject);
});
}
}