/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation.
*
* PowerFolder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id$
*/
package de.dal33t.powerfolder.clientserver;
import de.dal33t.powerfolder.Constants;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.Member;
import de.dal33t.powerfolder.PFComponent;
import de.dal33t.powerfolder.event.NodeManagerAdapter;
import de.dal33t.powerfolder.event.NodeManagerEvent;
import de.dal33t.powerfolder.message.Message;
import de.dal33t.powerfolder.message.MessageListener;
import de.dal33t.powerfolder.message.clientserver.Request;
import de.dal33t.powerfolder.message.clientserver.Response;
import de.dal33t.powerfolder.net.ConnectionException;
import de.dal33t.powerfolder.util.Reject;
/**
* Performs a basic request - response message cycle.
* <p>
* Rationale: Executes a request on a remote node.
* <p>
* Is thread safe. Can execute only one request.
*
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc</a>
* @version $Revision: 1.5 $
*/
public class RequestExecutor extends PFComponent {
private Object waitForResponseLock = new Object();
private Member node;
private DisconnectListener discoListener;
private ResponseMessageListener messageListener;
private String requestId;
private Response response;
public RequestExecutor(Controller controller, Member node) {
super(controller);
Reject.ifNull(node, "Node is null");
this.node = node;
this.discoListener = new DisconnectListener();
this.messageListener = new ResponseMessageListener();
}
public synchronized Response execute(Request request)
throws ConnectionException
{
if (!node.isConnected()) {
throw new ConnectionException("Not connected to " + node.getNick());
}
// Prepare
response = null;
requestId = request.getRequestId();
if (isFiner()) {
logFiner("Sending request to " + node.getNick() + " (" + requestId
+ "): " + request);
}
// Listen to receive the response
getController().getNodeManager().addNodeManagerListener(discoListener);
node.addMessageListener(messageListener);
node.sendMessage(request);
try {
waitForResponse(Constants.REQUEST_RESPONSE_TIMEOUT);
if (response == null) {
if (!node.isConnected()) {
throw new ConnectionException(node.getNick()
+ " disconnected");
}
throw new ConnectionException("Timeout to " + node.getNick());
}
if (isFiner()) {
logFiner("Response from " + node.getNick() + " (" + requestId
+ "): " + response + " to " + request);
}
} finally {
notifyAndcleanup();
}
return response;
}
// Internal helper ********************************************************
private void waitForResponse(long seconds) {
synchronized (waitForResponseLock) {
if (response != null) {
// Already got response
return;
}
try {
waitForResponseLock.wait(1000L * seconds);
} catch (InterruptedException e) {
logWarning("Interrupted while waiting for response (" + node
+ "): " + e);
logFiner("InterruptedException", e);
}
}
}
private void notifyAndcleanup() {
if (isFiner()) {
logFiner("Cleanup of request: " + requestId);
}
synchronized (waitForResponseLock) {
waitForResponseLock.notifyAll();
}
node.removeMessageListener(messageListener);
getController().getNodeManager().removeNodeManagerListener(
discoListener);
}
private class DisconnectListener extends NodeManagerAdapter {
@Override
public void nodeConnected(NodeManagerEvent e) {
if (!e.getNode().equals(node)) {
return;
}
// Break request
// notifyAndcleanup();
}
@Override
public void nodeDisconnected(NodeManagerEvent e) {
if (!e.getNode().equals(node)) {
return;
}
// Break request
notifyAndcleanup();
}
public boolean fireInEventDispatchThread() {
return false;
}
}
private class ResponseMessageListener implements MessageListener {
public void handleMessage(Member source, Message message) {
if (!source.equals(node)) {
return;
}
if (!(message instanceof Response)) {
return;
}
Response canidate = (Response) message;
if (requestId.equals(canidate.requestId)) {
response = canidate;
notifyAndcleanup();
}
}
public boolean fireInEventDispatchThread() {
return false;
}
}
}