/*
* Mojito Distributed Hash Table (Mojito DHT)
* Copyright (C) 2006-2007 LimeWire LLC
*
* This program 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.limewire.mojito.messages.impl;
import java.io.IOException;
import java.math.BigInteger;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Collection;
import org.limewire.io.ByteBufferInputStream;
import org.limewire.io.ByteBufferOutputStream;
import org.limewire.io.NetworkUtils;
import org.limewire.mojito.Context;
import org.limewire.mojito.KUID;
import org.limewire.mojito.db.DHTValueEntity;
import org.limewire.mojito.db.DHTValueType;
import org.limewire.mojito.io.MessageInputStream;
import org.limewire.mojito.messages.DHTMessage;
import org.limewire.mojito.messages.FindNodeRequest;
import org.limewire.mojito.messages.FindNodeResponse;
import org.limewire.mojito.messages.FindValueRequest;
import org.limewire.mojito.messages.FindValueResponse;
import org.limewire.mojito.messages.MessageFactory;
import org.limewire.mojito.messages.MessageFormatException;
import org.limewire.mojito.messages.MessageID;
import org.limewire.mojito.messages.PingRequest;
import org.limewire.mojito.messages.PingResponse;
import org.limewire.mojito.messages.StatsRequest;
import org.limewire.mojito.messages.StatsResponse;
import org.limewire.mojito.messages.StoreRequest;
import org.limewire.mojito.messages.StoreResponse;
import org.limewire.mojito.messages.DHTMessage.OpCode;
import org.limewire.mojito.messages.StatsRequest.StatisticType;
import org.limewire.mojito.messages.StoreResponse.StoreStatusCode;
import org.limewire.mojito.routing.Contact;
import org.limewire.mojito.routing.Version;
import org.limewire.security.SecurityToken;
/**
* The default implementation of the MessageFactory.
*/
public class DefaultMessageFactory implements MessageFactory {
protected final Context context;
public DefaultMessageFactory(Context context) {
this.context = context;
}
public DHTMessage createMessage(SocketAddress src, ByteBuffer... data)
throws MessageFormatException, IOException {
MessageInputStream in = null;
try {
in = new MessageInputStream(new ByteBufferInputStream(data), context.getMACCalculatorRepositoryManager());
// --- GNUTELLA HEADER ---
MessageID messageId = in.readMessageID();
int func = in.readUnsignedByte();
if (func != DHTMessage.F_DHT_MESSAGE) {
throw new MessageFormatException("Unknown function ID: " + func);
}
Version msgVersion = in.readVersion();
//byte[] length = in.readBytes(4); // Little-Endian!
in.skip(4);
// --- CONTINUTE WITH MOJITO HEADER ---
OpCode opcode = in.readOpCode();
switch(opcode) {
case PING_REQUEST:
return new PingRequestImpl(context, src, messageId, msgVersion, in);
case PING_RESPONSE:
return new PingResponseImpl(context, src, messageId, msgVersion, in);
case FIND_NODE_REQUEST:
return new FindNodeRequestImpl(context, src, messageId, msgVersion, in);
case FIND_NODE_RESPONSE:
return new FindNodeResponseImpl(context, src, messageId, msgVersion, in);
case FIND_VALUE_REQUEST:
return new FindValueRequestImpl(context, src, messageId, msgVersion, in);
case FIND_VALUE_RESPONSE:
return new FindValueResponseImpl(context, src, messageId, msgVersion, in);
case STORE_REQUEST:
return new StoreRequestImpl(context, src, messageId, msgVersion, in);
case STORE_RESPONSE:
return new StoreResponseImpl(context, src, messageId, msgVersion, in);
case STATS_REQUEST:
return new StatsRequestImpl(context, src, messageId, msgVersion, in);
case STATS_RESPONSE:
return new StatsResponseImpl(context, src, messageId, msgVersion, in);
default:
throw new IOException("Unhandled OpCode " + opcode);
}
} catch (IllegalArgumentException err) {
String msg = (src != null) ? src.toString() : null;
throw new MessageFormatException(msg, err);
} catch (IOException err) {
String msg = (src != null) ? src.toString() : null;
throw new MessageFormatException(msg, err);
} finally {
if (in != null) {
try { in.close(); } catch (IOException ignore) {}
}
}
}
public SecurityToken createSecurityToken(Contact dst) {
return context.getSecurityTokenHelper().createSecurityToken(dst);
}
public MessageID createMessageID(SocketAddress dst) {
if (!NetworkUtils.isValidSocketAddress(dst)) {
throw new IllegalArgumentException(dst + " is an invalid SocketAddress");
}
return DefaultMessageID.createWithSocketAddress(dst, context.getMACCalculatorRepositoryManager());
}
public ByteBuffer writeMessage(SocketAddress dst, DHTMessage message)
throws IOException {
ByteBufferOutputStream out = new ByteBufferOutputStream(640, true);
message.write(out);
out.close();
return ((ByteBuffer)out.getBuffer().flip()).order(ByteOrder.BIG_ENDIAN);
}
public FindNodeRequest createFindNodeRequest(Contact contact, SocketAddress dst, KUID lookupId) {
return new FindNodeRequestImpl(context, contact, createMessageID(dst), lookupId);
}
public FindNodeResponse createFindNodeResponse(Contact contact, Contact dst,
MessageID messageId, Collection<? extends Contact> nodes) {
return new FindNodeResponseImpl(context, contact, messageId, createSecurityToken(dst), nodes);
}
public FindValueRequest createFindValueRequest(Contact contact, SocketAddress dst,
KUID lookupId, Collection<KUID> keys, DHTValueType valueType) {
return new FindValueRequestImpl(context, contact, createMessageID(dst), lookupId, keys, valueType);
}
public FindValueResponse createFindValueResponse(Contact contact, Contact dst,
MessageID messageId, float requestLoad,
Collection<? extends DHTValueEntity> entities, Collection<KUID> secondaryKeys) {
return new FindValueResponseImpl(context, contact, messageId, requestLoad, entities, secondaryKeys);
}
public PingRequest createPingRequest(Contact contact, SocketAddress dst) {
return new PingRequestImpl(context, contact, createMessageID(dst));
}
public PingResponse createPingResponse(Contact contact, Contact dst,
MessageID messageId, SocketAddress externalAddress, BigInteger estimatedSize) {
return new PingResponseImpl(context, contact, messageId, externalAddress, estimatedSize);
}
public StatsRequest createStatsRequest(Contact contact, SocketAddress dst, StatisticType stats) {
return new StatsRequestImpl(context, contact, createMessageID(dst), stats);
}
public StatsResponse createStatsResponse(Contact contact, Contact dst,
MessageID messageId, byte[] statistics) {
return new StatsResponseImpl(context, contact, messageId, statistics);
}
public StoreRequest createStoreRequest(Contact contact, SocketAddress dst,
SecurityToken securityToken, Collection<? extends DHTValueEntity> values) {
return new StoreRequestImpl(context, contact, createMessageID(dst), securityToken, values);
}
public StoreResponse createStoreResponse(Contact contact, Contact dst,
MessageID messageId, Collection<StoreStatusCode> status) {
return new StoreResponseImpl(context, contact, messageId, status);
}
}