/* * 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.io; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Collection; import org.limewire.mojito.KUID; import org.limewire.mojito.StatusCode; import org.limewire.mojito.db.DHTValue; import org.limewire.mojito.db.DHTValueEntity; import org.limewire.mojito.db.DHTValueType; import org.limewire.mojito.messages.MessageID; 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.Vendor; import org.limewire.mojito.routing.Version; import org.limewire.security.AddressSecurityToken; import org.limewire.security.SecurityToken; /** * The MessageOutputStream class writes a DHTMessage (serializes) * to a given OutputStream. * <p> * <b>NOTE</b>: This class is specific to Mojito's Gnutella backed * Message format. You may or may not be able to use parts of this * class for alternative message formats! */ public class MessageOutputStream extends DataOutputStream { public MessageOutputStream(OutputStream out) { super(out); } /** * Writes the given KUID to the OutputStream. */ public void writeKUID(KUID kuid) throws IOException { if (kuid == null) { throw new NullPointerException("KUID cannot be null"); } kuid.write(this); } /** * Writes the given MessageID to the OutputStream. */ public void writeMessageID(MessageID messageId) throws IOException { if (messageId == null) { throw new NullPointerException("MessageID cannot be null"); } messageId.write(this); } /** * Writes the given BigInteger to the OutputStream. */ public void writeDHTSize(BigInteger estimatedSize) throws IOException { byte[] data = estimatedSize.toByteArray(); if (data.length > KUID.LENGTH) { // Can't be more than 2**160 bit throw new IOException("Illegal length: " + data.length + "/" + estimatedSize); } writeByte(data.length); write(data, 0, data.length); } /** * Writes the given DHTValue to the OutputStream. */ public void writeDHTValueEntity(DHTValueEntity entity) throws IOException { writeContact(entity.getCreator()); entity.getPrimaryKey().write(this); writeDHTValue(entity.getValue()); } /** * */ private void writeDHTValue(DHTValue value) throws IOException { writeDHTValueType(value.getValueType()); writeVersion(value.getVersion()); byte[] data = value.getValue(); writeShort(data.length); write(data, 0, data.length); } /** * Writes the given Collection of KUIDs to the OutputStream. */ public void writeKUIDs(Collection<KUID> keys) throws IOException { writeCollectionSize(keys); for (KUID k : keys) { k.write(this); } } /** * Writes the given Collection of DHTValues to the OutputStream. */ public void writeDHTValueEntities(Collection<? extends DHTValueEntity> values) throws IOException { writeCollectionSize(values); for(DHTValueEntity entity : values) { writeDHTValueEntity(entity); } } /** * Writes the given Signature to the OutputStream. */ public void writeSignature(byte[] signature) throws IOException { if (signature != null && signature.length > 0) { writeByte(signature.length); write(signature, 0, signature.length); } else { writeByte(0); } } /** * Writes the given Contact to the OutputStream. */ public void writeContact(Contact node) throws IOException { writeVendor(node.getVendor()); writeVersion(node.getVersion()); writeKUID(node.getNodeID()); writeSocketAddress(node.getContactAddress()); } /** * Writes the given Collection of Contact to the OutputStream. */ public void writeContacts(Collection<? extends Contact> nodes) throws IOException { writeCollectionSize(nodes); for(Contact node : nodes) { writeContact(node); } } /** * Writes the given InetAddress to the OutputStream. */ public void writeInetAddress(InetAddress addr) throws IOException { byte[] address = addr.getAddress(); writeByte(address.length); write(address, 0, address.length); } /** * Writes the given Port number to the OutputStream. */ public void writePort(int port) throws IOException { writeShort(port); } /** * Writes the given SocketAddress to the OutputStream. */ public void writeSocketAddress(SocketAddress addr) throws IOException { if (addr instanceof InetSocketAddress && !((InetSocketAddress)addr).isUnresolved()) { InetSocketAddress iaddr = (InetSocketAddress)addr; writeInetAddress(iaddr.getAddress()); writePort(iaddr.getPort()); } else { writeByte(0); } } /** * Writes the given AddressSecurityToken to the OutputStream. */ public void writeSecurityToken(SecurityToken securityToken) throws IOException { if (securityToken != null) { assert (securityToken instanceof AddressSecurityToken); byte[] qk = securityToken.getBytes(); writeByte(qk.length); write(qk, 0, qk.length); } else { writeByte(0); } } /** * Writes an encoded Statistics payload to the OutputStream. */ public void writeStatistics(byte[] statistics) throws IOException { if (statistics != null) { writeShort(statistics.length); write(statistics); } else { writeShort(0); } } /** * Writes the given OpCode to the OutputStream. */ public void writeOpCode(OpCode opcode) throws IOException { writeByte(opcode.toByte()); } /** * Writes the given Type to the OutputStream. */ public void writeStatisticType(StatisticType type) throws IOException { writeByte(type.toByte()); } /** * Writes the given StatusCode to the OutputStream. */ public void writeStatusCode(StatusCode statusCode) throws IOException { writeShort(statusCode.shortValue()); writeDHTString(statusCode.getDescription()); } /** * Writes the given String to the OutputStream. This is different * from writeUTF(String) which writes the String in the so called * Modified-UTF format! * * @param str must not be null */ void writeDHTString(String str) throws IOException { byte[] b = str.getBytes("UTF-8"); if (b.length > 0xFFFF) { throw new IOException("String is too big"); } writeShort(b.length); write(b); } /** * Writes the given Collection of StoreStatusCode(s) to the OutputStream. */ public void writeStoreStatusCodes(Collection<StoreStatusCode> statusCodes) throws IOException { writeCollectionSize(statusCodes); for (StoreStatusCode statusCode : statusCodes) { writeKUID(statusCode.getPrimaryKey()); writeKUID(statusCode.getSecondaryKey()); writeStatusCode(statusCode.getStatusCode()); } } /** * Writes the size of the given Collection to the OutputStream. */ private void writeCollectionSize(Collection c) throws IOException { int size = c.size(); if (size > 0xFF) { throw new IOException("Too many elements: " + size); } writeByte(size); } /** * Writes the DHTValueType to the OutputStream. */ public void writeDHTValueType(DHTValueType type) throws IOException { writeInt(type.toInt()); } /** * Writes the Vendor to the OutputStream. */ public void writeVendor(Vendor vendor) throws IOException { writeInt(vendor.intValue()); } /** * Writes the Version to the OutputStream. */ public void writeVersion(Version version) throws IOException { writeShort(version.shortValue()); } }