/*
* Copyright (C) 2012 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.errai.bus.server.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import org.jboss.errai.bus.client.api.BusMonitor;
import org.jboss.errai.bus.client.api.QueueSession;
import org.jboss.errai.bus.client.api.SubscribeListener;
import org.jboss.errai.bus.client.api.Subscription;
import org.jboss.errai.bus.client.api.UnsubscribeListener;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageCallback;
import org.jboss.errai.bus.server.api.MessageQueue;
import org.jboss.errai.bus.server.api.QueueClosedListener;
import org.jboss.errai.bus.server.api.ServerMessageBus;
import org.jboss.errai.common.client.api.Assert;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
/**
* @author Mike Brock
*/
public class MessageBusProxy implements ServerMessageBus {
// BEGIN: Only referenced in synchronized methods.
private List<Message> heldGlobalMessages = new ArrayList<Message>();
private List<Message> heldMessages = new ArrayList<Message>();
private Map<Message, Boolean> heldMessageFireListener = new LinkedHashMap<Message, Boolean>();
private Multimap<String, MessageCallback> heldSubscribe = LinkedHashMultimap.create();
private Multimap<String, MessageCallback> heldLocalSubscribe = LinkedHashMultimap.create();
private List<SubscribeListener> heldSubscribeListener = new ArrayList<SubscribeListener>();
private List<UnsubscribeListener> heldUnsubscribeListener = new ArrayList<UnsubscribeListener>();
private BusMonitor heldBusMonitor;
// END: ONly referenced in synchronized methods.
/*
* These fields must be volatile because they are read from unsynchronized methods.
*/
private volatile boolean proxyClosed;
private volatile ServerMessageBus proxied;
@Override
public void sendGlobal(Message message) {
Assert.notNull("message cannot be null", message);
if (proxyClosed || !offerSendGlobal(message)) {
proxied.sendGlobal(message);
}
}
private synchronized boolean offerSendGlobal(Message message) {
if (proxyClosed) {
return false;
} else {
heldGlobalMessages.add(message);
return true;
}
}
@Override
public void send(Message message) {
Assert.notNull("message cannot be null", message);
if (proxyClosed || !offerSend(message)) {
proxied.send(message);
}
}
private synchronized boolean offerSend(Message message) {
if (proxyClosed) {
return false;
} else {
heldMessages.add(message);
return true;
}
}
@Override
public void send(Message message, boolean fireListeners) {
Assert.notNull("message cannot be null", message);
if (proxyClosed || !offerSend(message, fireListeners)) {
proxied.send(message, fireListeners);
}
}
private synchronized boolean offerSend(Message message, boolean fireListeners) {
if (proxyClosed) {
return false;
} else {
heldMessageFireListener.put(message, fireListeners);
return true;
}
}
@Override
public Subscription subscribe(String subject, MessageCallback receiver) {
Assert.notNull("message callback cannot be null", receiver);
Subscription subscription = null;
if (!proxyClosed) {
subscription = offerSubscribe(subject, receiver);
}
if (subscription == null) {
subscription = proxied.subscribe(subject, receiver);
}
return subscription;
}
private synchronized Subscription offerSubscribe(String subject, MessageCallback receiver) {
if (proxyClosed) {
return null;
} else {
heldSubscribe.put(subject, receiver);
return new Subscription() {
@Override
public void remove() {
throw new IllegalStateException("cannot unsubscribe from a proxied MessageBus");
}
};
}
}
@Override
public Subscription subscribeLocal(String subject, MessageCallback receiver) {
Assert.notNull("message callback cannot be null", receiver);
Subscription subscription = null;
if (!proxyClosed) {
subscription = offerSubscribeLocal(subject, receiver);
}
if (subscription == null) {
subscription = proxied.subscribeLocal(subject, receiver);
}
return subscription;
}
private synchronized Subscription offerSubscribeLocal(String subject, MessageCallback receiver) {
if (proxyClosed) {
return null;
} else {
heldLocalSubscribe.put(subject, receiver);
return new Subscription() {
@Override
public void remove() {
throw new IllegalStateException("cannot unsubscribe from a proxied MessageBus");
}
};
}
}
@Override
public void unsubscribeAll(String subject) {
}
@Override
public synchronized boolean isSubscribed(String subject) {
if (proxyClosed) {
return proxied.isSubscribed(subject);
}
else {
return heldSubscribe.containsKey(subject) || heldLocalSubscribe.containsKey(subject);
}
}
@Override
public void addSubscribeListener(SubscribeListener listener) {
Assert.notNull("subscribe listener cannot be null", listener);
if (proxyClosed || !offerAddSubscribeListener(listener)) {
proxied.addSubscribeListener(listener);
}
}
private synchronized boolean offerAddSubscribeListener(SubscribeListener listener) {
if (proxyClosed) {
return false;
} else {
heldSubscribeListener.add(listener);
return true;
}
}
@Override
public void addUnsubscribeListener(UnsubscribeListener listener) {
Assert.notNull("unsubscribe listener cannot be null", listener);
if (proxyClosed || !offerAddUnsubscribeListener(listener)) {
proxied.addUnsubscribeListener(listener);
}
}
private synchronized boolean offerAddUnsubscribeListener(UnsubscribeListener listener) {
if (proxyClosed) {
return false;
} else {
heldUnsubscribeListener.add(listener);
return true;
}
}
@Override
public MessageQueue getQueue(QueueSession session) {
return proxied.getQueue(session);
}
@Override
public void closeQueue(String sessionId) {
proxied.closeQueue(sessionId);
}
@Override
public void closeQueue(MessageQueue queue) {
proxied.closeQueue(queue);
}
@Override
public ExecutorService getScheduler() {
return proxied.getScheduler();
}
@Override
public void addQueueClosedListener(QueueClosedListener listener) {
proxied.addQueueClosedListener(listener);
}
@Override
public void configure(ErraiServiceConfigurator service) {
proxied.configure(service);
}
@Override
public Collection<MessageCallback> getReceivers(String subject) {
Collection<MessageCallback> receivers = null;
if (!proxyClosed) {
receivers = offerGetReceivers(subject);
}
if (receivers == null) {
receivers = proxied.getReceivers(subject);
}
return receivers;
}
private synchronized Collection<MessageCallback> offerGetReceivers(String subject) {
if (proxyClosed) {
return null;
} else {
return new ArrayList<MessageCallback>(heldSubscribe.values());
}
}
@Override
public boolean hasRemoteSubscriptions(String subject) {
return proxied.hasRemoteSubscriptions(subject);
}
@Override
public boolean hasRemoteSubscription(String sessionId, String subject) {
return proxied.hasRemoteSubscription(sessionId, subject);
}
@Override
public Map<QueueSession, MessageQueue> getMessageQueues() {
return proxied.getMessageQueues();
}
@Override
public MessageQueue getQueueBySession(String id) {
return proxied.getQueueBySession(id);
}
@Override
public QueueSession getSessionBySessionId(String id) {
return proxied.getSessionBySessionId(id);
}
@Override
public void associateNewQueue(QueueSession oldSession, QueueSession newSession) {
proxied.associateNewQueue(oldSession, newSession);
}
@Override
public void stop() {
proxied.stop();
}
@Override
public Message getDeadLetterMessage(String messageId) {
return proxied.getDeadLetterMessage(messageId);
}
@Override
public boolean removeDeadLetterMessage(String messageId) {
return proxied.removeDeadLetterMessage(messageId);
}
@Override
public synchronized void attachMonitor(BusMonitor monitor) {
this.heldBusMonitor = monitor;
}
public synchronized void closeProxy(ServerMessageBus bus) {
Assert.notNull("message bus reference cannot be null", bus);
if (proxied != null) {
throw new IllegalStateException("proxy already closed");
}
if (heldBusMonitor != null) {
bus.attachMonitor(heldBusMonitor);
}
for (Map.Entry<String, MessageCallback> entry : heldSubscribe.entries()) {
bus.subscribe(entry.getKey(), entry.getValue());
}
for (Map.Entry<String, MessageCallback> entry : heldLocalSubscribe.entries()) {
bus.subscribeLocal(entry.getKey(), entry.getValue());
}
for (SubscribeListener subscribeListener : heldSubscribeListener) {
bus.addSubscribeListener(subscribeListener);
}
for (UnsubscribeListener unsubscribeListener : heldUnsubscribeListener) {
bus.addUnsubscribeListener(unsubscribeListener);
}
for (Message message : heldMessages) {
bus.send(message);
}
for (Message message : heldGlobalMessages) {
bus.sendGlobal(message);
}
for (Map.Entry<Message, Boolean> entry : heldMessageFireListener.entrySet()) {
bus.send(entry.getKey(), entry.getValue());
}
this.heldBusMonitor = null;
this.heldSubscribe = null;
this.heldLocalSubscribe = null;
this.heldSubscribeListener = null;
this.heldUnsubscribeListener = null;
this.heldMessages = null;
this.heldGlobalMessages = null;
this.heldMessageFireListener = null;
this.proxied = bus;
this.proxyClosed = true;
}
}