org.fixtrading.timpani.securitydef.datastore.MongoDBSecurityDefinitionStore.java Source code

Java tutorial

Introduction

Here is the source code for org.fixtrading.timpani.securitydef.datastore.MongoDBSecurityDefinitionStore.java

Source

/**
 * Copyright 2016 FIX Protocol Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 *
 */
package org.fixtrading.timpani.securitydef.datastore;

import static com.mongodb.client.model.Filters.*;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.fixtrading.timpani.codec.JsonCodec;
import org.fixtrading.timpani.securitydef.messages.SecurityDefinition;
import org.fixtrading.timpani.securitydef.messages.SecurityDefinitionImpl;
import org.fixtrading.timpani.securitydef.messages.SecurityDefinitionRequest;
import org.fixtrading.timpani.securitydef.messages.SecurityRequestType;

import com.mongodb.Block;
import com.mongodb.ServerAddress;
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClientSettings;
import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.async.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.connection.ClusterSettings;

public class MongoDBSecurityDefinitionStore implements SecurityDefinitionStore {

    class SecurityDefinitionBlock implements Block<Document> {

        private final Consumer<SecurityDefinitionImpl> consumer;
        private int count = 0;

        public SecurityDefinitionBlock(Consumer<SecurityDefinitionImpl> consumer) {
            this.consumer = consumer;
        }

        @Override
        public void apply(Document d) {
            count++;
            SecurityDefinitionImpl securityDefinition = jsonCodec.decode(d.toJson(), SecurityDefinitionImpl.class);
            consumer.accept(securityDefinition);
        }

        public int getCount() {
            return count;
        }
    }

    private static final String DATABASE_NAME = "refdata";
    private static final String SECDEF_COLLECTION_NAME = "secdef";
    private MongoCollection<Document> collection;
    private MongoDatabase database;
    private String[] hosts = { "localhost" };
    private final JsonCodec jsonCodec = new JsonCodec();
    private MongoClient mongoClient;

    /**
     * Constructor with default localhost
     */
    public MongoDBSecurityDefinitionStore() {
    }

    /**
     * Constructor with specified hosts
     * 
     * @param hosts array of host addresses of MongoDB cluster
     */
    public MongoDBSecurityDefinitionStore(String[] hosts) {
        this.hosts = hosts;
    }

    @Override
    public void close() {
        mongoClient.close();
    }

    public CompletableFuture<SecurityDefinitionStore> delete(SecurityDefinitionRequest securityDefinitionRequest) {

        CompletableFuture<SecurityDefinitionStore> future = new CompletableFuture<>();

        // todo: handle different request types
        collection.deleteMany(eq("symbol", securityDefinitionRequest.getSymbol()),
                (DeleteResult result, final Throwable t) -> {
                    if (t == null) {
                        future.complete(this);
                    } else {
                        future.completeExceptionally(t);
                    }
                });
        return future;
    }

    @Override
    public CompletableFuture<SecurityDefinitionStore> insert(Collection<SecurityDefinition> securityDefinitions) {
        CompletableFuture<SecurityDefinitionStore> future = new CompletableFuture<>();

        List<Document> docs = securityDefinitions.stream().map(s -> {
            final String json = jsonCodec.encode(s);
            return Document.parse(json);
        }).collect(Collectors.toList());

        collection.insertMany(docs, (Void result, final Throwable t) -> {
            if (t == null) {
                future.complete(this);
            } else {
                future.completeExceptionally(t);
            }
        });
        return future;
    }

    @Override
    public CompletableFuture<SecurityDefinitionStore> insert(SecurityDefinition securityDefinition) {
        CompletableFuture<SecurityDefinitionStore> future = new CompletableFuture<>();

        final String json = jsonCodec.encode(securityDefinition);
        // convert json to bson
        Document doc = Document.parse(json);
        collection.insertOne(doc, (Void result, final Throwable t) -> {
            if (t == null) {
                future.complete(this);
            } else {
                future.completeExceptionally(t);
            }
        });
        return future;
    }

