callable, Class extends Throwable> exception) throws Exception;
+}
\ No newline at end of file
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/http/jetty/InputStreamResponseListener.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/http/jetty/InputStreamResponseListener.java
new file mode 100644
index 00000000..908d482d
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/http/jetty/InputStreamResponseListener.java
@@ -0,0 +1,417 @@
+/**
+ * Portions of this file were modified by Amazon as indicated in the code.
+ * The Amazon modifications are copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * The Amazon modifications are subject to the License as defined in the LICENSE.txt file accompanying this source. You may not use this file as a whole except in compliance with the License. A link to the License is located in LICENSE.txt.
+ *
+ * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * The below copyright and license statements apply to the portions of this file other than the Amazon modifications.
+ */
+//
+// ========================================================================
+// Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package com.amazon.alexa.avs.http.jetty;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Response.Listener;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.util.IO;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Implementation of {@link Listener} that produces an {@link InputStream}
+ * that allows applications to read the response content.
+ *
+ * Typical usage is:
+ *
+ * InputStreamResponseListener listener = new InputStreamResponseListener();
+ * client.newRequest(...).send(listener);
+ *
+ * // Wait for the response headers to arrive
+ * Response response = listener.get(5, TimeUnit.SECONDS);
+ * if (response.getStatus() == 200)
+ * {
+ * // Obtain the input stream on the response content
+ * try (InputStream input = listener.getInputStream())
+ * {
+ * // Read the response content
+ * }
+ * }
+ *
+ *
+ * The {@link HttpClient} implementation (the producer) will feed the input stream
+ * asynchronously while the application (the consumer) is reading from it.
+ * Chunks of content are maintained in a queue, and it is possible to specify a
+ * maximum buffer size for the bytes held in the queue, by default 16384 bytes.
+ *
+ * If the consumer is faster than the producer, then the consumer will block
+ * with the typical {@link InputStream#read()} semantic.
+ * If the consumer is slower than the producer, then the producer will block
+ * until the client consumes.
+ */
+public class InputStreamResponseListener extends Listener.Adapter
+{
+ private static final Logger LOG = Log.getLogger(InputStreamResponseListener.class);
+ private static final byte[] EOF = new byte[0];
+ private static final byte[] CLOSED = new byte[0];
+ private static final byte[] FAILURE = new byte[0];
+ private final BlockingQueue queue = new LinkedBlockingQueue<>();
+ private final AtomicLong length = new AtomicLong();
+ private final CountDownLatch responseLatch = new CountDownLatch(1);
+ private final CountDownLatch resultLatch = new CountDownLatch(1);
+ private final AtomicReference stream = new AtomicReference<>();
+ private final long maxBufferSize;
+ private Response response;
+ private Result result;
+ private volatile Throwable failure;
+ private volatile boolean closed;
+
+ public InputStreamResponseListener()
+ {
+ this(16 * 1024L);
+ }
+
+ public InputStreamResponseListener(long maxBufferSize)
+ {
+ this.maxBufferSize = maxBufferSize;
+ }
+
+ @Override
+ public void onHeaders(Response response)
+ {
+ this.response = response;
+ responseLatch.countDown();
+ }
+
+ @Override
+ public void onContent(Response response, ByteBuffer content)
+ {
+ if (!closed)
+ {
+ int remaining = content.remaining();
+ if (remaining > 0)
+ {
+
+ byte[] bytes = new byte[remaining];
+ content.get(bytes);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queuing {}/{} bytes", bytes, remaining);
+ }
+ queue.offer(bytes);
+
+ long newLength = length.addAndGet(remaining);
+ while (newLength >= maxBufferSize)
+ {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queued bytes limit {}/{} exceeded, waiting", newLength, maxBufferSize);
+ }
+ // Block to avoid infinite buffering
+ if (!await()) {
+ break;
+ }
+ newLength = length.get();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queued bytes limit {}/{} exceeded, woken up", newLength, maxBufferSize);
+ }
+ }
+ }
+ else
+ {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queuing skipped, empty content {}", content);
+ }
+ }
+ }
+ else
+ {
+ LOG.debug("Queuing skipped, stream already closed");
+ }
+ }
+
+ @Override
+ public void onSuccess(Response response)
+ {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queuing end of content {}{}", EOF, "");
+ }
+ queue.offer(EOF);
+ signal();
+ }
+
+ @Override
+ public void onFailure(Response response, Throwable failure)
+ {
+ fail(failure);
+ signal();
+ }
+
+ @Override
+ public void onComplete(Result result)
+ {
+ if (result.isFailed() && (failure == null)) {
+ fail(result.getFailure());
+ }
+ this.result = result;
+ resultLatch.countDown();
+ signal();
+ }
+
+ private void fail(Throwable failure)
+ {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queuing failure {} {}", FAILURE, failure);
+ }
+ queue.offer(FAILURE);
+ this.failure = failure;
+ responseLatch.countDown();
+ }
+
+ protected boolean await()
+ {
+ try
+ {
+ synchronized (this)
+ {
+ while ((length.get() >= maxBufferSize) && (failure == null) && !closed) {
+ wait();
+ }
+ // Re-read the values as they may have changed while waiting.
+ return (failure == null) && !closed;
+ }
+ }
+ catch (InterruptedException x)
+ {
+ Thread.currentThread().interrupt();
+ return false;
+ }
+ }
+
+ protected void signal()
+ {
+ synchronized (this)
+ {
+ notifyAll();
+ }
+ }
+
+ /**
+ * Waits for the given timeout for the response to be available, then returns it.
+ *
+ * The wait ends as soon as all the HTTP headers have been received, without waiting for the content.
+ * To wait for the whole content, see {@link #await(long, TimeUnit)}.
+ *
+ * @param timeout the time to wait
+ * @param unit the timeout unit
+ * @return the response
+ * @throws InterruptedException if the thread is interrupted
+ * @throws TimeoutException if the timeout expires
+ * @throws ExecutionException if a failure happened
+ */
+ public Response get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException
+ {
+ boolean expired = !responseLatch.await(timeout, unit);
+ if (expired) {
+ throw new TimeoutException();
+ }
+ if (failure != null) {
+ throw new ExecutionException(failure);
+ }
+ return response;
+ }
+
+ /**
+ * Waits for the given timeout for the whole request/response cycle to be finished,
+ * then returns the corresponding result.
+ *
+ *
+ * @param timeout the time to wait
+ * @param unit the timeout unit
+ * @return the result
+ * @throws InterruptedException if the thread is interrupted
+ * @throws TimeoutException if the timeout expires
+ * @see #get(long, TimeUnit)
+ */
+ public Result await(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
+ {
+ boolean expired = !resultLatch.await(timeout, unit);
+ if (expired) {
+ throw new TimeoutException();
+ }
+ return result;
+ }
+
+ /**
+ * Returns an {@link InputStream} providing the response content bytes.
+ *
+ * The method may be invoked only once; subsequent invocations will return a closed {@link InputStream}.
+ *
+ * @return an input stream providing the response content
+ */
+ public InputStream getInputStream()
+ {
+ InputStream result = new Input();
+ if (stream.compareAndSet(null, result)) {
+ return result;
+ }
+ return IO.getClosedStream();
+ }
+
+ private class Input extends InputStream
+ {
+ private byte[] bytes;
+ private int index;
+
+ @Override
+ public int read() throws IOException
+ {
+ while (true)
+ {
+ if (bytes == EOF)
+ {
+ // Mark the fact that we saw -1,
+ // so that in the close case we don't throw
+ index = -1;
+ return -1;
+ }
+ else if (bytes == FAILURE)
+ {
+ throw failure();
+ }
+ else if (bytes == CLOSED)
+ {
+ if (index < 0) {
+ return -1;
+ }
+ throw new AsynchronousCloseException();
+ }
+ else if (bytes != null)
+ {
+ int result = bytes[index] & 0xFF;
+ if (++index == bytes.length)
+ {
+ length.addAndGet(-index);
+ bytes = null;
+ index = 0;
+ signal();
+ }
+ return result;
+ }
+ else
+ {
+ bytes = take();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Dequeued {}/{} bytes", bytes, bytes.length);
+ }
+ }
+ }
+ }
+
+ // START AMAZON CHANGES
+ @Override
+ public int read(byte buffer[], int offset, int length) throws IOException {
+ if (buffer == null) {
+ throw new NullPointerException();
+ } else if ((offset < 0) || (length < 0) || (length > (buffer.length - offset))) {
+ throw new IndexOutOfBoundsException();
+ } else if (length == 0) {
+ return 0;
+ }
+
+ // Contract specifies must attempt to read at least one byte. If the stream is at end of file: the value -1 is returned
+ int singleByte = read();
+ if (singleByte == -1) {
+ return -1;
+ }
+ buffer[offset] = (byte)singleByte;
+
+ int bytesWritten = 1;
+ try {
+ while (bytesWritten < length) {
+ singleByte = read();
+ if (singleByte == -1) {
+ break;
+ }
+ buffer[offset + bytesWritten] = (byte)singleByte;
+ bytesWritten++;
+
+ if (queue.isEmpty()) {
+ break;
+ }
+ }
+ } catch (IOException ee) {
+ }
+ return bytesWritten;
+ }
+ // END AMAZON CHANGES
+
+ private IOException failure()
+ {
+ if (failure instanceof IOException) {
+ return (IOException)failure;
+ } else {
+ return new IOException(failure);
+ }
+ }
+
+ private byte[] take() throws IOException
+ {
+ try
+ {
+ return queue.take();
+ }
+ catch (InterruptedException x)
+ {
+ throw new InterruptedIOException();
+ }
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ if (!closed)
+ {
+ super.close();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Queuing close {}{}", CLOSED, "");
+ }
+ queue.offer(CLOSED);
+ closed = true;
+ signal();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/http/jetty/PingSendingHttpClientTransportOverHTTP2.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/http/jetty/PingSendingHttpClientTransportOverHTTP2.java
new file mode 100644
index 00000000..27e72bca
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/http/jetty/PingSendingHttpClientTransportOverHTTP2.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.http.jetty;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2;
+import org.eclipse.jetty.http2.client.http.HttpDestinationOverHTTP2;
+import org.eclipse.jetty.http2.frames.PingFrame;
+import org.eclipse.jetty.util.Callback;
+
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Sends an HTTP/2 PING frame every 5 minutes after a connection is opened.
+ */
+public class PingSendingHttpClientTransportOverHTTP2 extends HttpClientTransportOverHTTP2 {
+ private static final int PING_INTERVAL_IN_MINUTES = 5;
+ private static final int INITIAL_PING_DELAY_IN_MINUTES = PING_INTERVAL_IN_MINUTES;
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ private Optional connectionListener = Optional.empty();
+ private HttpClient httpClient;
+
+ public PingSendingHttpClientTransportOverHTTP2(HTTP2Client client, ConnectionListener connectionListener) {
+ super(client);
+ this.connectionListener = Optional.ofNullable(connectionListener);
+ }
+
+ @Override
+ public void setHttpClient(HttpClient client) {
+ super.setHttpClient(client);
+ httpClient = client;
+ }
+
+ @Override
+ protected HttpConnectionOverHTTP2 newHttpConnection(HttpDestination destination, Session session) {
+ scheduler.scheduleAtFixedRate(new ServerPing(session), INITIAL_PING_DELAY_IN_MINUTES,
+ PING_INTERVAL_IN_MINUTES, TimeUnit.MINUTES);
+ return super.newHttpConnection(destination, session);
+ }
+
+ @Override
+ public HttpDestination newHttpDestination(Origin origin) {
+ return new ConnectionStatusHttpDestinationOverHTTP2(httpClient, origin);
+ }
+
+ /**
+ * A {@link HttpDestinationOverHTTP2} to let the listener know when the connection is opened or closed.
+ */
+ public class ConnectionStatusHttpDestinationOverHTTP2 extends HttpDestinationOverHTTP2 {
+ public ConnectionStatusHttpDestinationOverHTTP2(HttpClient client, Origin origin) {
+ super(client, origin);
+ }
+
+ @Override
+ public void close(Connection connection) {
+ super.close(connection);
+ connectionListener.ifPresent(l -> l.onDisconnected());
+ }
+
+ @Override
+ public void succeeded(Connection connection) {
+ super.succeeded(connection);
+ connectionListener.ifPresent(l -> l.onConnected());
+ }
+ }
+
+ /**
+ * Task to send a PING frame over an open HTTP/2 Session.
+ */
+ private static class ServerPing implements Runnable {
+ private Session session;
+
+ private ServerPing(Session session) {
+ this.session = session;
+ }
+
+ @Override
+ public void run() {
+ if (!session.isClosed()) {
+ PingFrame frame = new PingFrame(false);
+ session.ping(frame, Callback.NOOP);
+ }
+ }
+ }
+
+ /**
+ * Listener to inform others of the connection being opened or closed.
+ */
+ public interface ConnectionListener {
+ void onConnected();
+ void onDisconnected();
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/DialogRequestIdHeader.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/DialogRequestIdHeader.java
new file mode 100644
index 00000000..eb7354a5
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/DialogRequestIdHeader.java
@@ -0,0 +1,37 @@
+
+/*
+ * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message;
+
+public class DialogRequestIdHeader extends MessageIdHeader {
+
+ private String dialogRequestId;
+
+ public DialogRequestIdHeader() {
+ // For Jackson
+ }
+
+ public DialogRequestIdHeader(String namespace, String name, String dialogRequestId) {
+ super(namespace, name);
+ this.dialogRequestId = dialogRequestId;
+ }
+
+ public final void setDialogRequestId(String dialogRequestId) {
+ this.dialogRequestId = dialogRequestId;
+ }
+
+ public final String getDialogRequestId() {
+ return dialogRequestId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%1$s dialogRequestId:%2$s", super.toString(), dialogRequestId);
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Header.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Header.java
new file mode 100644
index 00000000..618f8eab
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Header.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message;
+
+public class Header {
+ private String namespace;
+ private String name;
+
+ public Header() {
+ // For Jackson
+ }
+
+ public Header(String namespace, String name) {
+ setNamespace(namespace);
+ setName(name);
+ }
+
+ public final void setNamespace(String namespace) {
+ if (namespace == null) {
+ throw new IllegalArgumentException("Header namespace must not be null");
+ }
+ this.namespace = namespace;
+ }
+
+ public final void setName(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Header name must not be null");
+ }
+ this.name = name;
+ }
+
+ public final String getNamespace() {
+ return namespace;
+ }
+
+ public final String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%1$s:%2$s", namespace, name);
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Message.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Message.java
new file mode 100644
index 00000000..ce6995e9
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Message.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message;
+
+import com.amazon.alexa.avs.AVSAPIConstants;
+import com.amazon.alexa.avs.config.ObjectMapperFactory;
+import com.amazon.alexa.avs.message.Message.MessageDeserializer;
+import com.amazon.alexa.avs.message.response.AlexaExceptionResponse;
+import com.amazon.alexa.avs.message.response.Directive;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.JsonParseException;
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.JsonProcessingException;
+import org.codehaus.jackson.annotate.JsonIgnore;
+import org.codehaus.jackson.map.DeserializationContext;
+import org.codehaus.jackson.map.JsonDeserializer;
+import org.codehaus.jackson.map.JsonMappingException;
+import org.codehaus.jackson.map.ObjectReader;
+import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import org.codehaus.jackson.node.ObjectNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+/**
+ * A message from the server. Can be an
+ * {@link com.amazon.alexa.avs.message.response.system.Exception Exception},
+ * {@link com.amazon.alexa.avs.message.request.Event Event} , or {@link Directive}
+ */
+@JsonDeserialize(using = MessageDeserializer.class)
+public abstract class Message {
+ protected Header header;
+ protected Payload payload;
+
+ @JsonIgnore
+ private String rawMessage;
+
+ protected Message(Header header, JsonNode payload, String rawMessage)
+ throws JsonParseException, JsonMappingException, IOException {
+
+ this.header = header;
+ try {
+ ObjectReader reader = ObjectMapperFactory.getObjectReader();
+ Class> type = Class.forName(getClass().getPackage().getName() + "."
+ + header.getNamespace().toLowerCase() + "." + header.getName());
+ this.payload = (Payload) reader.withType(type).readValue(payload);
+ } catch (ClassNotFoundException e) {
+ // Default to empty payload
+ this.payload = new Payload();
+ }
+
+ this.rawMessage = rawMessage;
+ }
+
+ protected Message(Header header, Payload payload, String rawMessage) {
+ this.header = header;
+ this.payload = payload;
+ this.rawMessage = rawMessage;
+ }
+
+ @JsonIgnore
+ public String getName() {
+ return header.getName();
+ }
+
+ @JsonIgnore
+ public String getNamespace() {
+ return header.getNamespace();
+ }
+
+ public void setHeader(Header header) {
+ this.header = header;
+ }
+
+ public Header getHeader() {
+ return header;
+ }
+
+ public void setPayload(Payload payload) {
+ this.payload = payload;
+ }
+
+ public Payload getPayload() {
+ return payload;
+ }
+
+ public String getRawMessage() {
+ return rawMessage;
+ }
+
+ @Override
+ public String toString() {
+ return header.toString();
+ }
+
+ public static class MessageDeserializer extends JsonDeserializer {
+ private static final Logger log = LoggerFactory.getLogger(MessageDeserializer.class);
+
+ @Override
+ public Message deserialize(JsonParser jp, DeserializationContext ctx)
+ throws IOException, JsonProcessingException {
+ ObjectReader reader = ObjectMapperFactory.getObjectReader();
+ ObjectNode obj = (ObjectNode) reader.readTree(jp);
+ Iterator> elementsIterator = obj.getFields();
+
+ String rawMessage = obj.toString();
+
+ DialogRequestIdHeader header = null;
+ JsonNode payloadNode = null;
+ ObjectReader headerReader =
+ ObjectMapperFactory.getObjectReader(DialogRequestIdHeader.class);
+ while (elementsIterator.hasNext()) {
+ Entry element = elementsIterator.next();
+ if (element.getKey().equals("header")) {
+ header = headerReader.readValue(element.getValue());
+ }
+ if (element.getKey().equals("payload")) {
+ payloadNode = element.getValue();
+ }
+ }
+ if (header == null) {
+ throw ctx.mappingException("Missing header");
+ }
+ if (payloadNode == null) {
+ throw ctx.mappingException("Missing payload");
+ }
+
+ return createMessage(header, payloadNode, rawMessage);
+ }
+
+ private Message createMessage(Header header, JsonNode payload, String rawMessage)
+ throws JsonParseException, JsonMappingException, IOException {
+ if (AVSAPIConstants.System.NAMESPACE.equals(header.getNamespace())
+ && AVSAPIConstants.System.Exception.NAME.equals(header.getName())) {
+ return new AlexaExceptionResponse(header, payload, rawMessage);
+ } else {
+ return new Directive(header, payload, rawMessage);
+ }
+ }
+
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/MessageIdHeader.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/MessageIdHeader.java
new file mode 100644
index 00000000..72b5f540
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/MessageIdHeader.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.amazon.alexa.avs.message;
+
+import java.util.UUID;
+
+public class MessageIdHeader extends Header {
+ private String messageId;
+
+ public MessageIdHeader() {
+ // For Jackson
+ }
+
+ public MessageIdHeader(String namespace, String name) {
+ super(namespace, name);
+ this.messageId = UUID.randomUUID().toString();
+ }
+
+ public final void setMessageId(String messageId) {
+ this.messageId = messageId;
+ }
+
+ public final String getMessageId() {
+ return messageId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%1$s id:%2$s", super.toString(), messageId);
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Payload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Payload.java
new file mode 100644
index 00000000..9e31b7d1
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/Payload.java
@@ -0,0 +1,16 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
+public class Payload {
+
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/ComponentStateFactory.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/ComponentStateFactory.java
new file mode 100644
index 00000000..059578a5
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/ComponentStateFactory.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request;
+
+import com.amazon.alexa.avs.AVSAPIConstants;
+import com.amazon.alexa.avs.message.Header;
+import com.amazon.alexa.avs.message.request.context.AlertsStatePayload;
+import com.amazon.alexa.avs.message.request.context.ComponentState;
+import com.amazon.alexa.avs.message.request.context.PlaybackStatePayload;
+import com.amazon.alexa.avs.message.request.context.SpeechStatePayload;
+import com.amazon.alexa.avs.message.request.context.VolumeStatePayload;
+
+public class ComponentStateFactory {
+
+ public static ComponentState createPlaybackState(PlaybackStatePayload playerState) {
+ return new ComponentState(new Header(AVSAPIConstants.AudioPlayer.NAMESPACE,
+ AVSAPIConstants.AudioPlayer.Events.PlaybackState.NAME), playerState);
+ }
+
+ public static ComponentState createSpeechState(SpeechStatePayload speechState) {
+ return new ComponentState(new Header(AVSAPIConstants.SpeechSynthesizer.NAMESPACE,
+ AVSAPIConstants.SpeechSynthesizer.Events.SpeechState.NAME), speechState);
+ }
+
+ public static ComponentState createAlertState(AlertsStatePayload alertState) {
+ return new ComponentState(new Header(AVSAPIConstants.Alerts.NAMESPACE,
+ AVSAPIConstants.Alerts.Events.AlertsState.NAME), alertState);
+ }
+
+ public static ComponentState createVolumeState(VolumeStatePayload volumeState) {
+ return new ComponentState(new Header(AVSAPIConstants.Speaker.NAMESPACE,
+ AVSAPIConstants.Speaker.Events.VolumeState.NAME), volumeState);
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/ContextEventRequestBody.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/ContextEventRequestBody.java
new file mode 100644
index 00000000..8d418693
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/ContextEventRequestBody.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.amazon.alexa.avs.message.request;
+
+import com.amazon.alexa.avs.message.request.context.ComponentState;
+
+import java.util.List;
+
+public class ContextEventRequestBody extends RequestBody {
+
+ private final List context;
+
+ public ContextEventRequestBody(List context, Event event) {
+ super(event);
+ this.context = context;
+ }
+
+ public final List getContext() {
+ return context;
+ }
+
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/Event.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/Event.java
new file mode 100644
index 00000000..c072a008
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/Event.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request;
+
+import com.amazon.alexa.avs.message.Header;
+import com.amazon.alexa.avs.message.Message;
+import com.amazon.alexa.avs.message.Payload;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * A message from the client to the server
+ */
+public class Event extends Message {
+
+ public Event(Header header, Payload payload) {
+ super(header, payload, StringUtils.EMPTY);
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/RequestBody.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/RequestBody.java
new file mode 100644
index 00000000..d52f51d7
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/RequestBody.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.amazon.alexa.avs.message.request;
+
+public class RequestBody {
+
+ private final Event event;
+
+ public RequestBody(Event event) {
+ this.event = event;
+ }
+
+ public final Event getEvent() {
+ return event;
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/RequestFactory.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/RequestFactory.java
new file mode 100644
index 00000000..f38641a2
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/RequestFactory.java
@@ -0,0 +1,300 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request;
+
+import com.amazon.alexa.avs.AVSAPIConstants;
+import com.amazon.alexa.avs.SpeechProfile;
+import com.amazon.alexa.avs.exception.DirectiveHandlingException.ExceptionType;
+import com.amazon.alexa.avs.message.DialogRequestIdHeader;
+import com.amazon.alexa.avs.message.Header;
+import com.amazon.alexa.avs.message.MessageIdHeader;
+import com.amazon.alexa.avs.message.Payload;
+import com.amazon.alexa.avs.message.request.alerts.AlertPayload;
+import com.amazon.alexa.avs.message.request.audioplayer.AudioPlayerPayload;
+import com.amazon.alexa.avs.message.request.audioplayer.PlaybackFailedPayload;
+import com.amazon.alexa.avs.message.request.audioplayer.PlaybackFailedPayload.ErrorType;
+import com.amazon.alexa.avs.message.request.audioplayer.PlaybackStutterFinishedPayload;
+import com.amazon.alexa.avs.message.request.context.AlertsStatePayload;
+import com.amazon.alexa.avs.message.request.context.ComponentState;
+import com.amazon.alexa.avs.message.request.context.PlaybackStatePayload;
+import com.amazon.alexa.avs.message.request.context.SpeechStatePayload;
+import com.amazon.alexa.avs.message.request.context.VolumeStatePayload;
+import com.amazon.alexa.avs.message.request.speechrecognizer.SpeechRecognizerPayload;
+import com.amazon.alexa.avs.message.request.speechsynthesizer.SpeechLifecyclePayload;
+import com.amazon.alexa.avs.message.request.system.ExceptionEncounteredPayload;
+import com.amazon.alexa.avs.message.request.system.UserInactivityReportPayload;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class RequestFactory {
+
+ public interface Request {
+ RequestBody withPlaybackStatePayload(PlaybackStatePayload state);
+ }
+
+ public static RequestBody createSpeechRegonizerRecognizeRequest(String dialogRequestId,
+ SpeechProfile profile, String format, PlaybackStatePayload playerState,
+ SpeechStatePayload speechState, AlertsStatePayload alertState,
+ VolumeStatePayload volumeState) {
+ SpeechRecognizerPayload payload = new SpeechRecognizerPayload(profile, format);
+ Header header = new DialogRequestIdHeader(AVSAPIConstants.SpeechRecognizer.NAMESPACE,
+ AVSAPIConstants.SpeechRecognizer.Events.Recognize.NAME, dialogRequestId);
+ Event event = new Event(header, payload);
+ return createRequestWithAllState(event, playerState, speechState, alertState, volumeState);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackStartedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(AVSAPIConstants.AudioPlayer.Events.PlaybackStarted.NAME,
+ streamToken, offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackNearlyFinishedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(
+ AVSAPIConstants.AudioPlayer.Events.PlaybackNearlyFinished.NAME, streamToken,
+ offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackStutterStartedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(
+ AVSAPIConstants.AudioPlayer.Events.PlaybackStutterStarted.NAME, streamToken,
+ offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackStutterFinishedEvent(String streamToken,
+ long offsetInMilliseconds, long stutterDurationInMilliseconds) {
+ Header header = new MessageIdHeader(AVSAPIConstants.AudioPlayer.NAMESPACE,
+ AVSAPIConstants.AudioPlayer.Events.PlaybackStutterFinished.NAME);
+ Event event = new Event(header, new PlaybackStutterFinishedPayload(streamToken,
+ offsetInMilliseconds, stutterDurationInMilliseconds));
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackFinishedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(AVSAPIConstants.AudioPlayer.Events.PlaybackFinished.NAME,
+ streamToken, offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackStoppedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(AVSAPIConstants.AudioPlayer.Events.PlaybackStopped.NAME,
+ streamToken, offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackPausedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(AVSAPIConstants.AudioPlayer.Events.PlaybackPaused.NAME,
+ streamToken, offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackResumedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(AVSAPIConstants.AudioPlayer.Events.PlaybackResumed.NAME,
+ streamToken, offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackQueueClearedEvent() {
+ Header header = new MessageIdHeader(AVSAPIConstants.AudioPlayer.NAMESPACE,
+ AVSAPIConstants.AudioPlayer.Events.PlaybackQueueCleared.NAME);
+ Event event = new Event(header, new Payload());
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createAudioPlayerPlaybackFailedEvent(String streamToken,
+ PlaybackStatePayload playbackStatePayload, ErrorType errorType) {
+ Header header = new MessageIdHeader(AVSAPIConstants.AudioPlayer.NAMESPACE,
+ AVSAPIConstants.AudioPlayer.Events.PlaybackFailed.NAME);
+ Event event = new Event(header,
+ new PlaybackFailedPayload(streamToken, playbackStatePayload, errorType));
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createAudioPlayerProgressReportDelayElapsedEvent(String streamToken,
+ long offsetInMilliseconds) {
+ return createAudioPlayerEvent(
+ AVSAPIConstants.AudioPlayer.Events.ProgressReportDelayElapsed.NAME, streamToken,
+ offsetInMilliseconds);
+ }
+
+ public static RequestBody createAudioPlayerProgressReportIntervalElapsedEvent(
+ String streamToken, long offsetInMilliseconds) {
+ return createAudioPlayerEvent(
+ AVSAPIConstants.AudioPlayer.Events.ProgressReportIntervalElapsed.NAME, streamToken,
+ offsetInMilliseconds);
+ }
+
+ private static RequestBody createAudioPlayerEvent(String name, String streamToken,
+ long offsetInMilliseconds) {
+ Header header = new MessageIdHeader(AVSAPIConstants.AudioPlayer.NAMESPACE, name);
+ Payload payload = new AudioPlayerPayload(streamToken, offsetInMilliseconds);
+ Event event = new Event(header, payload);
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createPlaybackControllerNextEvent(PlaybackStatePayload playbackState,
+ SpeechStatePayload speechState, AlertsStatePayload alertState,
+ VolumeStatePayload volumeState) {
+ return createPlaybackControllerEvent(
+ AVSAPIConstants.PlaybackController.Events.NextCommandIssued.NAME, playbackState,
+ speechState, alertState, volumeState);
+ }
+
+ public static RequestBody createPlaybackControllerPreviousEvent(
+ PlaybackStatePayload playbackState, SpeechStatePayload speechState,
+ AlertsStatePayload alertState, VolumeStatePayload volumeState) {
+ return createPlaybackControllerEvent(
+ AVSAPIConstants.PlaybackController.Events.PreviousCommandIssued.NAME, playbackState,
+ speechState, alertState, volumeState);
+ }
+
+ public static RequestBody createPlaybackControllerPlayEvent(PlaybackStatePayload playbackState,
+ SpeechStatePayload speechState, AlertsStatePayload alertState,
+ VolumeStatePayload volumeState) {
+ return createPlaybackControllerEvent(
+ AVSAPIConstants.PlaybackController.Events.PlayCommandIssued.NAME, playbackState,
+ speechState, alertState, volumeState);
+ }
+
+ public static RequestBody createPlaybackControllerPauseEvent(PlaybackStatePayload playbackState,
+ SpeechStatePayload speechState, AlertsStatePayload alertState,
+ VolumeStatePayload volumeState) {
+ return createPlaybackControllerEvent(
+ AVSAPIConstants.PlaybackController.Events.PauseCommandIssued.NAME, playbackState,
+ speechState, alertState, volumeState);
+ }
+
+ private static RequestBody createPlaybackControllerEvent(String name,
+ PlaybackStatePayload playbackState, SpeechStatePayload speechState,
+ AlertsStatePayload alertState, VolumeStatePayload volumeState) {
+ Header header = new MessageIdHeader(AVSAPIConstants.PlaybackController.NAMESPACE, name);
+ Event event = new Event(header, new Payload());
+ return createRequestWithAllState(event, playbackState, speechState, alertState,
+ volumeState);
+ }
+
+ public static RequestBody createSpeechSynthesizerSpeechStartedEvent(String speakToken) {
+ return createSpeechSynthesizerEvent(
+ AVSAPIConstants.SpeechSynthesizer.Events.SpeechStarted.NAME, speakToken);
+ }
+
+ public static RequestBody createSpeechSynthesizerSpeechFinishedEvent(String speakToken) {
+ return createSpeechSynthesizerEvent(
+ AVSAPIConstants.SpeechSynthesizer.Events.SpeechFinished.NAME, speakToken);
+ }
+
+ private static RequestBody createSpeechSynthesizerEvent(String name, String speakToken) {
+ Header header = new MessageIdHeader(AVSAPIConstants.SpeechSynthesizer.NAMESPACE, name);
+ Event event = new Event(header, new SpeechLifecyclePayload(speakToken));
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createAlertsSetAlertEvent(String alertToken, boolean success) {
+ if (success) {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.SetAlertSucceeded.NAME,
+ alertToken);
+ } else {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.SetAlertFailed.NAME, alertToken);
+ }
+ }
+
+ public static RequestBody createAlertsDeleteAlertEvent(String alertToken, boolean success) {
+ if (success) {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.DeleteAlertSucceeded.NAME,
+ alertToken);
+ } else {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.DeleteAlertFailed.NAME,
+ alertToken);
+ }
+ }
+
+ public static RequestBody createAlertsAlertStartedEvent(String alertToken) {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.AlertStarted.NAME, alertToken);
+ }
+
+ public static RequestBody createAlertsAlertStoppedEvent(String alertToken) {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.AlertStopped.NAME, alertToken);
+ }
+
+ public static RequestBody createAlertsAlertEnteredForegroundEvent(String alertToken) {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.AlertEnteredForeground.NAME,
+ alertToken);
+ }
+
+ public static RequestBody createAlertsAlertEnteredBackgroundEvent(String alertToken) {
+ return createAlertsEvent(AVSAPIConstants.Alerts.Events.AlertEnteredBackground.NAME,
+ alertToken);
+ }
+
+ private static RequestBody createAlertsEvent(String name, String alertToken) {
+ Header header = new MessageIdHeader(AVSAPIConstants.Alerts.NAMESPACE, name);
+ Payload payload = new AlertPayload(alertToken);
+ Event event = new Event(header, payload);
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createSpeakerVolumeChangedEvent(long volume, boolean muted) {
+ return createSpeakerEvent(AVSAPIConstants.Speaker.Events.VolumeChanged.NAME, volume, muted);
+ }
+
+ public static RequestBody createSpeakerMuteChangedEvent(long volume, boolean muted) {
+ return createSpeakerEvent(AVSAPIConstants.Speaker.Events.MuteChanged.NAME, volume, muted);
+ }
+
+ public static RequestBody createSpeakerEvent(String name, long volume, boolean muted) {
+ Header header = new MessageIdHeader(AVSAPIConstants.Speaker.NAMESPACE, name);
+
+ Event event = new Event(header, new VolumeStatePayload(volume, muted));
+ return new RequestBody(event);
+ }
+
+ public static RequestBody createSystemSynchronizeStateEvent(PlaybackStatePayload playerState,
+ SpeechStatePayload speechState, AlertsStatePayload alertState,
+ VolumeStatePayload volumeState) {
+ Header header = new MessageIdHeader(AVSAPIConstants.System.NAMESPACE,
+ AVSAPIConstants.System.Events.SynchronizeState.NAME);
+ Event event = new Event(header, new Payload());
+ return createRequestWithAllState(event, playerState, speechState, alertState, volumeState);
+ }
+
+ public static RequestBody createSystemExceptionEncounteredEvent(String directiveJson,
+ ExceptionType type, String message, PlaybackStatePayload playbackState,
+ SpeechStatePayload speechState, AlertsStatePayload alertState,
+ VolumeStatePayload volumeState) {
+ Header header = new MessageIdHeader(AVSAPIConstants.System.NAMESPACE,
+ AVSAPIConstants.System.Events.ExceptionEncountered.NAME);
+
+ Event event =
+ new Event(header, new ExceptionEncounteredPayload(directiveJson, type, message));
+
+ return createRequestWithAllState(event, playbackState, speechState, alertState,
+ volumeState);
+ }
+
+ public static RequestBody createSystemUserInactivityReportEvent(long inactiveTimeInSeconds) {
+ Header header = new MessageIdHeader(AVSAPIConstants.System.NAMESPACE,
+ AVSAPIConstants.System.Events.UserInactivityReport.NAME);
+ Event event = new Event(header, new UserInactivityReportPayload(inactiveTimeInSeconds));
+ return new RequestBody(event);
+ }
+
+ private static RequestBody createRequestWithAllState(Event event,
+ PlaybackStatePayload playbackState, SpeechStatePayload speechState,
+ AlertsStatePayload alertState, VolumeStatePayload volumeState) {
+ List context =
+ Arrays.asList(ComponentStateFactory.createPlaybackState(playbackState),
+ ComponentStateFactory.createSpeechState(speechState),
+ ComponentStateFactory.createAlertState(alertState),
+ ComponentStateFactory.createVolumeState(volumeState));
+ return new ContextEventRequestBody(context, event);
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/alerts/AlertPayload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/alerts/AlertPayload.java
new file mode 100644
index 00000000..94c69b9b
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/alerts/AlertPayload.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request.alerts;
+
+import com.amazon.alexa.avs.message.Payload;
+
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+public final class AlertPayload extends Payload {
+
+ private final String token;
+
+ public AlertPayload(String token) {
+ this.token = token;
+ }
+
+ public String getToken() {
+ return token;
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/AudioPlayerPayload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/AudioPlayerPayload.java
new file mode 100644
index 00000000..482b1edf
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/AudioPlayerPayload.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request.audioplayer;
+
+import com.amazon.alexa.avs.message.Payload;
+
+public class AudioPlayerPayload extends Payload {
+
+ private final String token;
+ private final long offsetInMilliseconds;
+
+ public AudioPlayerPayload(String token, long offsetInMilliseconds) {
+ this.token = token;
+ this.offsetInMilliseconds = offsetInMilliseconds;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public long getOffsetInMilliseconds() {
+ return offsetInMilliseconds;
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackFailedPayload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackFailedPayload.java
new file mode 100644
index 00000000..e59d2e81
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackFailedPayload.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request.audioplayer;
+
+import com.amazon.alexa.avs.message.Payload;
+import com.amazon.alexa.avs.message.request.context.PlaybackStatePayload;
+
+public final class PlaybackFailedPayload extends Payload {
+
+ private final String token;
+ private final PlaybackStatePayload currentPlaybackState;
+ private final ErrorStructure error;
+
+ public PlaybackFailedPayload(String token, PlaybackStatePayload playbackState,
+ ErrorType errorType) {
+ this.token = token;
+ this.currentPlaybackState = playbackState;
+ error = new ErrorStructure(errorType);
+
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public PlaybackStatePayload getCurrentPlaybackState() {
+ return currentPlaybackState;
+ }
+
+ public ErrorStructure getError() {
+ return error;
+ }
+
+ private final static class ErrorStructure {
+ private final ErrorType type;
+ private final String message;
+
+ public ErrorStructure(ErrorType type) {
+ this.type = type;
+ this.message = type.getMessage();
+ }
+
+ public ErrorType getType() {
+ return type;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+
+ public enum ErrorType {
+ MEDIA_ERROR_UNKNOWN("An unknown error occurred"),
+ MEDIA_ERROR_INVALID_REQUEST(
+ "The server recognized the request as being malformed (bad request, unauthorized, forbidden, not found, etc)"),
+ MEDIA_ERROR_SERVICE_UNAVAILABLE("The device was unavailable to reach the service"),
+ MEDIA_ERROR_INTERNAL_SERVER_ERROR(
+ "The server accepted the request, but was unable to process it as expected"),
+ MEDIA_ERROR_INTERNAL_DEVICE_ERROR("There was an internal error on the device");
+
+ private final String message;
+
+ private ErrorType(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackNearlyFinishedPayload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackNearlyFinishedPayload.java
new file mode 100644
index 00000000..430cb288
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackNearlyFinishedPayload.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request.audioplayer;
+
+import com.amazon.alexa.avs.message.Payload;
+
+public class PlaybackNearlyFinishedPayload extends Payload {
+ private final String navigationToken;
+
+ public PlaybackNearlyFinishedPayload(String navigationToken) {
+ this.navigationToken = navigationToken;
+ }
+
+ public String getNavigationToken() {
+ return navigationToken;
+ }
+
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackStutterFinishedPayload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackStutterFinishedPayload.java
new file mode 100644
index 00000000..f1c3664e
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/audioplayer/PlaybackStutterFinishedPayload.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request.audioplayer;
+
+public class PlaybackStutterFinishedPayload extends AudioPlayerPayload {
+
+ private final long stutterDurationInMilliseconds;
+
+ public PlaybackStutterFinishedPayload(String token, long offsetInMilliseconds,
+ long stutterDurationInMilliseconds) {
+ super(token, offsetInMilliseconds);
+ this.stutterDurationInMilliseconds = stutterDurationInMilliseconds;
+ }
+
+ public long getStutterDurationInMilliseconds() {
+ return stutterDurationInMilliseconds;
+ }
+}
diff --git a/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/context/AlertsStatePayload.java b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/context/AlertsStatePayload.java
new file mode 100644
index 00000000..4a257e14
--- /dev/null
+++ b/samples/javaclient/src/main/java/com/amazon/alexa/avs/message/request/context/AlertsStatePayload.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * You may not use this file except in compliance with the License. A copy of the License is located the "LICENSE.txt"
+ * file accompanying this source. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.amazon.alexa.avs.message.request.context;
+
+import com.amazon.alexa.avs.Alert;
+import com.amazon.alexa.avs.message.Payload;
+
+import java.util.List;
+
+public final class AlertsStatePayload extends Payload {
+
+ private final List allAlerts;
+ private final List