@Override public void onReconnected() { log.info("reconnected"); if (mode.contains(MODE.RESEND_SUBSCRIBE_ON_RECONNECTED)) { Set<String> channels; // channel as sourceDispatcherId channels = new HashSet<String>(); channels.add(getSourceDispatcherId()); driverImpl.subscribeChannels(channels); // channels as object_IDs registered with localObjectsMap channels = localObjectsMap.keySet(); if (!channels.isEmpty()) { driverImpl.subscribeChannels(channels); } // all channels registered with subscribersMap channels = subscribersMap.getSubscribedChannels(); if (!channels.isEmpty()) { driverImpl.subscribeChannels(channels); } // re-SUBSCRIBE completed with all the registered channels. pubSubDriverSuspended = false; // Resumed } else { // You may add some code here to inform other objects that // the network connectivity has resumed or the pubsub server // has become available. } }
/** * Adds a local object as a listener of messages. * * <p>The user (i.e., RemoteObject) of this class calls this method to register the RemoteObject * as "local RemoteObject" with this class. * * <pre> * [RemoteObject] [RemoteObject] [RemoteObject] * dispatchEvent() dispatchRequest() dispatchRequest() * ^ ^ ^ * | | | * +---------------+ + +---------------+ * | | | * [MessageDispatcher]<>-----[SubscribersMap] * | * message * | * [pubsub server] * </pre> * * <p>This method also sends "SUBSCRIBE own-object-ID-as-channel" to pubsub server to receive * PUBLISH destined to the remote object, since requestSync() method sends PUBLISH as "request" to * the remote object. * * @param localObject a remote object * @see org.o3project.odenos.remoteobject.RemoteObject#dispatchEvent * @see org.o3project.odenos.remoteobject.RemoteObject#dispatchRequest */ public void addLocalObject(RemoteObject localObject) { String objectId = localObject.getObjectId(); if (localObjectsMap.putIfAbsent(objectId, localObject) == null) { driverImpl.subscribeChannel(objectId); } if (objectId.equals(systemManagerId)) { driverImpl.systemManagerAttached(); } }
/** * Asynchronous event publication service for requestSync(). * * <p>The response eventually reaches its originating method (requestSync()) via * RemoteMessageTransport. */ public void publishResponseAsync( final int sno, final String channel, final Request request, final Response response) throws IOException { BufferPacker pk = msgpack.createBufferPacker(); // write delivery header. pk.write(TYPE_RESPONSE); pk.write(sno); pk.write(request.objectId); // write delivery body. pk.write(response); byte[] message = pk.toByteArray(); // PUBLISH driverImpl.publish(channel, message); if (log.isDebugEnabled()) { log.debug( "Response returned:\n" + " MessageDispatcher: {}\n" + " channel: {}\n" + " statusCode: {}\n" + " body: {}", sourceDispatcherId, channel, response.statusCode, response.getBodyValue()); } }
/** * 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()); } }
/** * Removes a local object. * * <p>This method also sends "UNSUBSCRIBE own-object-ID-as-channel" to pubsub server to stop * receiving PUBLISH destined to the remote object. * * @param localObject a remote object */ public void removeLocalObject(RemoteObject localObject) { String objectId = localObject.getObjectId(); if (localObjectsMap.remove(objectId) != null) { // Unsubscribes objectId as a channel to stop receiving Request // from remote objects via PubSub. driverImpl.unsubscribeChannel(objectId); } }
/** * Closes the services. * * <p>You can also do "try-with-resources" to automatically close this class. */ public void close() { // TODO: Graceful termination of all the components and the transport // TODO: subscriptionFeeder termination driverImpl.close(); remoteTransactions.onFinalize(); subscribersMap.clear(); log.info("terminated"); }
/** * Starts the services. * * <p>This method must be called after instantiating this class to start a {@link IPubSubDriver} * implementation class. */ public void start() { // This method blocks until the connectivity with pubsub server // has become ready. driverImpl.start(); // To receive Request from "remote" RemoteObject, // MessageDispatcher needs to register itself w/ pubsub server. driverImpl.subscribeChannel(sourceDispatcherId); // This thread feeds subscription info to EventManager // in an eventually-consistent manner. subscriptionFeeder = new Thread( new Runnable() { @Override public void run() { do { Request request = null; try { request = eventManagerQueue.take(); Response response = requestSync(request); if (response == null || !response.statusCode.equals(Response.OK)) { log.warn("Unsuccessful transaction to EventManager: " + response.statusCode); } } catch (InterruptedException e) { log.warn("Unsuccessful transaction to EventManager due to some internal error"); } catch (Exception e) { log.warn("EventManager may be inactive"); } } while (true); // TODO: graceful thread termination } }); subscriptionFeeder.start(); log.info("started"); }
/** * Channel unsubscription service. * * @param subscriberId subscriber's object ID * @param channelsToBeUnsubscribed channels to be unsubscribed */ public void unsubscribeChannels( final String subscriberId, final Map<String, Set<String>> channelsToBeUnsubscribed) { Set<String> channels = new HashSet<>(); for (String publisherId : channelsToBeUnsubscribed.keySet()) { Set<String> eventIds = channelsToBeUnsubscribed.get(publisherId); for (String eventId : eventIds) { String channel = channelString(publisherId, eventId); if (subscribersMap.removeSubscription(channel, subscriberId)) { channels.add(channel); } } } if (!channels.isEmpty() && !pubSubDriverSuspended) { driverImpl.unsubscribeChannels(channels); } }
/** * Asynchronous event publication service * * <p>Remote objects use this method to publish an event asynchronously. */ public void publishEventAsync(final Event event) throws IOException { BufferPacker pk = msgpack.createBufferPacker(); // write delivery header. pk.write(TYPE_EVENT); pk.write(0); pk.write("event"); // write delivery body. pk.write(event); byte[] message = pk.toByteArray(); String channel = channelString(event.publisherId, event.eventType); // PUBLISH driverImpl.publish(channel, message); if (log.isDebugEnabled()) { log.debug( "Event sent:\n" + " MessageDispatcher: {}\n" + " channel: {}\n" + " body: {}", sourceDispatcherId, channel, event.getBodyValue()); } }