/** * Asynchronous event publication service for requestSync(). * * <p>requestSync() turns into this method via RemoteMessageTransport. * * <p>Remote objects uses this method to publish a request as event asynchronously. * * <p>Although this method is asynchronous, {@link RemoteTransactions} provides a synchronous * method to wait for a response from another remote object. */ protected void publishRequestAsync(final int sno, final Request request) throws IOException { BufferPacker pk = msgpack.createBufferPacker(); // write delivery header. pk.write(TYPE_REQUEST); pk.write(sno); pk.write(getSourceDispatcherId()); // write delivery body. pk.write(request); byte[] message = pk.toByteArray(); String channel = request.objectId; // PUBLISH driverImpl.publish(channel, message); if (log.isDebugEnabled()) { log.debug( "Request sent:\n" + " MessageDispatcher: {}\n" + " sno: {}\n" + " channel: {}\n" + " method: {}\n" + " objectId: {}\n" + " path: /{}/\n" + " body: {}", sourceDispatcherId, sno, channel, request.method, request.objectId, request.path, request.getBodyValue()); } }
@Override protected Response onRequest(final Request req) { log.debug("onRequest: " + req.method + ", " + req.path); RequestParser<IActionCallback>.ParsedRequest parsed = parser.parse(req); if (parsed == null) { // Transfer Other Component Pattern pattern = Pattern.compile("^components/.*/.*"); if (pattern.matcher(req.path).matches()) { String compId = getDestinationCompId(req.path); String command = getDestinationPath(req.path); return transferComponent(compId, command, req.method, req.getBodyValue()); } return new Response(Response.BAD_REQUEST, null); } Response response = null; try { IActionCallback callback = parsed.getResult(); if (callback == null) { return new Response(Response.BAD_REQUEST, null); } response = callback.process(parsed); } catch (Exception e) { log.error("Exception Request: " + req.method + ", " + req.path, e); response = new Response(Response.BAD_REQUEST, null); } if (response == null) { response = new Response(Response.BAD_REQUEST, null); } return response; }
/** * onMessage implementation. * * <p>This method has two roles: * * <ul> * <li>Works like a mail transfer agent to transfer a received request or event to {@link * org.o3project.odenos.remoteobject.RemoteObject}. * <li>Supports both synchronous and asynchronous messaging. * </ul> */ @Override public void onMessage(final String channel, byte[] message) { serial++; // Serial number for incoming messages. try { BufferUnpacker upk = msgpack.createBufferUnpacker(message); // read delivery header. byte type = upk.readByte(); final int sno = upk.readInt(); // Sequence number for outgoing request messages. final String sourceObjectId = upk.readString(); // i.e., channel RemoteObject localObject = null; Queue<Mail> mailbox = null; Mail mail = null; switch (type) { case TYPE_REQUEST: // Transaction(request): synchronous operation /* * --- request --> dispatchRequest() -------> [RemoteObject] * | * <-- response --- publishResponseAsync() <-------+ */ final Request request = upk.read(Request.class); if (log.isDebugEnabled()) { log.debug( "Request received:\n" + " MessageDispatcher: {}\n" + " sno: {}\n" + " channel: {}\n" + " method: {}\n" + " objectId: {}\n" + " sourceObjectId: {}\n" + " path: /{}/\n" + " body: {}", sourceDispatcherId, sno, channel, request.method, request.objectId, sourceObjectId, request.path, request.getBodyValue()); } // Wraps the request with Mail and deliver it to a mailbox. String to = request.objectId; mail = new Mail(serial, sno, to, sourceObjectId, this, request, null); localObject = localObjectsMap.get(to); if (localObject != null) { mailbox = localObject.getMailbox(); synchronized (mailbox) { mailbox.add(mail); if (!localObject.isRunning()) { localObject.setRunning(true); // Assigns a thread to read a mail in the mailbox. actor.read(localObject); } } } break; case TYPE_RESPONSE: // Transaction(response): synchronous operation /* * publishRequestAsync() * requestSync() -> [RemoteTransaction] ---- request -----> [RemoteObject] * <-- ^ | * | publishResponseAsync() * | | * +- signalResponse() <-- response --+ */ Response response = upk.read(Response.class); if (log.isDebugEnabled()) { log.debug( "Response received:\n" + " MessageDispatcher: {}\n" + " channel: {}\n" + " statusCode: {}\n" + " body: {}", sourceDispatcherId, channel, response.statusCode, response.getBodyValue()); } remoteTransactions.signalResponse(sno, response); break; case TYPE_EVENT: // Asynchronous /* * publishEventAsync() -- event --> dispatchEvent() --> [RemoteObject] * : * [EventSubscriptionMap] */ final Event event = upk.read(Event.class); if (log.isDebugEnabled()) { log.debug( "Event received:\n" + " MessageDispatcher: {}\n" + " channel: {}\n" + " body: {}", sourceDispatcherId, channel, event.getBodyValue()); } // All the subscribers of the channel final Collection<String> subscribers = subscribersMap.getSubscribers(channel); if (subscribers == null) { // No subscribers found on the channel if (log.isDebugEnabled()) { log.debug("no subscribers subscribing the channel: {}", channel); } return; // Silently discards the event } // Wraps the event with Mail and deliver it to a mailbox. for (String subscriber : subscribers) { localObject = localObjectsMap.get(subscriber); if (localObject != null) { mail = new Mail(serial, sno, subscriber, channel, this, null, event); mailbox = localObject.getMailbox(); synchronized (mailbox) { mailbox.add(mail); if (!localObject.isRunning()) { localObject.setRunning(true); // Assigns a thread to read a mail in the mailbox. actor.read(localObject); } } } } break; default: break; } } catch (Exception e) { log.error("onMessage failed", e); } }