Store your data anywhere, and move it at any time, with no client code impact.
FS2 allows you to manage arbitrary data objects using familiar operations like create, read, update, and delete. Using a simple interface that abstracts the underlying persistence details, applications can interact with their data in a way similar to using a terminal to interact with a local filesystem. Behind the curtains, a storage provider does the heavy lifting, be it integrating with a cloud storage provider like Google Cloud Storage or Amazon S3, a Mongo NoSQL database, a fileshare, or even MemCache.
There are similar technologies out there such as Apache Commons Virtual File System (VFS) and Apache Jackrabbit. FS2 distinguishes itself with the following:
FS2 decouples meta-information like object size, date created, compression, etc from the object itself. The most common meta-data. such as date created, is modeled within the meta class. Extended attributes are contained in unmodeled key value pairs called headers. Client code can interact with the FS2 api using the meta object, or URI. Here is a simple CRUD example.
// get an instance of fs2 with default properties FlexibleStorageSystem FS2 = FS2Factory.newInstance(); // create the object. initially this object will have no data associated with it, just a name and some initial metadata. FS2ObjectMeta foo = FS2.createObjectEntry("/foo"); // add a custom header field to object foo FS2.addHeader(foo, "isText", "true"); // add some contents to foo InputStream is = new ByteArrayInputStream("hello world".getBytes()); FS2.writePayloadFromStream(foo, is); // delete FS2.delete(foo);
FS2 also supports the hierarchical arrangement of objects, like you would see in a filesystem, or any tree like data structure.
// create five nodes FS2ObjectMeta[] nodes = FS2.createObjectEntries("/foo", "/foo/bar", "/foo/baz", "/foo/bar/bam", "/foo/bar/moo"); // can access meta like this FS2ObjectMeta foo = nodes[0]; FS2ObjectMeta bar = nodes[1]; // sanity check a node exists by refetching from fs2. note bar2 should equal() bar unless another thread updated (ie) headers in between create and fetch FS2ObjectMeta bar2 = FS2.fetchObject("/foo/bar"); // list descendants of foo who's names begin with "m" (expect moo) FS2.listDescendants(foo, "*/m.*"); // delete bar and bar/bam FS2.deleteRecursive(bar); // get foo's remaining descendants. (expect just baz) FS2.listDescendants(nodes[0]);
Migrating from one storage provider to another can be done with just a few lines of code. It requires obtaining two instances of fs2, one for each provider, and making a move call. Below is an example of migrating from the file storage provider to a Mongo-backed storage provider.
// copy all contents from the file repo to mongoDB FlexibleStorageSystem fileRepo = FS2Factory.newInstance("file"); // override default and force an instance backed by the "file" storage provider FlexibleStorageSystem mongoRepo = FS2Factory.newInstance("mongo"); // override default and force an instance backed by the "mongo" storage provider fileRepo.copyTo(mongoRepo);Note the monikers used to create instances of fs2. These are mapped to fqn's in a property set used by fs2 at bootstrap. Configuration properties can be extended to map any number of fs2 storage provider implementations. Out of the box, file and mem are the only two that are always configured.