Client/Server Example Code

InfinityDB Client/Server provides a ‘RemoteItemSpace’ feature that allows a program to access an ItemSpace on the server as if it were local to the client.

There is an efficient binary link between the client and server that uses an ‘ItemPacket’ protocol. The ItemPacket protocol is secure, robust, transactional, and for mutations, very fast. Individual retrievals like next() require round-trips that limit performance to about 10k/s between processes on localhost, or 100/s over the internet. However there are ways to batch up the retrieval access for sequential Items (e.g. nextItems(Cu cu, int pl, byte buff[], int offset, int length)). The local end does not cache. Access to an InfinityDB instance local to a JVM is still full speed for all operations. An unlimited number of concurrent connections may be made between any servers and any clients, including multiple between a given pair. A particular connection is serialized at the client side.

There is also secure REST access from python clients or the curl command.

All communications uses SSL/TLS, and all clients authenticate by usernames/password. The server manages multiple databases, users, passwords, privileges, and roles, via a web site supported by a secure internal Servlet server that also provides a database browser.

This code uses the Map API, which provides a view of an ItemSpace as an extended java.util.concurrent.ConcurrentNavigableMap.

/*
Copyright (c) 2019 Boiler Bay Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package com.infinitydb.examples;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import com.infinitydb.Attribute;
import com.infinitydb.EntityClass;
import com.infinitydb.InfinityDB;
import com.infinitydb.ItemSpace;
import com.infinitydb.map.db.InfinityDBMap;
import com.infinitydb.remote.Authority;
import com.infinitydb.remote.Connector;
import com.infinitydb.remote.ItemSpaceAccessPermissions;
import com.infinitydb.remote.KeyStoreAccessor;
import com.infinitydb.remote.RemoteClientItemSpace;
import com.infinitydb.remote.SSLCertificateNameVerifier;
import com.infinitydb.remote.SocketConnector;

public class ClientServerExample {

    // Some EntityClasses and Attributes that provide 'internal metadata'
    // in each Item for very flexible, extensible structures.
    // These are well compressed in storage.

    // These are named [A-Z][A-Za-z0-9._-]*
    static final EntityClass TREES = new EntityClass("Trees");
    // These are named [a-z][A-Za-z0-9._-]*
    static final Attribute LEAF_TYPE = new Attribute("leafType");
    // For a nested table
    static final Attribute NURSERY = new Attribute("nursery");
    static final EntityClass NURSERY_LOCATION =
            new EntityClass("NurseryLocation");
    static final Attribute QUANTITY_ON_HAND = new Attribute("quantityOnHand");

    static final long MAX_CACHE_SIZE = 100_000;

    public static void main(String... args)
            throws IOException {

        try (InfinityDB db = InfinityDB.createTemporary(MAX_CACHE_SIZE)) {

            // The db 'ItemSpace' model goes above 1M ops/s, with 8 cores
            db.insert(TREES, "oak", LEAF_TYPE, "deciduous");
            db.insert(TREES, "red fir", LEAF_TYPE, "conifer");

            // When we add larch, we find 'deciduous' and 'conifer' are not
            // exclusive! Now we use a multi-value attribute.
            db.insert(TREES, "larch", LEAF_TYPE, "deciduous");
            db.insert(TREES, "larch", LEAF_TYPE, "conifer");

            // Use the optional Map interface, an extended
            // ConcurrentNavigableMap
            InfinityDBMap<EntityClass, String> map = new InfinityDBMap<>(db);
            for (String treeName : map.getSet(TREES)) {
                System.out.println("treeName: " + treeName);
            }

            // Add a new tree with a composite key "oak" "red".
            // The key is compound for only one of the Items!
            // That is a unique kind of flexibility.
            map.add(TREES, "oak", "red", LEAF_TYPE, "deciduous");

            // Now with the composite key, we need to iterate by tuples
            for (Object[] treeName : map.getTupleSet(TREES)) {
                System.out.println("tree: " + Arrays.toString(treeName));
            }

            // Iterate the larch's leaf types. getStringSet selects only the
            // Strings
            for (String leafType : map.getStringSet(TREES, "larch",
                    LEAF_TYPE)) {
                System.out.println("larch leaf type: " + leafType);
            }

            /*
             * Add some new Nurseries Under a 'nursery' Attribute, with a
             * 'Location' EntityClass under that. InfinityDBMaps have an
             * additional add() method, like a Set. Now we have a nested table.
             * That is a unique kind of flexibility.
             */
            map.add(TREES, "oak", NURSERY, NURSERY_LOCATION, "US",
                    "California", "Aptos", QUANTITY_ON_HAND, 3);
            map.add(TREES, "oak", "red", NURSERY, NURSERY_LOCATION, "US",
                    "California", "Capitola", QUANTITY_ON_HAND, 5);

            /*
             * How many total oaks of any sub-type do we have in all nursery
             * locations?
             */

            // This is hard without the variable compound keys.
            String tree = "oak";
            int treeTotalQuantity = 0;

            // The map containing only the trees having a particular first
            // component
            // in the name.
            InfinityDBMap<Object[], Object[]> treeSubMap =
                    map.getStringMap(TREES).getTupleMap(tree);

            // Iterate the variable compound key of tree name under just the
            // first component.
            // This will iterate every tree under "oak", getting "oak" itself,
            // plus "oak" "red". We allow locations to be variable-compound too.
            // This is very fast - the loops are small, and we don't iterate
            // everything.
            for (Object[] treeSubName : treeSubMap.keyTupleSet()) {
                InfinityDBMap<Object[], Object> locations =
                        treeSubMap.getAttributeMap(treeSubName)
                                .getEntityClassMap(NURSERY)
                                .getTupleMap(NURSERY_LOCATION);
                // Iterate locations in the nested locations map
                for (Object[] location : locations.keyTupleSet()) {
                    // The 0 is default
                    Long quantity = locations.getAttributeMap(location)
                            .getLong(QUANTITY_ON_HAND, 0);
                    treeTotalQuantity += quantity;
                }
            }
            System.out.println("oak total quantity = " + treeTotalQuantity);

            // The 'global' tx. There is also 'optimistic' for fine-grain.
            // This is not really useful, as the temp file gets deleted.
            db.commit();

            /*
             * Remote access to an InfinityDB on the infinitydb.com server.
             */

            // Optionally match also based on common name being "infinitydb"
            // It handles CN, IP, DNS, and blacklists.
