package org.kevoree.library.freepastry;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import rice.Continuation;
import rice.environment.Environment;
import rice.p2p.commonapi.Application;
import rice.p2p.commonapi.Endpoint;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.Message;
import rice.p2p.commonapi.RouteMessage;
import rice.p2p.past.Past;
import rice.p2p.past.PastImpl;
import rice.pastry.NodeHandle;
import rice.pastry.NodeIdFactory;
import rice.pastry.PastryNode;
import rice.pastry.PastryNodeFactory;
import rice.pastry.commonapi.PastryIdFactory;
import rice.pastry.routing.RouteSet;
import rice.pastry.socket.SocketPastryNodeFactory;
import rice.pastry.standard.RandomNodeIdFactory;
import rice.persistence.LRUCache;
import rice.persistence.MemoryStorage;
import rice.persistence.PersistentStorage;
import rice.persistence.Storage;
import rice.persistence.StorageManagerImpl;
import rice.tutorial.forwarding.MyMsg;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import rice.pastry.routing.RoutingTable;
public class PastryPeer implements Application {
private PastryIdFactory pastryIdFactory;
private Environment environment;
private NodeIdFactory nidFactory;
// construct the PastryNodeFactory, this is how we use rice.pastry.socket
private PastryNodeFactory factory;
// This will return null if we there is no node at that location
private NodeHandle bootHandle;
// construct a node, passing the null boothandle on the first loop will cause the node to start its own ring
private PastryNode node;
private Past app;
private static final Logger LOG = Logger.getLogger(PastryPeer.class.getName());
private List<Content> resultSet = new ArrayList<Content>();
private List<Id> nullResult = new ArrayList<Id>();
private List<Content> insertedContent = new ArrayList<Content>();
private List<Content> failedContent = new ArrayList<Content>();
private Endpoint endpoint;
//private TesterUtil defaults;
private final ReentrantLock lock = new ReentrantLock();
private final Condition dataInserted = lock.newCondition();
private final Condition dataRetrieved = lock.newCondition();
private InetSocketAddress socketAddress;
public PastryPeer(InetSocketAddress address) throws IOException {
socketAddress = address;
environment = new Environment();
nidFactory = new RandomNodeIdFactory(environment);
}
public PastryPeer() throws UnknownHostException, IOException {
this(new InetSocketAddress(InetAddress.getLocalHost(), 1200));
}
public boolean bootsrap() throws InterruptedException, IOException {
factory = new SocketPastryNodeFactory(nidFactory, socketAddress.getPort(), environment);
node = factory.newNode();
pastryIdFactory = new rice.pastry.commonapi.PastryIdFactory(environment);
node.boot(socketAddress);
synchronized (this) {
while (!node.isReady() && !node.joinFailed()) {
// delay so we don't busy-wait
this.wait(500);
// abort if can't join
if (node.joinFailed()) {
throw new IOException("Could not join the FreePastry ring. Reason:" + node.joinFailedReason());
}
}
}
return true;
}
public boolean join() throws InterruptedException, IOException {
FreeLocalPort port = new FreeLocalPort();
int freePort = port.getPort();
factory = new SocketPastryNodeFactory(nidFactory, freePort, environment);
node = factory.newNode();
pastryIdFactory = new rice.pastry.commonapi.PastryIdFactory(environment);
node.boot(socketAddress);
// the node may require sending several messages to fully boot into the ring
synchronized (this) {
int tries = 0;
while (!node.isReady() && !node.joinFailed()) {
// delay so we don't busy-wait
this.wait(200);
// abort if can't join
if (node.joinFailed()) {
LOG.log(Level.SEVERE, "Could not join the FreePastry ring. Reason:{0}", node.joinFailedReason());
throw new IOException("Could not join the FreePastry ring. Reason:" + node.joinFailedReason());
} else if (tries > 300) {
return false;
}
tries++;
}
// We are only going to use one instance of this application on each PastryNode
endpoint = node.buildEndpoint(this, "myinstance");
// now we can receive messages
endpoint.register();
}
return true;
}
public void createPast() throws IOException {
// Setting log(n) replicas
Double replica = Math.log(8) / Math.log(2);
// create a different storage root for each node
String storageDirectory = "./storage" + node.getId().hashCode();
// create the persistent part
Storage stor = new PersistentStorage(pastryIdFactory, storageDirectory, 4 * 1024 * 1024, node.getEnvironment());
app = new PastImpl(node, new StorageManagerImpl(pastryIdFactory, stor, new LRUCache(
new MemoryStorage(pastryIdFactory), 512 * 1024, node.getEnvironment())), replica.intValue(), "");
//log.log(Level.INFO, "Started with node id : {0}", node.getLocalHandle().toString());
}
public void put(String key, String value) throws InterruptedException {
Content myContent = new Content(pastryIdFactory.buildId(key), value);
this.insert(myContent);
}
private void insert(final Content content) throws InterruptedException {
app.insert(content, new Continuation() {
public void receiveResult(Object result) {
lock.lock();
try {
LOG.log(Level.INFO, "Data inserted: {0}", result);
dataInserted.signal();
} finally {
lock.unlock();
}
insertedContent.add(content);
}
public void receiveException(Exception result) {
failedContent.add(content);
throw new RuntimeException("Error storing " + content, result);
}
});
lock.lock();
try {
LOG.info("Waiting for insertion.");
dataInserted.await();
LOG.info("Insertion finished.");
} finally {
lock.unlock();
}
}
public String get(String key) throws InterruptedException {
Id id = pastryIdFactory.buildId(key);
String result = this.retrieve(id);
LOG.log(Level.INFO, "Retrieved value: {0}", result);
return result;
}
private String retrieve(final Id key) throws InterruptedException {
assert key != null : "Trying to retrieve null key.";
final AtomicReference<Content> result = new AtomicReference<Content>();
Continuation cont = new Continuation() {
public void receiveResult(Object o) {
Content c = (Content) o;
if ((o != null) && (key.equals(c.getId()))) {
result.set(c);
LOG.log(Level.INFO, "Content received: {0}", o.toString());
lock.lock();
try {
dataRetrieved.signal();
} finally {
lock.unlock();
}
}
}
public void receiveException(Exception result) {
LOG.log(Level.SEVERE, "Error receiving content: {0}",
result.getMessage());
throw new RuntimeException("Error receiving " + result);
}
};
lock.lock();
app.lookup(key, true, cont);
try {
LOG.info("Waiting for data reception.");
while (result.get() == null) {
dataRetrieved.await();
}
LOG.info("Data arrived");
} finally {
lock.unlock();
}
return result.get().getContent();
}
public void lookup(Id key) {
LOG.log(Level.INFO, "Looking up ...");
final Id lookupKey = key;
LOG.log(Level.INFO, "Looking up {0} at node {1}",
new Object[]{lookupKey, app.getLocalNodeHandle()});
app.lookup(lookupKey, true, new Continuation() {
public void receiveResult(Object result) {
LOG.log(Level.INFO, "Successfully looked up {0} for key {1}.",
new Object[]{result, lookupKey});
if (result == null) {
nullResult.add(lookupKey);
} else {
if (!resultSet.contains(result)) {
resultSet.add((Content) result);
}
}
}
public void receiveException(Exception result) {
LOG.log(Level.SEVERE, "Error looking up " + lookupKey, result);
result.printStackTrace();
}
});
}
public List<Content> getResultSet() {
return resultSet;
}
public int getSizeExpected() {
return resultSet.size();
}
public List<Content> getInsertedContent() {
return insertedContent;
}
public List<Content> getFailedContent() {
return failedContent;
}
public void leave() {
node.destroy();
environment.destroy();
}
@Deprecated
public boolean isAlive() {
return bootHandle.isAlive();
}
@Deprecated
public List<NodeHandle> oldGetRoutingTable() {
List<NodeHandle> list = new ArrayList<NodeHandle>();
RouteSet[] routeSetVector;
RouteSet routeSet;
for (int i = 0; i < node.getRoutingTable().numRows(); i++) {
routeSetVector = node.getRoutingTable().getRow(i);
for (int j = 0; j < routeSetVector.length; j++) {
if (routeSetVector[j] != null) {
routeSet = routeSetVector[j];
for (int k = 0; k < routeSet.size(); k++) {
if (!list.contains(routeSet.get(k))) {
list.add(routeSet.get(k));
}
}
}
}
}
return list;
}
public Set<String> getRoutingTable() {
Set<String> result = new HashSet<String>();
RoutingTable rt = node.getRoutingTable();
for (int i = 0; i < rt.numRows(); i++) {
for (RouteSet rs : rt.getRow(i)) {
// rs can be null
if (rs != null) {
for (NodeHandle handle : rs) {
Id id = handle.getNodeId();
result.add(id.toStringFull());
}
}
}
}
return result;
}
public void pingNodes() {
RouteSet[] routeSetVector;
for (int i = 0; i < node.getRoutingTable().numRows(); i++) {
routeSetVector = node.getRoutingTable().getRow(i);
for (int j = 0; j < routeSetVector.length; j++) {
if (routeSetVector[j] != null) {
routeSetVector[j].pingAllNew();
}
}
}
}
/* public void ping(PastryNode nd){
Ping pg = new Ping(nd);
}*/
@Deprecated
public Id oldGetId() {
return node.getNodeId();
}
/**
*
* @return The node Id, as String
*/
public String getId() {
return node.getId().toStringFull();
}
public boolean isReady() {
if (node.joinFailed()) {
return false;
} else if (node.isReady()) {
return true;
} else {
return false;
}
}
public List<Id> getNullResulKeys() {
return nullResult;
}
public void deliver(Id id, Message message) {
System.out.println(this + " received " + message);
}
public boolean forward(RouteMessage message) {
try {
MyMsg msg = (MyMsg) message.getMessage(endpoint.getDeserializer());
msg.addHop(endpoint.getLocalNodeHandle());
} catch (IOException ioe) {
ioe.printStackTrace();
}
return true;
}
public void update(rice.p2p.commonapi.NodeHandle arg0, boolean arg1) {
// TODO Auto-generated method stub
}
public void routeMyMsg(Id id) {
System.out.println(this + " sending to " + id);
Message msg = new MyMsg(endpoint.getId(), id);
endpoint.route(id, msg, null);
}
public Past getPast() {
return app;
}
public int getPort() {
return socketAddress.getPort();
}
public void print() {
}
public static void main(String[] argv) throws UnknownHostException, IOException, InterruptedException {
InetSocketAddress address =
new InetSocketAddress(InetAddress.getLocalHost(), 1200);
PastryPeer p = new PastryPeer(address);
System.out.println("#");
System.out.println(Integer.parseInt("*"));
p.bootsrap();
}
}