community FileSharing; schema { Piece { int id; int offset; int length; string digest; string state; } File { string name; string digest; int length; int blockSize; Piece* pieces; string path; } } role Provider { local helper java:beans.FileHelper; when ( self: start()) { helper.showGUI(); } when ( self: share(string filePath, string descFilePath) ) { share((File) helper.initialize(filePath, descFilePath)); } when ( self: share(File file) ) { concurrency 10; log.info(file); converse (file.digest) { when ( Requester r: request(int* needs) ) { Piece* avails = Piece* (); enum n: needs do { avails = avails + file.pieces[id==n && state=="solid"][0]; } if (avails) provide(avails.id); } when ( Requester r: request(self, Piece need) ) { concurrency 5; Piece p = file.pieces[id==need.id][0]; if (p.state =="solid" && p.digest == need.digest) { converse (p.digest) { string url = (string) helper.getURL(p); provide(url); helper.sharePiece(p, url, 20000); conclude; } } } when ( self: raise("java.io.IOException", string message) ) { // gui.alert(message); } } } } role Requester { local helper java:beans.FileHelper; when ( self: take(File file) ) { file = (File) helper.validate(file); converse (file.digest) { concurrency 10; request(); when (self: silent(10000) ), when (self: request() ) { Piece* pending = file.pieces[state=="empty" || state=="req"]; if (! pending) { // should not quit here, // there may be other requester conversations join Provider; share(file); conclude; } else { Piece* needs = file.pieces[state=="empty"]; if (needs) { request(needs.id); } } } when (Provider p: provide(int* avails) ) { synchronized (file) { enum a: avails do { Piece* needs = file.pieces[id == a && state=="empty"]; if (needs) { Piece need = needs[0]; need.state = "req"; converse (need.digest) { request(p, need); when (p: provide(string url)) { string path = file.path; if ((bool) helper.fetchPiece(need, url, path)) { need.state = "solid"; join Provider; share(file); } else { need.state = "empty"; } request(); // There is a race condition here?? conclude; } when (self: idle(20000)), // timeout when (self: raise("java.lang.NullPointerException", string message)), when (self: raise("java.io.IOException", string message)) { need.state = "empty"; conclude; } } break; } } } } } } }