Skip to content

Commit 2080cba

Browse files
DavidOgunsAWSJustin Boswellgraebm
authored
New Greengrass IPC client added (#121)
update CRT to 0.9.2 (#17) Adding Greengrass IPC client Co-authored-by: Justin Boswell <[email protected]> Co-authored-by: Michael Graeb <[email protected]>
1 parent 4be6b15 commit 2080cba

File tree

157 files changed

+11505
-7
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

157 files changed

+11505
-7
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ mvn clean install
5151
``` sh
5252
# NOTE: use the latest version of the CRT here
5353

54-
git clone --branch v0.4.20 https://github.com/awslabs/aws-crt-java.git
54+
git clone --branch v0.9.2 https://github.com/awslabs/aws-crt-java.git
5555

5656
git clone https://github.com/awslabs/aws-iot-device-sdk-java-v2.git
5757
cd aws-crt-java
@@ -66,7 +66,7 @@ Supports API 26 or newer.
6666
NOTE: The shadow sample does not currently complete on android due to its dependence on stdin keyboard input.
6767

6868
``` sh
69-
git clone --recursive --branch v0.6.2 https://github.com/awslabs/aws-crt-java.git
69+
git clone --recursive --branch v0.9.2 https://github.com/awslabs/aws-crt-java.git
7070
git clone https://github.com/awslabs/aws-iot-device-sdk-java-v2.git
7171
cd aws-crt-java/android
7272
./gradlew connectedCheck # optional, will run the unit tests on any connected devices/emulators
@@ -87,7 +87,7 @@ repositories {
8787
}
8888
8989
dependencies {
90-
implementation 'software.amazon.awssdk.crt:android:0.6.2'
90+
implementation 'software.amazon.awssdk.crt:android:0.9.2'
9191
}
9292
```
9393

android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ repositories {
5050
dependencies {
5151
implementation fileTree(dir: 'libs', include: ['*.jar'])
5252
implementation project(":iotdevicesdk")
53-
implementation 'software.amazon.awssdk.crt:android:0.8.4'
53+
implementation 'software.amazon.awssdk.crt:android:0.9.2'
5454
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
5555
implementation 'androidx.appcompat:appcompat:1.1.0'
5656
implementation 'androidx.core:core:1.2.0'

android/iotdevicesdk/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ repositories {
8787

8888
dependencies {
8989
implementation fileTree(dir: 'libs', include: ['*.jar'])
90-
implementation 'software.amazon.awssdk.crt:android:0.8.4'
90+
implementation 'software.amazon.awssdk.crt:android:0.9.2'
9191
implementation 'com.google.code.gson:gson:2.8.5'
9292
implementation 'androidx.appcompat:appcompat:1.1.0'
9393
testImplementation 'junit:junit:4.12'

sdk/greengrass/event-stream-rpc-client/src/main/java/software/amazon/awssdk/eventstreamrpc/EventStreamRPCClient.java

Lines changed: 273 additions & 0 deletions
Large diffs are not rendered by default.

sdk/greengrass/event-stream-rpc-client/src/main/java/software/amazon/awssdk/eventstreamrpc/EventStreamRPCConnection.java

Lines changed: 352 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package software.amazon.awssdk.eventstreamrpc;
2+
3+
import software.amazon.awssdk.crt.io.*;
4+
import software.amazon.awssdk.eventstreamrpc.MessageAmendInfo;
5+
6+
import java.util.concurrent.CompletableFuture;
7+
import java.util.function.Supplier;
8+
9+
/**
10+
* The closeable elements inside the EventStreamRPCConnectionConfig are not cleaned up when
11+
* this config object is done. It is still up to the caller of the constructor to clean up
12+
* resources that are associated in the config.
13+
*
14+
* The connect message transformer is used to supply additional connect message headers
15+
* and supply the payload of the connect message. This is to be used to supply authentication
16+
* information on the connect
17+
*/
18+
public class EventStreamRPCConnectionConfig {
19+
private final ClientBootstrap clientBootstrap;
20+
private final EventLoopGroup eventLoopGroup;
21+
private final SocketOptions socketOptions;
22+
private final ClientTlsContext tlsContext;
23+
private final String host;
24+
private final int port;
25+
26+
/**
27+
* MessageAmendInfo here is used to add supplied headers to the Connect message, and
28+
* set the payload of that message as well.
29+
*/
30+
private final Supplier<CompletableFuture<MessageAmendInfo>> connectMessageAmender;
31+
32+
public EventStreamRPCConnectionConfig(ClientBootstrap clientBootstrap, EventLoopGroup eventLoopGroup,
33+
SocketOptions socketOptions, ClientTlsContext tlsContext,
34+
String host, int port, Supplier<CompletableFuture<MessageAmendInfo>> connectMessageAmender) {
35+
this.clientBootstrap = clientBootstrap;
36+
this.eventLoopGroup = eventLoopGroup;
37+
this.socketOptions = socketOptions;
38+
this.tlsContext = tlsContext;
39+
this.host = host;
40+
this.port = port;
41+
this.connectMessageAmender = connectMessageAmender;
42+
43+
//perform cast to throw exception here if port value is out of short value range
44+
final short shortPort = (short)port;
45+
46+
//bit of C++ RAII here, validate what we can
47+
if (clientBootstrap == null || eventLoopGroup == null || socketOptions == null ||
48+
host == null || host.isEmpty() || port < 0) {
49+
throw new IllegalArgumentException("EventStreamRPCConnectionConfig values are invalid!");
50+
}
51+
}
52+
53+
public ClientBootstrap getClientBootstrap() {
54+
return clientBootstrap;
55+
}
56+
57+
public EventLoopGroup getEventLoopGroup() {
58+
return eventLoopGroup;
59+
}
60+
61+
public SocketOptions getSocketOptions() {
62+
return socketOptions;
63+
}
64+
65+
public ClientTlsContext getTlsContext() {
66+
return tlsContext;
67+
}
68+
69+
public String getHost() {
70+
return host;
71+
}
72+
73+
public int getPort() {
74+
return port;
75+
}
76+
77+
public Supplier<CompletableFuture<MessageAmendInfo>> getConnectMessageAmender() {
78+
return connectMessageAmender;
79+
}
80+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package software.amazon.awssdk.eventstreamrpc;
2+
3+
import com.google.gson.Gson;
4+
import software.amazon.awssdk.crt.eventstream.Header;
5+
6+
import java.nio.charset.StandardCharsets;
7+
import java.util.LinkedList;
8+
import java.util.List;
9+
import java.util.concurrent.CompletableFuture;
10+
import java.util.function.Supplier;
11+
12+
public class GreengrassConnectMessageSupplier {
13+
14+
public static Supplier<CompletableFuture<MessageAmendInfo>> connectMessageSupplier(String authToken) {
15+
return () -> {
16+
final List<Header> headers = new LinkedList<>();
17+
GreengrassEventStreamConnectMessage connectMessage = new GreengrassEventStreamConnectMessage();
18+
connectMessage.setAuthToken(authToken);
19+
String payload = new Gson().toJson(connectMessage);
20+
return CompletableFuture.completedFuture(new MessageAmendInfo(headers, payload.getBytes(StandardCharsets.UTF_8)));
21+
};
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package software.amazon.awssdk.eventstreamrpc;
2+
3+
public class GreengrassEventStreamConnectMessage {
4+
5+
private String authToken;
6+
7+
public void setAuthToken(String authToken) {
8+
this.authToken = authToken;
9+
}
10+
11+
public String getAuthToken() {
12+
return this.authToken;
13+
}
14+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package software.amazon.awssdk.eventstreamrpc;
2+
3+
import software.amazon.awssdk.crt.eventstream.ClientConnectionContinuation;
4+
import software.amazon.awssdk.crt.eventstream.Header;
5+
import software.amazon.awssdk.crt.eventstream.MessageFlags;
6+
import software.amazon.awssdk.crt.eventstream.MessageType;
7+
import software.amazon.awssdk.eventstreamrpc.model.EventStreamJsonMessage;
8+
9+
import java.util.LinkedList;
10+
import java.util.List;
11+
import java.util.concurrent.CompletableFuture;
12+
import java.util.concurrent.atomic.AtomicBoolean;
13+
import java.util.logging.Logger;
14+
15+
/**
16+
* Underlying type for operation response handling. Enables publishing on stream operations from
17+
* client, closing of any open stream, and retrieval of response. Specific generated operation response
18+
* handlers are usually simple wrappers with the generic types specified
19+
*
20+
* @param <ResponseType>
21+
* @param <StreamRequestType>
22+
*/
23+
public class OperationResponse<ResponseType extends EventStreamJsonMessage,
24+
StreamRequestType extends EventStreamJsonMessage>
25+
implements StreamResponse<ResponseType, StreamRequestType>, AutoCloseable {
26+
private static final Logger LOGGER = Logger.getLogger(OperationResponse.class.getName());
27+
private final OperationModelContext operationModelContext;
28+
private final ClientConnectionContinuation continuation;
29+
private final CompletableFuture<ResponseType> responseFuture;
30+
private final CompletableFuture<Void> requestFlushFuture;
31+
private final AtomicBoolean isClosed;
32+
33+
public OperationResponse(OperationModelContext<ResponseType, ?, StreamRequestType, ?> operationModelContext,
34+
ClientConnectionContinuation continuation,
35+
CompletableFuture<ResponseType> responseFuture,
36+
CompletableFuture<Void> requestFlushFuture) {
37+
this.operationModelContext = operationModelContext;
38+
this.continuation = continuation;
39+
this.responseFuture = responseFuture;
40+
this.requestFlushFuture = requestFlushFuture;
41+
this.isClosed = new AtomicBoolean(continuation != null && !continuation.isNull());
42+
}
43+
44+
final public CompletableFuture<Void> getRequestFlushFuture() {
45+
return requestFlushFuture;
46+
}
47+
48+
/**
49+
* Get the response completable future to wait on the initial response
50+
* if there is one.
51+
*
52+
* May throw exception if requestFlushFuture throws an exception and will
53+
* block if requestFlush has not completed.
54+
*
55+
* @return
56+
*/
57+
public CompletableFuture<ResponseType> getResponse() {
58+
//semantics here are: if the request was never successfully sent
59+
//then the request flush future holds the exception thrown so that
60+
//must be made visible of the caller waits for the response directly.
61+
//It is impossible to have a successful response future completed
62+
//with a request flush never having completed or having thrown an
63+
//exception.
64+
return requestFlushFuture.thenCompose((v) -> responseFuture);
65+
}
66+
67+
/**
68+
* Publish stream events on an open operation's event stream.
69+
* @param streamEvent event to publish
70+
*/
71+
@Override
72+
public CompletableFuture<Void> sendStreamEvent(final StreamRequestType streamEvent) {
73+
try {
74+
final List<Header> headers = new LinkedList<>();
75+
headers.add(Header.createHeader(EventStreamRPCServiceModel.SERVICE_MODEL_TYPE_HEADER,
76+
(String) operationModelContext.getStreamingRequestApplicationModelType().get()));
77+
headers.add(Header.createHeader(EventStreamRPCServiceModel.CONTENT_TYPE_HEADER,
78+
EventStreamRPCServiceModel.CONTENT_TYPE_APPLICATION_JSON));
79+
final byte[] payload = operationModelContext.getServiceModel()
80+
.toJson(streamEvent);
81+
return continuation.sendMessage(headers, payload,
82+
MessageType.ApplicationMessage, 0)
83+
.whenComplete((res, ex) -> {
84+
if (ex != null) {
85+
LOGGER.warning(String.format("%s caught %s while sending message the event stream: %s",
86+
operationModelContext.getOperationName(), ex.getClass().getName(),
87+
ex.getMessage()));
88+
closeStream();
89+
}
90+
});
91+
} catch (Exception e) {
92+
final CompletableFuture<Void> future = new CompletableFuture<>();
93+
future.completeExceptionally(e);
94+
return future;
95+
}
96+
}
97+
98+
/**
99+
* Initiate a close on the event stream from the client side.
100+
*
101+
* @return
102+
*/
103+
@Override
104+
public CompletableFuture<Void> closeStream() {
105+
if (continuation != null && !continuation.isNull()) {
106+
return continuation.sendMessage(null, null,
107+
MessageType.ApplicationMessage, MessageFlags.TerminateStream.getByteValue())
108+
.whenComplete((res, ex) -> {
109+
LOGGER.info(operationModelContext.getOperationName() + " operation stream closed");
110+
continuation.close();
111+
if (ex != null) {
112+
LOGGER.warning(String.format("%s threw %s while closing the event stream: %s",
113+
operationModelContext.getOperationName(), ex.getClass().getName(),
114+
ex.getMessage()));
115+
}
116+
});
117+
}
118+
return CompletableFuture.completedFuture(null);
119+
}
120+
121+
/**
122+
* Checks if the stream is closed
123+
* @return
124+
*/
125+
public boolean isClosed() {
126+
return isClosed.get();
127+
}
128+
129+
@Override
130+
public void close() throws Exception {
131+
if (isClosed.compareAndSet(false, true)) {
132+
closeStream();
133+
}
134+
}
135+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package software.amazon.awssdk.eventstreamrpc;
2+
3+
import software.amazon.awssdk.eventstreamrpc.StreamEventPublisher;
4+
import software.amazon.awssdk.eventstreamrpc.model.EventStreamJsonMessage;
5+
6+
import java.util.concurrent.CompletableFuture;
7+
8+
public interface StreamResponse<ResponseType extends EventStreamJsonMessage, StreamRequestType extends EventStreamJsonMessage>
9+
extends StreamEventPublisher<StreamRequestType> {
10+
/**
11+
* Completable future indicating flush of the request that initiated the stream operation
12+
*
13+
* @return
14+
*/
15+
CompletableFuture<Void> getRequestFlushFuture();
16+
17+
/**
18+
* Completable future for retrieving the initial-response of the stream operation
19+
*
20+
* @return
21+
*/
22+
CompletableFuture<ResponseType> getResponse();
23+
24+
/**
25+
* Tests if the stream is closed
26+
* @return
27+
*/
28+
boolean isClosed();
29+
}

0 commit comments

Comments
 (0)