/*
* Copyright 2009 Thomas Bocek
*
* 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 net.tomp2p.rpc;
import java.io.IOException;
import java.security.KeyPair;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import net.tomp2p.connection2.ChannelCreator;
import net.tomp2p.connection2.ConnectionBean;
import net.tomp2p.connection2.PeerBean;
import net.tomp2p.connection2.RequestHandler;
import net.tomp2p.futures.FutureResponse;
import net.tomp2p.message.Message;
import net.tomp2p.message.Message.Command;
import net.tomp2p.message.Message.Type;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number320;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.storage.Data;
import net.tomp2p.task.TaskManager;
import net.tomp2p.task.TaskStatus;
import net.tomp2p.task.Worker;
import net.tomp2p.utils.Utils;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TaskRPC extends DispatchHandler {
final private static Logger logger = LoggerFactory.getLogger(TaskRPC.class);
private final TaskManager taskManager;
public TaskRPC(PeerBean peerBean, ConnectionBean connectionBean) {
super(peerBean, connectionBean);
this.taskManager = peerBean.getTaskManager();
registerIoHandler(Command.TASK);
}
/**
* Sends a task to a remote peer. This is an RPC.
*
* @param remotePeer
* The remote peer to send this request
* @param channelCreator
* The channel creator that creates connections
* @param forceTCP
* Set to true if the communication should be TCP, default is UDP
* @return The future response to keep track of future events
*/
public FutureResponse sendTask(final PeerAddress remotePeer, ChannelCreator channelCreator, Number160 taskID,
Map<Number160, Data> dataMap, Worker mapper, KeyPair keyPair, boolean forceUDP, boolean sign) {
final Message message = createMessage(remotePeer, Command.TASK, Type.REQUEST_1);
FutureResponse futureResponse = new FutureResponse(message);
try {
byte[] me = Utils.encodeJavaObject(mapper);
ChannelBuffer payload = ChannelBuffers.wrappedBuffer(me);
message.setPayload(payload);
message.setDataMap(dataMap);
message.setKey(taskID);
if (sign) {
message.setPublicKeyAndSign(keyPair);
}
if (forceUDP) {
final RequestHandler<FutureResponse> requestHandler = new RequestHandler<FutureResponse>(
futureResponse, getPeerBean(), getConnectionBean(), message);
if (logger.isDebugEnabled()) {
logger.debug("send Task " + message);
}
return requestHandler.sendUDP(channelCreator);
} else {
final RequestHandlerTCP<FutureResponse> requestHandler = new RequestHandlerTCP<FutureResponse>(
futureResponse, getPeerBean(), getConnectionBean(), message);
if (logger.isDebugEnabled()) {
logger.debug("send Task " + message);
}
return requestHandler.sendTCP(channelCreator);
}
} catch (IOException ioe) {
futureResponse.setFailed(ioe.toString());
if (logger.isErrorEnabled()) {
ioe.printStackTrace();
}
return futureResponse;
}
}
public FutureResponse sendResult(final PeerAddress remotePeer, ChannelCreator channelCreator, Number160 taskID,
Map<Number160, Data> dataMap, KeyPair keyPair, boolean forceUDP, boolean sign) {
final Message message = createMessage(remotePeer, Command.TASK, Type.REQUEST_3);
FutureResponse futureResponse = new FutureResponse(message);
if (dataMap != null) {
message.setDataMap(dataMap);
}
message.setKey(taskID);
if (sign) {
message.setPublicKeyAndSign(keyPair);
}
if (logger.isDebugEnabled()) {
logger.debug("send Result " + message);
}
if (forceUDP) {
final RequestHandler<FutureResponse> requestHandler = new RequestHandler<FutureResponse>(
futureResponse, getPeerBean(), getConnectionBean(), message);
return requestHandler.sendUDP(channelCreator);
} else {
final RequestHandlerTCP<FutureResponse> requestHandler = new RequestHandlerTCP<FutureResponse>(
futureResponse, getPeerBean(), getConnectionBean(), message);
return requestHandler.sendTCP(channelCreator);
}
}
public FutureResponse taskStatus(final PeerAddress remotePeer, ChannelCreator channelCreator,
Collection<Number160> taskIDs, boolean forceTCP) {
final Message message = createMessage(remotePeer, Command.TASK, Type.REQUEST_2);
message.setKeys(taskIDs);
FutureResponse futureResponse = new FutureResponse(message);
if (!forceTCP) {
final RequestHandler<FutureResponse> requestHandler = new RequestHandler<FutureResponse>(
futureResponse, getPeerBean(), getConnectionBean(), message);
return requestHandler.sendUDP(channelCreator);
} else {
final RequestHandlerTCP<FutureResponse> requestHandler = new RequestHandlerTCP<FutureResponse>(
futureResponse, getPeerBean(), getConnectionBean(), message);
return requestHandler.sendTCP(channelCreator);
}
}
@Override
public Message handleResponse(final Message message, boolean sign) throws Exception {
if (!((message.getType() == Type.REQUEST_1 || message.getType() == Type.REQUEST_2 || message.getType() == Type.REQUEST_3) && message
.getCommand() == Command.TASK)) {
throw new IllegalArgumentException("Message content is wrong");
}
if (logger.isDebugEnabled()) {
logger.debug("send Task - received " + message);
}
final Message responseMessage = createResponseMessage(message, Type.OK);
if (sign) {
responseMessage.setPublicKeyAndSign(getPeerBean().getKeyPair());
}
if (message.getType() == Type.REQUEST_1) {
Number160 taskId = message.getKey();
// request 1 is task creation
Map<Number160, Data> dataMap = message.getDataMap();
ChannelBuffer channelBuffer = message.getPayload1();
Object obj = Utils.decodeJavaObject(channelBuffer);
Worker mapper = (Worker) obj;
int queuePosition = taskManager.submitTask(getPeerBean().getPeer(), taskId, mapper, dataMap,
message.getSender(), sign);
responseMessage.setInteger(queuePosition);
} else if (message.getType() == Type.REQUEST_2) {
Collection<Number160> taskIDs = message.getKeys();
Map<Number160, Data> dataMap = new HashMap<Number160, Data>();
for (Number160 taskId : taskIDs) {
Number320 taskKey = new Number320(taskId, message.getSender().getPeerId());
TaskStatus taskStatus = taskManager.taskStatus(taskKey);
Data data = new Data(taskStatus);
dataMap.put(taskId, data);
}
responseMessage.setDataMap(dataMap);
if (logger.isDebugEnabled()) {
logger.debug("finished task status for tasks " + taskIDs);
}
} else if (message.getType() == Type.REQUEST_3) {
Number160 taskId = message.getKey();
Map<Number160, Data> dataMap = message.getDataMap();
Number320 taskKey = new Number320(taskId, message.getSender().getPeerId());
taskManager.notifyListeners(taskKey, dataMap);
} else {
responseMessage.setType(Type.NOT_FOUND);
}
return responseMessage;
}
}