package com.limegroup.gnutella.downloader.serial;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.limewire.io.Address;
import org.limewire.io.ConnectableImpl;
import org.limewire.net.address.AddressFactory;
import org.limewire.net.address.AddressSerializer;
import org.limewire.util.UnboxUtils;
import org.limewire.util.StringUtils;
import com.limegroup.gnutella.PushEndpointFactory;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.downloader.RemoteFileDescImpl;
/** A memento for a remote host. */
public class RemoteHostMemento implements Serializable {
private static final Log LOG = LogFactory.getLog(RemoteHostMemento.class);
private static final long serialVersionUID = 1452696797555431199L;
// NOTE: Never remove items, only add. Otherwise, mementos serialized with earlier
// keys cannot be deserialized.
private static enum Keys {
HOST, PORT, FILENAME, INDEX, CLIENTGUID,
SPEED, SIZE, CHAT, QUALITY, REPLY_TO_MULTICAST,
XML, URNS, BH, FIREWALLED, VENDOR, HTTP11,
TLS, PUSH_ADDR, CUSTOM_URL, ADDRESS, TYPE
}
private final Map<Keys, Serializable> propertiesMap;
public RemoteHostMemento(Address address, String filename, long index, byte[] clientGuid,
int speed, long size, int quality, boolean replyToMulticast, String xml, Set<URN> urns,
boolean browseHost, String vendor, boolean http1, String type, AddressFactory addressFactory) {
this.propertiesMap = new HashMap<Keys, Serializable>(Keys.values().length);
propertiesMap.put(Keys.FILENAME, filename);
propertiesMap.put(Keys.INDEX, index);
propertiesMap.put(Keys.CLIENTGUID, clientGuid);
propertiesMap.put(Keys.SPEED, speed);
propertiesMap.put(Keys.SIZE, size);
propertiesMap.put(Keys.QUALITY, quality);
propertiesMap.put(Keys.REPLY_TO_MULTICAST, replyToMulticast);
propertiesMap.put(Keys.XML, xml);
propertiesMap.put(Keys.URNS, (Serializable) urns);
propertiesMap.put(Keys.BH, browseHost);
propertiesMap.put(Keys.VENDOR, vendor);
propertiesMap.put(Keys.HTTP11, http1);
propertiesMap.put(Keys.ADDRESS, serializeAddress(address, addressFactory));
propertiesMap.put(Keys.TYPE, type);
}
/**
* Encodes address as address-type:utf8(base64(serialized-address)).
*/
public static String serializeAddress(Address address, AddressFactory addressFactory) {
AddressSerializer serializer = addressFactory.getSerializer(address);
assert serializer != null : "for address class: " + address.getClass();
StringBuilder builder = new StringBuilder(serializer.getAddressType());
builder.append(":");
try {
builder.append(StringUtils.getUTF8String(Base64.encodeBase64(serializer.serialize(address))));
return builder.toString();
} catch (IOException e) {
// impossible
throw new RuntimeException(e);
}
}
static Address deserializeAddress(final String addressString, AddressFactory addressFactory) {
StringTokenizer st = new StringTokenizer(addressString, ":");
if (st.hasMoreTokens()) {
String type = st.nextToken();
if (st.hasMoreTokens()) {
String addressPart = st.nextToken();
AddressSerializer serializer = addressFactory.getSerializer(type);
if (serializer != null) {
try {
return serializer.deserialize(Base64.decodeBase64(StringUtils.toUTF8Bytes(addressPart)));
} catch (IOException e) {
LOG.debug("", e);
}
}
LOG.debug("not enough arguments");
}
}
return new Address() {
@Override
public String getAddressDescription() {
return addressString;
}
};
}
public String getHost() { return (String)propertiesMap.get(Keys.HOST); }
public int getPort() { return UnboxUtils.toInt((Integer)propertiesMap.get(Keys.PORT)); }
public String getFileName() { return (String)propertiesMap.get(Keys.FILENAME); }
public long getIndex() { return UnboxUtils.toLong((Long)propertiesMap.get(Keys.INDEX)); }
public byte[] getClientGuid() { return (byte[])propertiesMap.get(Keys.CLIENTGUID); }
public int getSpeed() { return UnboxUtils.toInt((Integer)propertiesMap.get(Keys.SPEED)); }
public long getSize() { return UnboxUtils.toLong((Long)propertiesMap.get(Keys.SIZE)); }
public int getQuality() { return UnboxUtils.toInt((Integer)propertiesMap.get(Keys.QUALITY)); }
public boolean isReplyToMulticast() { return UnboxUtils.toBoolean((Boolean)propertiesMap.get(Keys.REPLY_TO_MULTICAST)); }
public String getXml() { return (String)propertiesMap.get(Keys.XML); }
@SuppressWarnings("unchecked")
public Set<URN> getUrns() { return (Set<URN>)propertiesMap.get(Keys.URNS); }
public boolean isBrowseHost() { return UnboxUtils.toBoolean((Boolean)propertiesMap.get(Keys.BH)); }
public String getVendor() { return (String)propertiesMap.get(Keys.VENDOR); }
public boolean isHttp11() { return UnboxUtils.toBoolean((Boolean)propertiesMap.get(Keys.HTTP11)); }
public boolean isTls() { return UnboxUtils.toBoolean((Boolean)propertiesMap.get(Keys.TLS)); }
public String getPushAddr() { return (String)propertiesMap.get(Keys.PUSH_ADDR); }
public URL getCustomUrl() { return (URL)propertiesMap.get(Keys.CUSTOM_URL); }
/**
* Returns the type of the remote host memento which is used for deserializing
* the correct type of {@link RemoteFileDesc}.
*
* @return {@link RemoteFileDescImpl#TYPE} in case no type is set
*/
public String getType() {
String type = (String) propertiesMap.get(Keys.TYPE);
if (type != null) {
return type;
}
return RemoteFileDescImpl.TYPE;
}
public Address getAddress(AddressFactory addressFactory, PushEndpointFactory pushEndpointFactory) throws IOException {
return getAddress((String) propertiesMap.get(Keys.ADDRESS), addressFactory, pushEndpointFactory);
}
public Address getAddress(String address, AddressFactory addressFactory, PushEndpointFactory pushEndpointFactory) throws IOException {
if (address != null) {
return deserializeAddress(address, addressFactory);
}
String pushAddress = getPushAddr();
if (pushAddress != null) {
return pushEndpointFactory.createPushEndpoint(pushAddress);
}
return new ConnectableImpl(getHost(), getPort(), isTls());
}
public void setCustomUrl(URL url) {
propertiesMap.put(Keys.CUSTOM_URL, url);
}
}