Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / mojo / public / java / bindings / src / org / chromium / mojo / bindings / RouterImpl.java
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.MessagePipeHandle;
9
10 import java.util.HashMap;
11 import java.util.Map;
12
13 /**
14  * Implementation of {@link Router}.
15  */
16 public class RouterImpl implements Router {
17
18     /**
19      * {@link MessageReceiver} used as the {@link Connector} callback.
20      */
21     private class ResponderThunk implements MessageReceiver {
22
23         /**
24          * @see MessageReceiver#accept(Message)
25          */
26         @Override
27         public boolean accept(Message message) {
28             return handleIncomingMessage(message);
29         }
30
31         /**
32          * @see org.chromium.mojo.bindings.MessageReceiver#close()
33          */
34         @Override
35         public void close() {
36             handleConnectorClose();
37         }
38
39     }
40
41     /**
42      * The {@link Connector} which is connected to the handle.
43      */
44     private final Connector mConnector;
45
46     /**
47      * The {@link MessageReceiverWithResponder} that will consume the messages received from the
48      * pipe.
49      */
50     private MessageReceiverWithResponder mIncomingMessageReceiver;
51
52     /**
53      * The next id to use for a request id which needs a response. It is auto-incremented.
54      */
55     private long mNextRequestId = 1;
56
57     /**
58      * The map from request ids to {@link MessageReceiver} of request currently in flight.
59      */
60     private Map<Long, MessageReceiver> mResponders = new HashMap<Long, MessageReceiver>();
61
62     /**
63      * Constructor that will use the default {@link AsyncWaiter}.
64      *
65      * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
66      */
67     public RouterImpl(MessagePipeHandle messagePipeHandle) {
68         this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
69     }
70
71     /**
72      * Constructor.
73      *
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
76      *            handle.
77      */
78     public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
79         mConnector = new Connector(messagePipeHandle, asyncWaiter);
80         mConnector.setIncomingMessageReceiver(new ResponderThunk());
81     }
82
83     /**
84      * @see org.chromium.mojo.bindings.Router#start()
85      */
86     @Override
87     public void start() {
88         mConnector.start();
89     }
90
91     /**
92      * @see Router#setIncomingMessageReceiver(MessageReceiverWithResponder)
93      */
94     @Override
95     public void setIncomingMessageReceiver(MessageReceiverWithResponder incomingMessageReceiver) {
96         this.mIncomingMessageReceiver = incomingMessageReceiver;
97     }
98
99     /**
100      * @see MessageReceiver#accept(Message)
101      */
102     @Override
103     public boolean accept(Message message) {
104         // A message without responder is directly forwarded to the connector.
105         return mConnector.accept(message);
106     }
107
108     /**
109      * @see MessageReceiverWithResponder#acceptWithResponder(Message, MessageReceiver)
110      */
111     @Override
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);
117
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++;
123         }
124         if (mResponders.containsKey(requestId)) {
125             throw new IllegalStateException("Unable to find a new request identifier.");
126         }
127         messageWithHeader.setRequestId(requestId);
128         if (!mConnector.accept(messageWithHeader)) {
129             return false;
130         }
131         // Only keep the responder is the message has been accepted.
132         mResponders.put(requestId, responder);
133         return true;
134     }
135
136     /**
137      * @see org.chromium.mojo.bindings.HandleOwner#passHandle()
138      */
139     @Override
140     public MessagePipeHandle passHandle() {
141         return mConnector.passHandle();
142     }
143
144     /**
145      * @see java.io.Closeable#close()
146      */
147     @Override
148     public void close() {
149         mConnector.close();
150     }
151
152     /**
153      * @see Router#setErrorHandler(ConnectionErrorHandler)
154      */
155     @Override
156     public void setErrorHandler(ConnectionErrorHandler errorHandler) {
157         mConnector.setErrorHandler(errorHandler);
158     }
159
160     /**
161      * Receive a message from the connector. Returns |true| if the message has been handled.
162      */
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);
168             }
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.
171             close();
172             return false;
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) {
177                 return false;
178             }
179             mResponders.remove(requestId);
180             return responder.accept(message);
181         } else {
182             if (mIncomingMessageReceiver != null) {
183                 return mIncomingMessageReceiver.accept(message);
184             }
185             // OK to drop the message.
186         }
187         return false;
188     }
189
190     private void handleConnectorClose() {
191         if (mIncomingMessageReceiver != null) {
192             mIncomingMessageReceiver.close();
193         }
194     }
195 }