OLD | NEW |
| (Empty) |
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. | |
4 | |
5 package org.chromium.mojo.bindings; | |
6 | |
7 import org.chromium.mojo.system.AsyncWaiter; | |
8 import org.chromium.mojo.system.Core; | |
9 import org.chromium.mojo.system.MessagePipeHandle; | |
10 | |
11 import java.util.HashMap; | |
12 import java.util.Map; | |
13 import java.util.concurrent.Executor; | |
14 | |
15 /** | |
16 * Implementation of {@link Router}. | |
17 */ | |
18 public class RouterImpl implements Router { | |
19 | |
20 /** | |
21 * {@link MessageReceiver} used as the {@link Connector} callback. | |
22 */ | |
23 private class HandleIncomingMessageThunk implements MessageReceiver { | |
24 | |
25 /** | |
26 * @see MessageReceiver#accept(Message) | |
27 */ | |
28 @Override | |
29 public boolean accept(Message message) { | |
30 return handleIncomingMessage(message); | |
31 } | |
32 | |
33 /** | |
34 * @see MessageReceiver#close() | |
35 */ | |
36 @Override | |
37 public void close() { | |
38 handleConnectorClose(); | |
39 } | |
40 | |
41 } | |
42 | |
43 /** | |
44 * | |
45 * {@link MessageReceiver} used to return responses to the caller. | |
46 */ | |
47 class ResponderThunk implements MessageReceiver { | |
48 private boolean mAcceptWasInvoked = false; | |
49 | |
50 /** | |
51 * @see | |
52 * MessageReceiver#accept(Message) | |
53 */ | |
54 @Override | |
55 public boolean accept(Message message) { | |
56 mAcceptWasInvoked = true; | |
57 return RouterImpl.this.accept(message); | |
58 } | |
59 | |
60 /** | |
61 * @see MessageReceiver#close() | |
62 */ | |
63 @Override | |
64 public void close() { | |
65 RouterImpl.this.close(); | |
66 } | |
67 | |
68 @Override | |
69 protected void finalize() throws Throwable { | |
70 if (!mAcceptWasInvoked) { | |
71 // We close the pipe here as a way of signaling to the calling a
pplication that an | |
72 // error condition occurred. Without this the calling applicatio
n would have no | |
73 // way of knowing it should stop waiting for a response. | |
74 RouterImpl.this.closeOnHandleThread(); | |
75 } | |
76 super.finalize(); | |
77 } | |
78 } | |
79 | |
80 /** | |
81 * The {@link Connector} which is connected to the handle. | |
82 */ | |
83 private final Connector mConnector; | |
84 | |
85 /** | |
86 * The {@link MessageReceiverWithResponder} that will consume the messages r
eceived from the | |
87 * pipe. | |
88 */ | |
89 private MessageReceiverWithResponder mIncomingMessageReceiver; | |
90 | |
91 /** | |
92 * The next id to use for a request id which needs a response. It is auto-in
cremented. | |
93 */ | |
94 private long mNextRequestId = 1; | |
95 | |
96 /** | |
97 * The map from request ids to {@link MessageReceiver} of request currently
in flight. | |
98 */ | |
99 private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageRe
ceiver>(); | |
100 | |
101 /** | |
102 * An Executor that will run on the thread associated with the MessagePipe t
o which | |
103 * this Router is bound. This may be {@code Null} if the MessagePipeHandle p
assed | |
104 * in to the constructor is not valid. | |
105 */ | |
106 private final Executor mExecutor; | |
107 | |
108 /** | |
109 * Constructor that will use the default {@link AsyncWaiter}. | |
110 * | |
111 * @param messagePipeHandle The {@link MessagePipeHandle} to route message f
or. | |
112 */ | |
113 public RouterImpl(MessagePipeHandle messagePipeHandle) { | |
114 this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(me
ssagePipeHandle)); | |
115 } | |
116 | |
117 /** | |
118 * Constructor. | |
119 * | |
120 * @param messagePipeHandle The {@link MessagePipeHandle} to route message f
or. | |
121 * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of
new messages on the | |
122 * handle. | |
123 */ | |
124 public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWait
er) { | |
125 mConnector = new Connector(messagePipeHandle, asyncWaiter); | |
126 mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk()); | |
127 Core core = messagePipeHandle.getCore(); | |
128 if (core != null) { | |
129 mExecutor = ExecutorFactory.getExecutorForCurrentThread(core); | |
130 } else { | |
131 mExecutor = null; | |
132 } | |
133 } | |
134 | |
135 /** | |
136 * @see org.chromium.mojo.bindings.Router#start() | |
137 */ | |
138 @Override | |
139 public void start() { | |
140 mConnector.start(); | |
141 } | |
142 | |
143 /** | |
144 * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder) | |
145 */ | |
146 @Override | |
147 public void setIncomingMessageReceiver(MessageReceiverWithResponder incoming
MessageReceiver) { | |
148 this.mIncomingMessageReceiver = incomingMessageReceiver; | |
149 } | |
150 | |
151 /** | |
152 * @see MessageReceiver#accept(Message) | |
153 */ | |
154 @Override | |
155 public boolean accept(Message message) { | |
156 // A message without responder is directly forwarded to the connector. | |
157 return mConnector.accept(message); | |
158 } | |
159 | |
160 /** | |
161 * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageRec
eiver) | |
162 */ | |
163 @Override | |
164 public boolean acceptWithResponder(Message message, MessageReceiver responde
r) { | |
165 // The message must have a header. | |
166 ServiceMessage messageWithHeader = message.asServiceMessage(); | |
167 // Checking the message expects a response. | |
168 assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPEC
TS_RESPONSE_FLAG); | |
169 | |
170 // Compute a request id for being able to route the response. | |
171 long requestId = mNextRequestId++; | |
172 // Reserve 0 in case we want it to convey special meaning in the future. | |
173 if (requestId == 0) { | |
174 requestId = mNextRequestId++; | |
175 } | |
176 if (mResponders.containsKey(requestId)) { | |
177 throw new IllegalStateException("Unable to find a new request identi
fier."); | |
178 } | |
179 messageWithHeader.setRequestId(requestId); | |
180 mResponders.put(requestId, responder); | |
181 if (!mConnector.accept(messageWithHeader)) { | |
182 // Only keep the responder is the message has been accepted. | |
183 mResponders.remove(requestId); | |
184 return false; | |
185 } | |
186 return true; | |
187 } | |
188 | |
189 /** | |
190 * @see org.chromium.mojo.bindings.HandleOwner#passHandle() | |
191 */ | |
192 @Override | |
193 public MessagePipeHandle passHandle() { | |
194 return mConnector.passHandle(); | |
195 } | |
196 | |
197 /** | |
198 * @see java.io.Closeable#close() | |
199 */ | |
200 @Override | |
201 public void close() { | |
202 mConnector.close(); | |
203 } | |
204 | |
205 /** | |
206 * @see Router#setErrorHandler(ConnectionErrorHandler) | |
207 */ | |
208 @Override | |
209 public void setErrorHandler(ConnectionErrorHandler errorHandler) { | |
210 mConnector.setErrorHandler(errorHandler); | |
211 } | |
212 | |
213 /** | |
214 * Receive a message from the connector. Returns |true| if the message has b
een handled. | |
215 */ | |
216 private boolean handleIncomingMessage(Message message) { | |
217 MessageHeader header = message.asServiceMessage().getHeader(); | |
218 if (header.hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG)) { | |
219 if (mIncomingMessageReceiver != null) { | |
220 return mIncomingMessageReceiver.acceptWithResponder(message, new
ResponderThunk()); | |
221 } | |
222 // If we receive a request expecting a response when the client is n
ot | |
223 // listening, then we have no choice but to tear down the pipe. | |
224 close(); | |
225 return false; | |
226 } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { | |
227 long requestId = header.getRequestId(); | |
228 MessageReceiver responder = mResponders.get(requestId); | |
229 if (responder == null) { | |
230 return false; | |
231 } | |
232 mResponders.remove(requestId); | |
233 return responder.accept(message); | |
234 } else { | |
235 if (mIncomingMessageReceiver != null) { | |
236 return mIncomingMessageReceiver.accept(message); | |
237 } | |
238 // OK to drop the message. | |
239 } | |
240 return false; | |
241 } | |
242 | |
243 private void handleConnectorClose() { | |
244 if (mIncomingMessageReceiver != null) { | |
245 mIncomingMessageReceiver.close(); | |
246 } | |
247 } | |
248 | |
249 /** | |
250 * Invokes {@link #close()} asynchronously on the thread associated with | |
251 * this Router's Handle. If this Router was constructed with an invalid | |
252 * handle then this method does nothing. | |
253 */ | |
254 private void closeOnHandleThread() { | |
255 if (mExecutor != null) { | |
256 mExecutor.execute(new Runnable() { | |
257 | |
258 @Override | |
259 public void run() { | |
260 close(); | |
261 } | |
262 }); | |
263 } | |
264 } | |
265 } | |
OLD | NEW |