1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.mojo.bindings;
7 import org.chromium.mojo.system.AsyncWaiter;
8 import org.chromium.mojo.system.MessagePipeHandle;
10 import java.util.HashMap;
14 * Implementation of {@link Router}.
16 public class RouterImpl implements Router {
19 * {@link MessageReceiver} used as the {@link Connector} callback.
21 private class ResponderThunk implements MessageReceiver {
24 * @see MessageReceiver#accept(Message)
27 public boolean accept(Message message) {
28 return handleIncomingMessage(message);
32 * @see org.chromium.mojo.bindings.MessageReceiver#close()
36 handleConnectorClose();
42 * The {@link Connector} which is connected to the handle.
44 private final Connector mConnector;
47 * The {@link MessageReceiverWithResponder} that will consume the messages received from the
50 private MessageReceiverWithResponder mIncomingMessageReceiver;
53 * The next id to use for a request id which needs a response. It is auto-incremented.
55 private long mNextRequestId = 1;
58 * The map from request ids to {@link MessageReceiver} of request currently in flight.
60 private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageReceiver>();
63 * Constructor that will use the default {@link AsyncWaiter}.
65 * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
67 public RouterImpl(MessagePipeHandle messagePipeHandle) {
68 this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
74 * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
75 * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the
78 public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
79 mConnector = new Connector(messagePipeHandle, asyncWaiter);
80 mConnector.setIncomingMessageReceiver(new ResponderThunk());
84 * @see org.chromium.mojo.bindings.Router#start()
92 * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
95 public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
96 this.mIncomingMessageReceiver = incomingMessageReceiver;
100 * @see MessageReceiver#accept(Message)
103 public boolean accept(Message message) {
104 // A message without responder is directly forwarded to the connector.
105 return mConnector.accept(message);
109 * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
112 public boolean acceptWithResponder(Message message, MessageReceiver responder) {
113 // The message must have a header.
114 ServiceMessage messageWithHeader = message.asServiceMessage();
115 // Checking the message expects a response.
116 assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG);
118 // Compute a request id for being able to route the response.
119 long requestId = mNextRequestId++;
120 // Reserve 0 in case we want it to convey special meaning in the future.
121 if (requestId == 0) {
122 requestId = mNextRequestId++;
124 if (mResponders.containsKey(requestId)) {
125 throw new IllegalStateException("Unable to find a new request identifier.");
127 messageWithHeader.setRequestId(requestId);
128 if (!mConnector.accept(messageWithHeader)) {
131 // Only keep the responder is the message has been accepted.
132 mResponders.put(requestId, responder);
137 * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
140 public MessagePipeHandle passHandle() {
141 return mConnector.passHandle();
145 * @see java.io.Closeable#close()
148 public void close() {
153 * @see Router#setErrorHandler(ConnectionErrorHandler)
156 public void setErrorHandler(ConnectionErrorHandler errorHandler) {
157 mConnector.setErrorHandler(errorHandler);
161 * Receive a message from the connector. Returns |true| if the message has been handled.
163 private boolean handleIncomingMessage(Message message) {
164 MessageHeader header = message.asServiceMessage().getHeader();
165 if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) {
166 if (mIncomingMessageReceiver != null) {
167 return mIncomingMessageReceiver.acceptWithResponder(message, this);
169 // If we receive a request expecting a response when the client is not
170 // listening, then we have no choice but to tear down the pipe.
173 } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
174 long requestId = header.getRequestId();
175 MessageReceiver responder = mResponders.get(requestId);
176 if (responder == null) {
179 mResponders.remove(requestId);
180 return responder.accept(message);
182 if (mIncomingMessageReceiver != null) {
183 return mIncomingMessageReceiver.accept(message);
185 // OK to drop the message.
190 private void handleConnectorClose() {
191 if (mIncomingMessageReceiver != null) {
192 mIncomingMessageReceiver.close();