Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.ipfs.api.IPFS Maven / Gradle / Ivy
Go to download
The ProximaX IPFS Java API is a Java library for interacting IPFS server.
package io.ipfs.api;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.ipfs.cid.Cid;
import io.ipfs.multiaddr.MultiAddress;
import io.ipfs.multihash.Multihash;
public class IPFS {
public static final Version MIN_VERSION = Version.parse("0.4.11");
public enum PinType {all, direct, indirect, recursive}
public List ObjectTemplates = Arrays.asList("unixfs-dir");
public List ObjectPatchTypes = Arrays.asList("add-link", "rm-link", "set-data", "append-data");
public final String host;
public final int port;
private final String version;
public final Key key = new Key();
public final Pin pin = new Pin();
public final Repo repo = new Repo();
public final IPFSObject object = new IPFSObject();
public final Swarm swarm = new Swarm();
public final Bootstrap bootstrap = new Bootstrap();
public final Block block = new Block();
public final Dag dag = new Dag();
public final Diag diag = new Diag();
public final Config config = new Config();
public final Refs refs = new Refs();
public final Update update = new Update();
public final DHT dht = new DHT();
public final File file = new File();
public final Stats stats = new Stats();
public final Name name = new Name();
public final Pubsub pubsub = new Pubsub();
public IPFS(String host, int port) {
this(host, port, "/api/v0/");
}
public IPFS(String multiaddr) {
this(new MultiAddress(multiaddr));
}
public IPFS(MultiAddress addr) {
this(addr.getHost(), addr.getTCPPort(), "/api/v0/");
}
public IPFS(String host, int port, String version) {
this.host = host;
this.port = port;
this.version = version;
// Check IPFS is sufficiently recent
try {
Version detected = Version.parse(version());
if (detected.isBefore(MIN_VERSION))
throw new IllegalStateException("You need to use a more recent version of IPFS! >= " + MIN_VERSION);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public List add(NamedStreamable file) throws IOException {
return add(file, false);
}
public List add(NamedStreamable file, boolean wrap) throws IOException {
return add(file, wrap, false);
}
public List add(NamedStreamable file, boolean wrap, boolean hashOnly) throws IOException {
return add(Collections.singletonList(file), wrap, hashOnly);
}
public List add(List files, boolean wrap, boolean hashOnly) throws IOException {
Multipart m = new Multipart("http://" + host + ":" + port + version + "add?w="+wrap + "&n="+hashOnly, "UTF-8");
for (NamedStreamable file: files) {
if (file.isDirectory()) {
m.addSubtree(Paths.get(""), file);
} else
m.addFilePart("file", Paths.get(""), file);
};
String res = m.finish();
return JSONParser.parseStream(res).stream()
.map(x -> MerkleNode.fromJSON((Map) x))
.collect(Collectors.toList());
}
public List ls(Multihash hash) throws IOException {
Map res = retrieveMap("ls/" + hash);
return ((List) res.get("Objects")).stream().map(x -> MerkleNode.fromJSON((Map) x)).collect(Collectors.toList());
}
public byte[] cat(Multihash hash) throws IOException {
return retrieve("cat/" + hash);
}
public byte[] cat(Multihash hash, String subPath) throws IOException {
return retrieve("cat?arg=" + hash + URLEncoder.encode(subPath, "UTF-8"));
}
public byte[] get(Multihash hash) throws IOException {
return retrieve("get/" + hash);
}
public InputStream catStream(Multihash hash) throws IOException {
return retrieveStream("cat/" + hash);
}
public List refs(Multihash hash, boolean recursive) throws IOException {
String jsonStream = new String(retrieve("refs?arg=" + hash + "&r=" + recursive));
return JSONParser.parseStream(jsonStream).stream()
.map(m -> (String) (((Map) m).get("Ref")))
.map(Cid::decode)
.collect(Collectors.toList());
}
public Map resolve(String scheme, Multihash hash, boolean recursive) throws IOException {
return retrieveMap("resolve?arg=/" + scheme+"/"+hash +"&r="+recursive);
}
public String dns(String domain) throws IOException {
Map res = retrieveMap("dns?arg=" + domain);
return (String)res.get("Path");
}
public Map mount(java.io.File ipfsRoot, java.io.File ipnsRoot) throws IOException {
if (ipfsRoot != null && !ipfsRoot.exists())
ipfsRoot.mkdirs();
if (ipnsRoot != null && !ipnsRoot.exists())
ipnsRoot.mkdirs();
return (Map)retrieveAndParse("mount?arg=" + (ipfsRoot != null ? ipfsRoot.getPath() : "/ipfs" ) + "&arg=" +
(ipnsRoot != null ? ipnsRoot.getPath() : "/ipns" ));
}
// level 2 commands
public class Refs {
public List local() throws IOException {
String jsonStream = new String(retrieve("refs/local"));
return JSONParser.parseStream(jsonStream).stream()
.map(m -> (String) (((Map) m).get("Ref")))
.map(Cid::decode)
.collect(Collectors.toList());
}
}
/* Pinning an object ensures a local copy of it is kept.
*/
public class Pin {
public List add(Multihash hash) throws IOException {
return ((List)((Map)retrieveAndParse("pin/add?stream-channels=true&arg=" + hash)).get("Pins"))
.stream()
.map(x -> Cid.decode((String) x))
.collect(Collectors.toList());
}
public Map ls() throws IOException {
return ls(PinType.direct);
}
public Map ls(PinType type) throws IOException {
return ((Map)(((Map)retrieveAndParse("pin/ls?stream-channels=true&t="+type.name())).get("Keys"))).entrySet()
.stream()
.collect(Collectors.toMap(x -> Cid.decode(x.getKey()), x-> x.getValue()));
}
public List rm(Multihash hash) throws IOException {
return rm(hash, true);
}
public List rm(Multihash hash, boolean recursive) throws IOException {
Map json = retrieveMap("pin/rm?stream-channels=true&r=" + recursive + "&arg=" + hash);
return ((List) json.get("Pins")).stream().map(x -> Cid.decode((String) x)).collect(Collectors.toList());
}
public List update(Multihash existing, Multihash modified, boolean unpin) throws IOException {
return ((List)((Map)retrieveAndParse("pin/update?stream-channels=true&arg=" + existing + "&arg=" + modified + "&unpin=" + unpin)).get("Pins"))
.stream()
.map(x -> new MultiAddress((String) x))
.collect(Collectors.toList());
}
}
/* 'ipfs repo' is a plumbing command used to manipulate the repo.
*/
public class Key {
public KeyInfo gen(String name, Optional type, Optional size) throws IOException {
return KeyInfo.fromJson(retrieveAndParse("key/gen?arg=" + name + type.map(t -> "&type=" + t).orElse("") + size.map(s -> "&size=" + s).orElse("")));
}
public List list() throws IOException {
return ((List)((Map)retrieveAndParse("key/list")).get("Keys"))
.stream()
.map(KeyInfo::fromJson)
.collect(Collectors.toList());
}
public Object rename(String name, String newName) throws IOException {
return retrieveAndParse("key/rename?arg="+name + "&arg=" + newName);
}
public List rm(String name) throws IOException {
return ((List)((Map)retrieveAndParse("key/rm?arg=" + name)).get("Keys"))
.stream()
.map(KeyInfo::fromJson)
.collect(Collectors.toList());
}
}
/* 'ipfs repo' is a plumbing command used to manipulate the repo.
*/
public class Repo {
public Object gc() throws IOException {
return retrieveAndParse("repo/gc");
}
}
public class Pubsub {
public Object ls() throws IOException {
return retrieveAndParse("pubsub/ls");
}
public Object peers() throws IOException {
return retrieveAndParse("pubsub/peers");
}
public Object peers(String topic) throws IOException {
return retrieveAndParse("pubsub/peers?arg="+topic);
}
public Object pub(String topic, String data) throws Exception {
return retrieveAndParse("pubsub/pub?arg="+topic + "&arg=" + data);
}
public Stream> sub(String topic) throws Exception {
return sub(topic, ForkJoinPool.commonPool());
}
public Stream> sub(String topic, ForkJoinPool threadSupplier) throws Exception {
return retrieveAndParseStream("pubsub/sub?arg=" + topic, threadSupplier).map(obj -> (Map)obj);
}
/**
* A synchronous method to subscribe which consumes the calling thread
* @param topic
* @param results
* @throws IOException
*/
public void sub(String topic, Consumer> results, Consumer error) throws IOException {
retrieveAndParseStream("pubsub/sub?arg="+topic, res -> results.accept((Map)res), error);
}
}
/* 'ipfs block' is a plumbing command used to manipulate raw ipfs blocks.
*/
public class Block {
public byte[] get(Multihash hash) throws IOException {
return retrieve("block/get?stream-channels=true&arg=" + hash);
}
public List put(List data) throws IOException {
return put(data, Optional.empty());
}
public List put(List data, Optional format) throws IOException {
// N.B. Once IPFS implements a bulk put this can become a single multipart call with multiple 'files'
return data.stream().map(array -> put(array, format)).collect(Collectors.toList());
}
public MerkleNode put(byte[] data, Optional format) {
String fmt = format.map(f -> "&format=" + f).orElse("");
Multipart m = new Multipart("http://" + host + ":" + port + version+"block/put?stream-channels=true" + fmt, "UTF-8");
m.addFilePart("file", Paths.get(""), new NamedStreamable.ByteArrayWrapper(data));
String res = m.finish();
return JSONParser.parseStream(res).stream().map(x -> MerkleNode.fromJSON((Map) x)).findFirst().get();
}
public Map stat(Multihash hash) throws IOException {
return retrieveMap("block/stat?stream-channels=true&arg=" + hash);
}
}
/* 'ipfs object' is a plumbing command used to manipulate DAG objects directly. {Object} is a subset of {Block}
*/
public class IPFSObject {
public List put(List data) throws IOException {
Multipart m = new Multipart("http://" + host + ":" + port + version+"object/put?stream-channels=true", "UTF-8");
for (byte[] f : data)
m.addFilePart("file", Paths.get(""), new NamedStreamable.ByteArrayWrapper(f));
String res = m.finish();
return JSONParser.parseStream(res).stream().map(x -> MerkleNode.fromJSON((Map) x)).collect(Collectors.toList());
}
public List put(String encoding, List data) throws IOException {
if (!"json".equals(encoding) && !"protobuf".equals(encoding))
throw new IllegalArgumentException("Encoding must be json or protobuf");
Multipart m = new Multipart("http://" + host + ":" + port + version+"object/put?stream-channels=true&encoding="+encoding, "UTF-8");
for (byte[] f : data)
m.addFilePart("file", Paths.get(""), new NamedStreamable.ByteArrayWrapper(f));
String res = m.finish();
return JSONParser.parseStream(res).stream().map(x -> MerkleNode.fromJSON((Map) x)).collect(Collectors.toList());
}
public MerkleNode get(Multihash hash) throws IOException {
Map json = retrieveMap("object/get?stream-channels=true&arg=" + hash);
json.put("Hash", hash.toBase58());
return MerkleNode.fromJSON(json);
}
public MerkleNode links(Multihash hash) throws IOException {
Map json = retrieveMap("object/links?stream-channels=true&arg=" + hash);
return MerkleNode.fromJSON(json);
}
public Map stat(Multihash hash) throws IOException {
return retrieveMap("object/stat?stream-channels=true&arg=" + hash);
}
public byte[] data(Multihash hash) throws IOException {
return retrieve("object/data?stream-channels=true&arg=" + hash);
}
public MerkleNode _new(Optional template) throws IOException {
if (template.isPresent() && !ObjectTemplates.contains(template.get()))
throw new IllegalStateException("Unrecognised template: "+template.get());
Map json = retrieveMap("object/new?stream-channels=true"+(template.isPresent() ? "&arg=" + template.get() : ""));
return MerkleNode.fromJSON(json);
}
public MerkleNode patch(Multihash base, String command, Optional data, Optional name, Optional target) throws IOException {
if (!ObjectPatchTypes.contains(command))
throw new IllegalStateException("Illegal Object.patch command type: "+command);
String targetPath = "object/patch/"+command+"?arg=" + base.toBase58();
if (name.isPresent())
targetPath += "&arg=" + name.get();
if (target.isPresent())
targetPath += "&arg=" + target.get().toBase58();
switch (command) {
case "add-link":
if (!target.isPresent())
throw new IllegalStateException("add-link requires name and target!");
case "rm-link":
if (!name.isPresent())
throw new IllegalStateException("link name is required!");
return MerkleNode.fromJSON(retrieveMap(targetPath));
case "set-data":
case "append-data":
if (!data.isPresent())
throw new IllegalStateException("set-data requires data!");
Multipart m = new Multipart("http://" + host + ":" + port + version+"object/patch/"+command+"?arg="+base.toBase58()+"&stream-channels=true", "UTF-8");
m.addFilePart("file", Paths.get(""), new NamedStreamable.ByteArrayWrapper(data.get()));
String res = m.finish();
return MerkleNode.fromJSON(JSONParser.parse(res));
default:
throw new IllegalStateException("Unimplemented");
}
}
}
public class Name {
public Map publish(Multihash hash) throws IOException {
return publish(hash, Optional.empty());
}
public Map publish(Multihash hash, Optional id) throws IOException {
return retrieveMap("name/publish?arg=/ipfs/" + hash + id.map(name -> "&key=" + name).orElse(""));
}
public String resolve(Multihash hash) throws IOException {
Map res = (Map) retrieveAndParse("name/resolve?arg=" + hash);
return (String)res.get("Path");
}
}
public class DHT {
public Map findprovs(Multihash hash) throws IOException {
return retrieveMap("dht/findprovs?arg=" + hash);
}
public Map query(Multihash peerId) throws IOException {
return retrieveMap("dht/query?arg=" + peerId.toString());
}
public Map findpeer(Multihash id) throws IOException {
return retrieveMap("dht/findpeer?arg=" + id.toString());
}
public Map get(Multihash hash) throws IOException {
return retrieveMap("dht/get?arg=" + hash);
}
public Map put(String key, String value) throws IOException {
return retrieveMap("dht/put?arg=" + key + "&arg="+value);
}
}
public class File {
public Map ls(Multihash path) throws IOException {
return retrieveMap("file/ls?arg=" + path);
}
}
// Network commands
public List bootstrap() throws IOException {
return ((List)retrieveMap("bootstrap/").get("Peers"))
.stream()
.flatMap(x -> {
try {
return Stream.of(new MultiAddress(x));
} catch (Exception e) {
return Stream.empty();
}
}).collect(Collectors.toList());
}
public class Bootstrap {
public List list() throws IOException {
return bootstrap();
}
public List add(MultiAddress addr) throws IOException {
return ((List)retrieveMap("bootstrap/add?arg="+addr).get("Peers")).stream().map(x -> new MultiAddress(x)).collect(Collectors.toList());
}
public List rm(MultiAddress addr) throws IOException {
return rm(addr, false);
}
public List rm(MultiAddress addr, boolean all) throws IOException {
return ((List)retrieveMap("bootstrap/rm?"+(all ? "all=true&":"")+"arg="+addr).get("Peers")).stream().map(x -> new MultiAddress(x)).collect(Collectors.toList());
}
}
/* ipfs swarm is a tool to manipulate the network swarm. The swarm is the
component that opens, listens for, and maintains connections to other
ipfs peers in the internet.
*/
public class Swarm {
public List peers() throws IOException {
Map m = retrieveMap("swarm/peers?stream-channels=true");
return ((List)m.get("Peers")).stream()
.flatMap(json -> {
try {
return Stream.of(Peer.fromJSON(json));
} catch (Exception e) {
return Stream.empty();
}
}).collect(Collectors.toList());
}
public Map addrs() throws IOException {
Map m = retrieveMap("swarm/addrs?stream-channels=true");
return (Map)m.get("Addrs");
}
public Map connect(String multiAddr) throws IOException {
Map m = retrieveMap("swarm/connect?arg="+multiAddr);
return m;
}
public Map disconnect(String multiAddr) throws IOException {
Map m = retrieveMap("swarm/disconnect?arg="+multiAddr);
return m;
}
}
public class Dag {
public byte[] get(Cid cid) throws IOException {
return retrieve("block/get?stream-channels=true&arg=" + cid);
}
public MerkleNode put(byte[] object) throws IOException {
return put("json", object, "cbor");
}
public MerkleNode put(String inputFormat, byte[] object) throws IOException {
return put(inputFormat, object, "cbor");
}
public MerkleNode put(byte[] object, String outputFormat) throws IOException {
return put("json", object, outputFormat);
}
public MerkleNode put(String inputFormat, byte[] object, String outputFormat) throws IOException {
block.put(Arrays.asList(object));
String prefix = "http://" + host + ":" + port + version;
Multipart m = new Multipart(prefix + "block/put/?stream-channels=true&input-enc=" + inputFormat + "&f=" + outputFormat, "UTF-8");
m.addFilePart("file", Paths.get(""), new NamedStreamable.ByteArrayWrapper(object));
String res = m.finish();
return MerkleNode.fromJSON(JSONParser.parse(res));
}
}
public class Diag {
public String cmds() throws IOException {
return new String(retrieve("diag/cmds?stream-channels=true"));
}
public String sys() throws IOException {
return new String(retrieve("diag/sys?stream-channels=true"));
}
}
public Map ping(String target) throws IOException {
return retrieveMap("ping/" + target);
}
public Map id(String target) throws IOException {
return retrieveMap("id/" + target);
}
public Map id() throws IOException {
return retrieveMap("id");
}
public class Stats {
public Map bw() throws IOException {
return retrieveMap("stats/bw");
}
}
// Tools
public String version() throws IOException {
Map m = (Map)retrieveAndParse("version");
return (String)m.get("Version");
}
public Map commands() throws IOException {
return retrieveMap("commands");
}
public Map log() throws IOException {
return retrieveMap("log/tail");
}
public class Config {
public Map show() throws IOException {
return (Map)retrieveAndParse("config/show");
}
public void replace(NamedStreamable file) throws IOException {
Multipart m = new Multipart("http://" + host + ":" + port + version+"config/replace?stream-channels=true", "UTF-8");
m.addFilePart("file", Paths.get(""), file);
String res = m.finish();
}
public String get(String key) throws IOException {
Map m = (Map)retrieveAndParse("config?arg="+key);
return (String)m.get("Value");
}
public Map set(String key, String value) throws IOException {
return retrieveMap("config?arg=" + key + "&arg=" + value);
}
}
public Object update() throws IOException {
return retrieveAndParse("update");
}
public class Update {
public Object check() throws IOException {
return retrieveAndParse("update/check");
}
public Object log() throws IOException {
return retrieveAndParse("update/log");
}
}
private Map retrieveMap(String path) throws IOException {
return (Map)retrieveAndParse(path);
}
private Object retrieveAndParse(String path) throws IOException {
byte[] res = retrieve(path);
return JSONParser.parse(new String(res));
}
private Stream retrieveAndParseStream(String path, ForkJoinPool executor) {
BlockingQueue> results = new LinkedBlockingQueue<>();
executor.submit(() -> getObjectStream(path,
res -> {
results.add(CompletableFuture.completedFuture(res));
},
err -> {
CompletableFuture fut = new CompletableFuture<>();
fut.completeExceptionally(err);
results.add(fut);
})
);
return Stream.generate(() -> {
try {
return JSONParser.parse(new String(results.take().get()));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
/**
* A synchronous stream retriever that consumes the calling thread
* @param path
* @param results
* @throws IOException
*/
private void retrieveAndParseStream(String path, Consumer results, Consumer err) throws IOException {
getObjectStream(path, d -> results.accept(JSONParser.parse(new String(d))), err);
}
private byte[] retrieve(String path) throws IOException {
URL target = new URL("http", host, port, version + path);
return IPFS.get(target);
}
private static byte[] get(URL target) throws IOException {
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/json");
try {
InputStream in = conn.getInputStream();
ByteArrayOutputStream resp = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) >= 0)
resp.write(buf, 0, r);
return resp.toByteArray();
} catch (ConnectException e) {
throw new RuntimeException("Couldn't connect to IPFS daemon at "+target+"\n Is IPFS running?");
} catch (IOException e) {
String err = new String(readFully(conn.getErrorStream()));
throw new RuntimeException("IOException contacting IPFS daemon.\nTrailer: " + conn.getHeaderFields().get("Trailer") + " " + err, e);
}
}
private void getObjectStream(String path, Consumer processor, Consumer error) {
byte LINE_FEED = (byte)10;
try {
InputStream in = retrieveStream(path);
ByteArrayOutputStream resp = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int r;
while ((r = in.read(buf)) >= 0) {
resp.write(buf, 0, r);
if (buf[r - 1] == LINE_FEED) {
processor.accept(resp.toByteArray());
resp.reset();
}
}
} catch (IOException e) {
error.accept(e);
}
}
private InputStream retrieveStream(String path) throws IOException {
URL target = new URL("http", host, port, version + path);
return IPFS.getStream(target);
}
private static InputStream getStream(URL target) throws IOException {
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/json");
return conn.getInputStream();
}
private Map postMap(String path, byte[] body, Map headers) throws IOException {
URL target = new URL("http", host, port, version + path);
return (Map) JSONParser.parse(new String(post(target, body, headers)));
}
private static byte[] post(URL target, byte[] body, Map headers) throws IOException {
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
for (String key: headers.keySet())
conn.setRequestProperty(key, headers.get(key));
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
OutputStream out = conn.getOutputStream();
out.write(body);
out.flush();
out.close();
InputStream in = conn.getInputStream();
return readFully(in);
}
private static final byte[] readFully(InputStream in) throws IOException {
ByteArrayOutputStream resp = new ByteArrayOutputStream();
byte[] buf = new byte[4096];
int r;
while ((r=in.read(buf)) >= 0)
resp.write(buf, 0, r);
return resp.toByteArray();
}
}