//             SSLCertificateNameVerifier sslCertificateNameVerifier =
//             new SSLCertificateNameVerifier(new String[] {"infinitydb"} ,
//             null, null, null, null, null);
            SSLCertificateNameVerifier sslCertificateNameVerifier = null;
            String databaseName = "demo/writeable";
            // We are not providing an extra optional trust store.
            // $JAVA_HOME/lib/security/cacerts is included. 
            KeyStoreAccessor keystoreAccessor =
                    new KeyStoreAccessor(null, null, 
                            sslCertificateNameVerifier);
            Authority authority = new Authority("testUser", "db");
            // RemoteItemSpaceServer is at the https server port + 1
            URL url = new URL("https", "infinitydb.com", 37412,
                    "/" + databaseName);
            // For any future server params
            Map<String, String> formParams = new HashMap<>();
            SocketConnector connector = new SocketConnector(url, authority,
                    keystoreAccessor, formParams,
                    ItemSpaceAccessPermissions.READ_WRITE);

            ItemSpace remoteClientItemSpace = new RemoteClientItemSpace(connector);
            
            // Put Items in the demo/writeable remote.
            // Now you can see them in the server via the web data browser/editor
            remoteClientItemSpace.insert(TREES, "oak", LEAF_TYPE, "deciduous");
            remoteClientItemSpace.insert(TREES, "red fir", LEAF_TYPE,
                    "conifer");
            // Iterate the trees remotely.
            InfinityDBMap<EntityClass, String> remoteMap =
                    new InfinityDBMap<>(remoteClientItemSpace);
            for (String treeName : remoteMap.getSet(TREES)) {
                System.out.println("treeName: " + treeName);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}