/*
* Copyright 2000-2006 JetBrains s.r.o.
*
* 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 jetbrains.communicator.core.impl.dispatcher;
import jetbrains.communicator.core.EventBroadcaster;
import jetbrains.communicator.core.EventVisitor;
import jetbrains.communicator.core.IDEtalkAdapter;
import jetbrains.communicator.core.IDEtalkEvent;
import jetbrains.communicator.core.dispatcher.AsyncMessageDispatcher;
import jetbrains.communicator.core.dispatcher.Message;
import jetbrains.communicator.core.users.User;
import jetbrains.communicator.core.users.UserEvent;
import jetbrains.communicator.ide.IDEFacade;
import jetbrains.communicator.util.WaitFor;
import org.apache.log4j.Logger;
/**
* @author Kir
*/
public class AsyncMessageDispatcherImpl extends AbstractMessageDispatcher implements AsyncMessageDispatcher, Runnable {
private static final Logger LOG = Logger.getLogger(AsyncMessageDispatcherImpl.class);
private static final String FILE_NAME = "pendingNetworkMessages.xml";
private final MyEventListener myEventListener;
private final IDEFacade myIdeFacade;
private final Object myWorkingThreadLock = new Object();
private Thread myWorkingThread;
private Thread myThread;
private boolean myShouldDeliverNow;
public AsyncMessageDispatcherImpl(EventBroadcaster broadcaster, IDEFacade ideFacade) {
super(broadcaster, ideFacade.getCacheDir());
myIdeFacade = ideFacade;
myEventListener = new MyEventListener(broadcaster);
start();
}
private void start() {
assert !isRunning(): "Already started";
Thread t = new Thread(this, "Network Message Dispatcher");
t.setDaemon(true);
t.start();
}
public boolean isRunning() {
return myThread != null && myThread.isAlive();
}
public void run() {
LOG.debug("Starting " + Thread.currentThread().getName());
synchronized (myWorkingThreadLock) {
myWorkingThread = Thread.currentThread();
myThread = Thread.currentThread();
}
try {
LOG.debug("Started Network Message Dispatcher thread");
while (notDisposed()) {
synchronized(myWorkingThreadLock) {
while (!myShouldDeliverNow && notDisposed()) {
myWorkingThreadLock.wait();
}
myShouldDeliverNow = false;
}
LOG.debug("Process pending network messages");
if (notDisposed()) {
dispatchAllMessages();
}
}
} catch (InterruptedException e) {
LOG.warn(e.getMessage(), e);
}
finally {
synchronized(myWorkingThreadLock) {
myWorkingThread = null;
}
}
}
private boolean notDisposed() {
return myWorkingThread != null;
}
private void dispatchAllMessages() {
User[] usersWithMessages = getUsersWithMessages();
for (int i = 0; i < usersWithMessages.length && isRunning(); i++) {
User user = usersWithMessages[i];
Message[] pendingMessages = getPendingMessages(user);
for (int j = 0; j < pendingMessages.length && isRunning(); j++) {
performDispatch(user, pendingMessages[j]);
}
}
save();
}
public void dispose() {
myEventListener.dispose();
synchronized(myWorkingThreadLock) {
myWorkingThread = null;
myWorkingThreadLock.notifyAll();
}
new WaitFor(10000){
protected boolean condition() {
return !isRunning();
}
};
super.dispose();
}
protected String getEventsFileName() {
return FILE_NAME;
}
public void sendLater(User user, Message message) {
synchronized(myWorkingThreadLock) {
addPendingMessage(user, message);
triggerDelivery();
}
}
public IDEFacade getIdeFacade() {
return myIdeFacade;
}
void triggerDelivery() {
synchronized(myWorkingThreadLock) {
myShouldDeliverNow = true;
myWorkingThreadLock.notifyAll();
}
}
private class MyEventListener extends IDEtalkAdapter {
private final EventBroadcaster myBroadcaster;
MyEventListener(EventBroadcaster broadcaster) {
myBroadcaster = broadcaster;
broadcaster.addListener(this);
}
public void dispose() {
myBroadcaster.removeListener(this);
}
public void afterChange(IDEtalkEvent event) {
event.accept(new EventVisitor(){
@Override public void visitUserOnline(UserEvent.Online online) {
triggerDelivery();
}
});
}
}
}