    @Override
    public CompletableFuture<SecurityDefinitionStore> open() {
        List<ServerAddress> hostList = Arrays.stream(hosts).map(h -> new ServerAddress(h))
                .collect(Collectors.toList());
        ClusterSettings clusterSettings = ClusterSettings.builder().hosts(hostList).build();
        MongoClientSettings settings = MongoClientSettings.builder().clusterSettings(clusterSettings).build();
        mongoClient = MongoClients.create(settings);

        database = mongoClient.getDatabase(DATABASE_NAME);
        collection = database.getCollection(SECDEF_COLLECTION_NAME);

        // In the case of MongoDB, open is synchronous because it doesn't
        // actually communicate with the server until a query is invoked.
        return CompletableFuture.completedFuture(this);
    }

    @Override
    public CompletableFuture<Integer> select(SecurityDefinitionRequest securityDefinitionRequest,
            Consumer<? extends SecurityDefinition> consumer) {

        CompletableFuture<Integer> future = new CompletableFuture<>();

        Bson filter = null;
        SecurityRequestType securityRequestType = securityDefinitionRequest.getSecurityRequestType();
        switch (securityRequestType) {
        case RequestSecurityIdentityAndSpecifications:
            break;
        case RequestListSecurities:
            break;
        case Symbol:
            filter = eq("symbol", securityDefinitionRequest.getSymbol());
            break;
        case SecurityTypeAndOrCFICode: {
            String securityType = securityDefinitionRequest.getSecurityType();
            String cfiCode = securityDefinitionRequest.getCfiCode();
            if (securityType != null && cfiCode != null) {
                filter = Filters.or(eq("securityType", securityDefinitionRequest.getSecurityType()),
                        eq("cfiCode", securityDefinitionRequest.getCfiCode()));
            } else if (securityType != null) {
                filter = eq("securityType", securityDefinitionRequest.getSecurityType());
            } else if (cfiCode != null) {
                filter = eq("cfiCode", securityDefinitionRequest.getCfiCode());
            } else {
                throw new IllegalArgumentException("Missing security type or CFI code");
            }
        }
            break;
        case Product: {
            String product = securityDefinitionRequest.getProduct();
            String securityGroup = securityDefinitionRequest.getSecurityGroup();
            if (product != null) {
                filter = eq("product", product);
            } else if (securityGroup != null) {
                filter = eq("securityGroup", securityGroup);
            } else {
                throw new IllegalArgumentException("Missing product");
            }
        }
            break;
        case TradingSessionID:
            filter = eq("tradingSessionID", securityDefinitionRequest.getTradingSessionID());
            break;
        case AllSecurities:
            filter = exists("symbol");
            break;
        case MarketIDOrMarketID: {
            String marketId = securityDefinitionRequest.getMarketID();
            String marketSegmentID = securityDefinitionRequest.getMarketSegmentID();
            String securityExchange = securityDefinitionRequest.getSecurityExchange();
            if (marketId != null) {
                filter = elemMatch("marketSegmentGrp", Filters.eq("marketID", marketId));
            } else if (marketSegmentID != null) {
                filter = elemMatch("marketSegmentGrp", Filters.eq("marketSegmentID", marketSegmentID));
            } else if (securityExchange != null) {
                filter = eq("securityExchange", securityExchange);
            } else {
                throw new IllegalArgumentException("Missing market or market segment");
            }
        }
            break;
        default:
            throw new UnsupportedOperationException("Request type not supported");
        }

        @SuppressWarnings("unchecked")
        SecurityDefinitionBlock block = new SecurityDefinitionBlock((Consumer<SecurityDefinitionImpl>) consumer);

        SingleResultCallback<Void> callback = (result, t) -> {
            if (t == null) {
                future.complete(block.getCount());
            } else {
                future.completeExceptionally(t);
            }
        };

        collection.find(filter).forEach(block, callback);

        return future;
    }

    @Override
    public CompletableFuture<SecurityDefinitionStore> truncate() {
        CompletableFuture<SecurityDefinitionStore> future = new CompletableFuture<>();

        collection.drop((Void result, final Throwable t) -> {
            if (t == null) {
                collection = database.getCollection(SECDEF_COLLECTION_NAME);

                future.complete(this);
            } else {
                future.completeExceptionally(t);
            }
        });
        return future;
    }

}