All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.apiman.manager.api.es.EsStorage Maven / Gradle / Ivy

There is a newer version: 1.2.7.Final
Show newest version
/*
 * Copyright 2015 JBoss Inc
 *
 * 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 io.apiman.manager.api.es;

import io.apiman.common.util.crypt.DataEncryptionContext;
import io.apiman.common.util.crypt.IDataEncrypter;
import io.apiman.manager.api.beans.apis.ApiBean;
import io.apiman.manager.api.beans.apis.ApiGatewayBean;
import io.apiman.manager.api.beans.apis.ApiPlanBean;
import io.apiman.manager.api.beans.apis.ApiStatus;
import io.apiman.manager.api.beans.apis.ApiVersionBean;
import io.apiman.manager.api.beans.audit.AuditEntityType;
import io.apiman.manager.api.beans.audit.AuditEntryBean;
import io.apiman.manager.api.beans.clients.ClientBean;
import io.apiman.manager.api.beans.clients.ClientStatus;
import io.apiman.manager.api.beans.clients.ClientVersionBean;
import io.apiman.manager.api.beans.contracts.ContractBean;
import io.apiman.manager.api.beans.download.DownloadBean;
import io.apiman.manager.api.beans.gateways.GatewayBean;
import io.apiman.manager.api.beans.idm.PermissionBean;
import io.apiman.manager.api.beans.idm.PermissionType;
import io.apiman.manager.api.beans.idm.RoleBean;
import io.apiman.manager.api.beans.idm.RoleMembershipBean;
import io.apiman.manager.api.beans.idm.UserBean;
import io.apiman.manager.api.beans.orgs.OrganizationBean;
import io.apiman.manager.api.beans.plans.PlanBean;
import io.apiman.manager.api.beans.plans.PlanStatus;
import io.apiman.manager.api.beans.plans.PlanVersionBean;
import io.apiman.manager.api.beans.plugins.PluginBean;
import io.apiman.manager.api.beans.policies.PolicyBean;
import io.apiman.manager.api.beans.policies.PolicyDefinitionBean;
import io.apiman.manager.api.beans.policies.PolicyType;
import io.apiman.manager.api.beans.search.OrderByBean;
import io.apiman.manager.api.beans.search.PagingBean;
import io.apiman.manager.api.beans.search.SearchCriteriaBean;
import io.apiman.manager.api.beans.search.SearchCriteriaFilterBean;
import io.apiman.manager.api.beans.search.SearchCriteriaFilterOperator;
import io.apiman.manager.api.beans.search.SearchResultsBean;
import io.apiman.manager.api.beans.summary.ApiEntryBean;
import io.apiman.manager.api.beans.summary.ApiPlanSummaryBean;
import io.apiman.manager.api.beans.summary.ApiRegistryBean;
import io.apiman.manager.api.beans.summary.ApiSummaryBean;
import io.apiman.manager.api.beans.summary.ApiVersionSummaryBean;
import io.apiman.manager.api.beans.summary.ClientSummaryBean;
import io.apiman.manager.api.beans.summary.ClientVersionSummaryBean;
import io.apiman.manager.api.beans.summary.ContractSummaryBean;
import io.apiman.manager.api.beans.summary.GatewaySummaryBean;
import io.apiman.manager.api.beans.summary.OrganizationSummaryBean;
import io.apiman.manager.api.beans.summary.PlanSummaryBean;
import io.apiman.manager.api.beans.summary.PlanVersionSummaryBean;
import io.apiman.manager.api.beans.summary.PluginSummaryBean;
import io.apiman.manager.api.beans.summary.PolicyDefinitionSummaryBean;
import io.apiman.manager.api.beans.summary.PolicySummaryBean;
import io.apiman.manager.api.core.IStorage;
import io.apiman.manager.api.core.IStorageQuery;
import io.apiman.manager.api.core.exceptions.StorageException;
import io.apiman.manager.api.core.util.PolicyTemplateUtil;
import io.apiman.manager.api.es.beans.ApiDefinitionBean;
import io.apiman.manager.api.es.beans.PoliciesBean;
import io.apiman.manager.api.es.util.AndFilterBuilder;
import io.apiman.manager.api.es.util.FilterBuilders;
import io.apiman.manager.api.es.util.FilteredQueryBuilder;
import io.apiman.manager.api.es.util.QueryBuilder;
import io.apiman.manager.api.es.util.QueryBuilders;
import io.apiman.manager.api.es.util.SearchSourceBuilder;
import io.apiman.manager.api.es.util.SortOrder;
import io.apiman.manager.api.es.util.TermsQueryBuilder;
import io.apiman.manager.api.es.util.XContentBuilder;
import io.searchbox.action.Action;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.cluster.Health;
import io.searchbox.core.Delete;
import io.searchbox.core.DeleteByQuery;
import io.searchbox.core.Get;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import io.searchbox.core.SearchResult;
import io.searchbox.core.SearchResult.Hit;
import io.searchbox.core.SearchScroll;
import io.searchbox.core.SearchScroll.Builder;
import io.searchbox.indices.CreateIndex;
import io.searchbox.indices.IndicesExists;
import io.searchbox.params.Parameters;
import io.searchbox.params.SearchType;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;

import com.google.gson.Gson;

/**
 * An implementation of the API Manager persistence layer that uses git to store
 * the entities.
 *
 * @author [email protected]
 */
@ApplicationScoped @Alternative
public class EsStorage implements IStorage, IStorageQuery {

    private static final String DEFAULT_INDEX_NAME = "apiman_manager"; //$NON-NLS-1$

    private static int guidCounter = 100;

    @Inject @Named("storage")
    JestClient esClient;
    @Inject IDataEncrypter encrypter;
    @PostConstruct
    public void postConstruct() {
        // Kick the encrypter, causing it to be loaded/resolved in CDI
        encrypter.encrypt("", new DataEncryptionContext()); //$NON-NLS-1$
    }

    private String indexName = DEFAULT_INDEX_NAME;

    /**
     * Constructor.
     */
    public EsStorage() {
    }

    /**
     * Called to initialize the storage.
     */
    @Override
    public void initialize() {
        try {
            esClient.execute(new Health.Builder().build());
            // TODO Do we need a loop to wait for all nodes to join the cluster?
            Action action = new IndicesExists.Builder(getIndexName()).build();
            JestResult result = esClient.execute(action);
            if (! result.isSucceeded()) {
                createIndex(getIndexName());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param indexName
     * @throws Exception
     */
    private void createIndex(String indexName) throws Exception {
        URL settings = getClass().getResource("index-settings.json"); //$NON-NLS-1$
        String source = IOUtils.toString(settings);
        JestResult response = esClient.execute(new CreateIndex.Builder(indexName).settings(source).build());
        if (!response.isSucceeded()) {
            throw new StorageException("Failed to create index " + indexName + ": " + response.getErrorMessage()); //$NON-NLS-1$ //$NON-NLS-2$
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#beginTx()
     */
    @Override
    public void beginTx() throws StorageException {
        // No Transaction support for ES
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#commitTx()
     */
    @Override
    public void commitTx() throws StorageException {
        // No Transaction support for ES
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#rollbackTx()
     */
    @Override
    public void rollbackTx() {
        // No Transaction support for ES
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createOrganization(io.apiman.manager.api.beans.orgs.OrganizationBean)
     */
    @Override
    public void createOrganization(OrganizationBean organization) throws StorageException {
        indexEntity("organization", organization.getId(), EsMarshalling.marshall(organization), true); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createClient(io.apiman.manager.api.beans.clients.ClientBean)
     */
    @Override
    public void createClient(ClientBean client) throws StorageException {
        indexEntity("client", id(client.getOrganization().getId(), client.getId()), EsMarshalling.marshall(client)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createClientVersion(io.apiman.manager.api.beans.clients.ClientVersionBean)
     */
    @Override
    public void createClientVersion(ClientVersionBean version) throws StorageException {
        ClientBean client = version.getClient();
        String id = id(client.getOrganization().getId(), client.getId(), version.getVersion());
        indexEntity("clientVersion", id, EsMarshalling.marshall(version)); //$NON-NLS-1$
        PoliciesBean policies = PoliciesBean.from(PolicyType.Client, client.getOrganization().getId(),
                client.getId(), version.getVersion());
        indexEntity("clientPolicies", id, EsMarshalling.marshall(policies)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createContract(io.apiman.manager.api.beans.contracts.ContractBean)
     */
    @Override
    public void createContract(ContractBean contract) throws StorageException {
        List contracts = getClientContracts(contract.getClient().getClient().getOrganization().getId(),
                contract.getClient().getClient().getId(), contract.getClient().getVersion());
        for (ContractSummaryBean csb : contracts) {
            if (csb.getApiOrganizationId().equals(contract.getApi().getApi().getOrganization().getId()) &&
                    csb.getApiId().equals(contract.getApi().getApi().getId()) &&
                    csb.getApiVersion().equals(contract.getApi().getVersion()))
                {
                    throw new StorageException("Error creating contract: duplicate contract detected."); //$NON-NLS-1$
                }
        }
        contract.setId(generateGuid());
        indexEntity("contract", String.valueOf(contract.getId()), EsMarshalling.marshall(contract), true); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createApi(io.apiman.manager.api.beans.apis.ApiBean)
     */
    @Override
    public void createApi(ApiBean api) throws StorageException {
        indexEntity("api", id(api.getOrganization().getId(), api.getId()), EsMarshalling.marshall(api)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createApiVersion(io.apiman.manager.api.beans.apis.ApiVersionBean)
     */
    @Override
    public void createApiVersion(ApiVersionBean version) throws StorageException {
        ApiBean api = version.getApi();
        String id = id(api.getOrganization().getId(), api.getId(), version.getVersion());
        indexEntity("apiVersion", id, EsMarshalling.marshall(version)); //$NON-NLS-1$
        PoliciesBean policies = PoliciesBean.from(PolicyType.Api, api.getOrganization().getId(),
                api.getId(), version.getVersion());
        indexEntity("apiPolicies", id, EsMarshalling.marshall(policies)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createPlan(io.apiman.manager.api.beans.plans.PlanBean)
     */
    @Override
    public void createPlan(PlanBean plan) throws StorageException {
        indexEntity("plan", id(plan.getOrganization().getId(), plan.getId()), EsMarshalling.marshall(plan)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createPlanVersion(io.apiman.manager.api.beans.plans.PlanVersionBean)
     */
    @Override
    public void createPlanVersion(PlanVersionBean version) throws StorageException {
        PlanBean plan = version.getPlan();
        String id = id(plan.getOrganization().getId(), plan.getId(), version.getVersion());
        indexEntity("planVersion", id, EsMarshalling.marshall(version)); //$NON-NLS-1$
        PoliciesBean policies = PoliciesBean.from(PolicyType.Plan, plan.getOrganization().getId(),
                plan.getId(), version.getVersion());
        indexEntity("planPolicies", id, EsMarshalling.marshall(policies)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createPolicy(io.apiman.manager.api.beans.policies.PolicyBean)
     */
    @Override
    public void createPolicy(PolicyBean policy) throws StorageException {
        String docType = getPoliciesDocType(policy.getType());
        String id = id(policy.getOrganizationId(), policy.getEntityId(), policy.getEntityVersion());
        Map source = getEntity(docType, id);
        if (source == null) {
            throw new StorageException("Failed to create policy (missing PoliciesBean)."); //$NON-NLS-1$
        }
        PoliciesBean policies = EsMarshalling.unmarshallPolicies(source);
        policy.setId(generateGuid());
        policies.getPolicies().add(policy);
        orderPolicies(policies);
        updateEntity(docType, id, EsMarshalling.marshall(policies));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#reorderPolicies(io.apiman.manager.api.beans.policies.PolicyType, java.lang.String, java.lang.String, java.lang.String, java.util.List)
     */
    @Override
    public void reorderPolicies(PolicyType type, String organizationId, String entityId,
            String entityVersion, List newOrder) throws StorageException {
        String docType = getPoliciesDocType(type);
        String pid = id(organizationId, entityId, entityVersion);
        Map source = getEntity(docType, pid);
        if (source == null) {
            return;
        }
        PoliciesBean policiesBean = EsMarshalling.unmarshallPolicies(source);
        List policies = policiesBean.getPolicies();
        List reordered = new ArrayList<>(policies.size());
        for (Long policyId : newOrder) {
            ListIterator iterator = policies.listIterator();
            while (iterator.hasNext()) {
                PolicyBean policyBean = iterator.next();
                if (policyBean.getId().equals(policyId)) {
                    iterator.remove();
                    reordered.add(policyBean);
                    break;
                }
            }
        }
        // Make sure we don't stealth-delete any policies.  Put anything
        // remaining at the end of the list.
        for (PolicyBean policyBean : policies) {
            reordered.add(policyBean);
        }
        policiesBean.setPolicies(reordered);
        updateEntity(docType, pid, EsMarshalling.marshall(policiesBean));
    }

    /**
     * Set the order index of all policies.
     * @param policies
     */
    private void orderPolicies(PoliciesBean policies) {
        int idx = 1;
        for (PolicyBean policy : policies.getPolicies()) {
            policy.setOrderIndex(idx++);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createGateway(io.apiman.manager.api.beans.gateways.GatewayBean)
     */
    @Override
    public void createGateway(GatewayBean gateway) throws StorageException {
        indexEntity("gateway", gateway.getId(), EsMarshalling.marshall(gateway)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createPlugin(io.apiman.manager.api.beans.plugins.PluginBean)
     */
    @Override
    public void createPlugin(PluginBean plugin) throws StorageException {
        plugin.setId(generateGuid());
        indexEntity("plugin", String.valueOf(plugin.getId()), EsMarshalling.marshall(plugin), true); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createDownload(io.apiman.manager.api.beans.download.DownloadBean)
     */
    @Override
    public void createDownload(DownloadBean download) throws StorageException {
        indexEntity("download", download.getId(), EsMarshalling.marshall(download)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createPolicyDefinition(io.apiman.manager.api.beans.policies.PolicyDefinitionBean)
     */
    @Override
    public void createPolicyDefinition(PolicyDefinitionBean policyDef) throws StorageException {
        indexEntity("policyDef", policyDef.getId(), EsMarshalling.marshall(policyDef)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createRole(io.apiman.manager.api.beans.idm.RoleBean)
     */
    @Override
    public void createRole(RoleBean role) throws StorageException {
        indexEntity("role", role.getId(), EsMarshalling.marshall(role)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createAuditEntry(io.apiman.manager.api.beans.audit.AuditEntryBean)
     */
    @Override
    public void createAuditEntry(AuditEntryBean entry) throws StorageException {
        if (entry == null) {
            return;
        }
        entry.setId(generateGuid());
        indexEntity("auditEntry", String.valueOf(entry.getId()), EsMarshalling.marshall(entry)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateOrganization(io.apiman.manager.api.beans.orgs.OrganizationBean)
     */
    @Override
    public void updateOrganization(OrganizationBean organization) throws StorageException {
        updateEntity("organization", organization.getId(), EsMarshalling.marshall(organization)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateClient(io.apiman.manager.api.beans.clients.ClientBean)
     */
    @Override
    public void updateClient(ClientBean client) throws StorageException {
        updateEntity("client", id(client.getOrganization().getId(), client.getId()), EsMarshalling.marshall(client)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateClientVersion(io.apiman.manager.api.beans.clients.ClientVersionBean)
     */
    @Override
    public void updateClientVersion(ClientVersionBean version) throws StorageException {
        ClientBean client = version.getClient();
        updateEntity("clientVersion", id(client.getOrganization().getId(), client.getId(), version.getVersion()),  //$NON-NLS-1$
                EsMarshalling.marshall(version));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateApi(io.apiman.manager.api.beans.apis.ApiBean)
     */
    @Override
    public void updateApi(ApiBean api) throws StorageException {
        updateEntity("api", id(api.getOrganization().getId(), api.getId()), EsMarshalling.marshall(api)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateApiVersion(io.apiman.manager.api.beans.apis.ApiVersionBean)
     */
    @Override
    public void updateApiVersion(ApiVersionBean version) throws StorageException {
        ApiBean api = version.getApi();
        updateEntity("apiVersion", id(api.getOrganization().getId(), api.getId(), version.getVersion()),  //$NON-NLS-1$
                EsMarshalling.marshall(version));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateApiDefinition(io.apiman.manager.api.beans.apis.ApiVersionBean, java.io.InputStream)
     */
    @Override
    public void updateApiDefinition(ApiVersionBean version, InputStream definitionStream)
            throws StorageException {
        InputStream apiDefinition = null;
        try {
            String id = id(version.getApi().getOrganization().getId(), version.getApi().getId(), version.getVersion()) + ":def"; //$NON-NLS-1$
            apiDefinition = getApiDefinition(version);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtils.copy(definitionStream, baos);
            String data = Base64.encodeBase64String(baos.toByteArray());
            ApiDefinitionBean definition = new ApiDefinitionBean();
            definition.setData(data);
            if (apiDefinition == null) {
                indexEntity("apiDefinition", id, EsMarshalling.marshall(definition)); //$NON-NLS-1$
            } else {
                updateEntity("apiDefinition", id, EsMarshalling.marshall(definition)); //$NON-NLS-1$
            }
        } catch (IOException e) {
            throw new StorageException(e);
        } finally {
            IOUtils.closeQuietly(apiDefinition);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updatePlan(io.apiman.manager.api.beans.plans.PlanBean)
     */
    @Override
    public void updatePlan(PlanBean plan) throws StorageException {
        updateEntity("plan", id(plan.getOrganization().getId(), plan.getId()), EsMarshalling.marshall(plan)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updatePlanVersion(io.apiman.manager.api.beans.plans.PlanVersionBean)
     */
    @Override
    public void updatePlanVersion(PlanVersionBean version) throws StorageException {
        PlanBean plan = version.getPlan();
        updateEntity("planVersion", id(plan.getOrganization().getId(), plan.getId(), version.getVersion()),  //$NON-NLS-1$
                EsMarshalling.marshall(version));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updatePolicy(io.apiman.manager.api.beans.policies.PolicyBean)
     */
    @Override
    public void updatePolicy(PolicyBean policy) throws StorageException {
        String docType = getPoliciesDocType(policy.getType());
        String pid = id(policy.getOrganizationId(), policy.getEntityId(), policy.getEntityVersion());
        Map source = getEntity(docType, pid);
        if (source == null) {
            throw new StorageException("Policy not found."); //$NON-NLS-1$
        }
        PoliciesBean policies = EsMarshalling.unmarshallPolicies(source);
        List policyBeans = policies.getPolicies();
        boolean found = false;
        if (policyBeans != null) {
            for (PolicyBean policyBean : policyBeans) {
                if (policyBean.getId().equals(policy.getId())) {
                    policyBean.setConfiguration(policy.getConfiguration());
                    policyBean.setModifiedBy(policy.getModifiedBy());
                    policyBean.setModifiedOn(policy.getModifiedOn());
                    found = true;
                    break;
                }
            }
        }
        if (found) {
            updateEntity(docType, pid, EsMarshalling.marshall(policies));
        } else {
            throw new StorageException("Policy not found."); //$NON-NLS-1$
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateGateway(io.apiman.manager.api.beans.gateways.GatewayBean)
     */
    @Override
    public void updateGateway(GatewayBean gateway) throws StorageException {
        updateEntity("gateway", gateway.getId(), EsMarshalling.marshall(gateway)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updatePolicyDefinition(io.apiman.manager.api.beans.policies.PolicyDefinitionBean)
     */
    @Override
    public void updatePolicyDefinition(PolicyDefinitionBean policyDef) throws StorageException {
        updateEntity("policyDef", policyDef.getId(), EsMarshalling.marshall(policyDef)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updatePlugin(io.apiman.manager.api.beans.plugins.PluginBean)
     */
    @Override
    public void updatePlugin(PluginBean pluginBean) throws StorageException {
        updateEntity("plugin", String.valueOf(pluginBean.getId()), EsMarshalling.marshall(pluginBean)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateRole(io.apiman.manager.api.beans.idm.RoleBean)
     */
    @Override
    public void updateRole(RoleBean role) throws StorageException {
        updateEntity("role", role.getId(), EsMarshalling.marshall(role)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteOrganization(io.apiman.manager.api.beans.orgs.OrganizationBean)
     */
    @Override
    @SuppressWarnings("nls")
    public void deleteOrganization(OrganizationBean organization) throws StorageException {
        String orgId = organization.getId().replace('"', '_');
        String query = "{\n" +
                "  \"query\": {\n" +
                "    \"filtered\": {\n" +
                "      \"query\": {\n" +
                "        \"match_all\": {}\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"or\": [\n" +
                "          {\n" +
                "            \"term\": {\n" +
                "              \"organizationId\": \"" + orgId + "\"\n" +
                "            }\n" +
                "          },\n" +
                "          {\n" +
                "            \"term\": {\n" +
                "              \"clientOrganizationId\": \"" + orgId + "\"\n" +
                "            }\n" +
                "          },\n" +
                "          {\n" +
                "            \"term\": {\n" +
                "              \"apiOrganizationId\": \"" + orgId + "\"\n" +
                "            }\n" +
                "          }\n" +
                "        ]\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}";
        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(query).addIndex(getIndexName())
                .addType("api")
                .addType("apiPolicies")
                .addType("apiVersion")
                .addType("auditEntry")
                .addType("client")
                .addType("clientPolicies")
                .addType("clientVersion")
                .addType("contract")
                .addType("plan")
                .addType("planPolicies")
                .addType("planVersion")
                .addType("roleMembership")
                .build();
        try {
            JestResult response = esClient.execute(deleteByQuery);
            if (!response.isSucceeded()) {
                throw new StorageException(response.getErrorMessage());
            }
        } catch (Exception e) {
            throw new StorageException(e);
        }
        deleteEntity("organization", orgId); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteClient(io.apiman.manager.api.beans.clients.ClientBean)
     */
    @Override
    @SuppressWarnings("nls")
    public void deleteClient(ClientBean client) throws StorageException {
        String clientId = client.getId().replace('"', '_');
        String orgId = client.getOrganization().getId().replace('"', '_');
        String query = "{\n" +
                "  \"query\": {\n" +
                "    \"filtered\": {\n" +
                "      \"query\": {\n" +
                "        \"match_all\": {}\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"bool\": {\n" +
                "          \"must\": [\n" +
                "            {\n" +
                "              \"bool\": {\n" +
                "                \"should\": [\n" +
                "                  {\n" +
                "                    \"term\": {\n" +
                "                      \"clientOrganizationId\": \"" + orgId + "\"\n" +
                "                    }\n" +
                "                  },\n" +
                "                  {\n" +
                "                    \"term\": {\n" +
                "                      \"organizationId\": \"" + orgId + "\"\n" +
                "                    }\n" +
                "                  }\n" +
                "                ]\n" +
                "              }\n" +
                "            },\n" +
                "            {\n" +
                "              \"bool\": {\n" +
                "                \"should\": [\n" +
                "                  {\n" +
                "                    \"bool\": {\n" +
                "                      \"must\": [\n" +
                "                        {\n" +
                "                          \"term\": {\n" +
                "                            \"entityId\": \"" + clientId + "\"\n" +
                "                          }\n" +
                "                        },\n" +
                "                        {\n" +
                "                          \"term\": {\n" +
                "                            \"entityType\": \"" + AuditEntityType.Client.name() + "\"\n" +
                "                          }\n" +
                "                        }\n" +
                "                      ]\n" +
                "                    }\n" +
                "                  },\n" +
                "                  {\n" +
                "                    \"bool\": {\n" +
                "                      \"must\": [\n" +
                "                        {\n" +
                "                          \"term\": {\n" +
                "                             \"entityId\": \"" + clientId + "\"\n" +
                "                           }\n" +
                "                         },\n" +
                "                         {\n" +
                "                           \"term\": {\n" +
                "                               \"type\": \"" + AuditEntityType.Client.name() + "\"\n" +
                "                             }\n" +
                "                           }\n" +
                "                         ]\n" +
                "                       }\n" +
                "                     },\n" +
                "                     {\n" +
                "                       \"term\": {\n" +
                "                           \"clientId\": \"" + clientId + "\"\n" +
                "                       }\n" +
                "                     }" +
                "                ]\n" +
                "              }\n" +
                "            }\n" +
                "          ]\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}";

        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(query).addIndex(getIndexName())
                .addType("auditEntry")
                .addType("client")
                .addType("clientVersion")
                .addType("clientPolicies")
                .addType("contract")
                .build();
        try {
            JestResult response = esClient.execute(deleteByQuery);
            if (!response.isSucceeded()) {
                throw new StorageException(response.getErrorMessage());
            }
        } catch (Exception e) {
            throw new StorageException(e);
        }
        deleteEntity("client", id(orgId, clientId)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteClientVersion(io.apiman.manager.api.beans.clients.ClientVersionBean)
     */
    @Override
    public void deleteClientVersion(ClientVersionBean version) throws StorageException {
        ClientBean client = version.getClient();
        deleteEntity("clientVersion", id(client.getOrganization().getId(), client.getId(), version.getVersion())); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteContract(io.apiman.manager.api.beans.contracts.ContractBean)
     */
    @Override
    public void deleteContract(ContractBean contract) throws StorageException {
        deleteEntity("contract", String.valueOf(contract.getId())); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteApi(io.apiman.manager.api.beans.apis.ApiBean)
     */
    @Override
    @SuppressWarnings("nls")
    public void deleteApi(ApiBean api) throws StorageException {
        String apiId = api.getId().replace('"', '_');
        String orgId = api.getOrganization().getId().replace('"', '_');

        String query = "{\n" +
        "    \"query\": {\n" +
        "        \"filtered\": {\n" +
        "            \"query\": {\n" +
        "                \"match_all\": {}\n" +
        "            },\n" +
        "            \"filter\": {\n" +
        "                \"bool\": {\n" +
        "                    \"must\": [\n" +
        "                        {\n" +
        "                            \"bool\": {\n" +
        "                                \"should\": [\n" +
        "                                    {\n" +
        "                                        \"term\": {\n" +
        "                                            \"apiOrganizationId\": \"" + orgId + "\"\n" +
        "                                        }\n" +
        "                                    },\n" +
        "                                    {\n" +
        "                                        \"term\": {\n" +
        "                                            \"organizationId\": \"" + orgId + "\"\n" +
        "                                        }\n" +
        "                                    }\n" +
        "                                ]\n" +
        "                            }\n" +
        "                        },\n" +
        "                        {\n" +
        "                            \"bool\": {\n" +
        "                                \"should\": [\n" +
        "                                    {\n" +
        "                                        \"bool\": {\n" +
        "                                            \"must\": [\n" +
        "                                                {\n" +
        "                                                    \"term\": {\n" +
        "                                                        \"entityId\": \"" + apiId + "\"\n" +
        "                                                    }\n" +
        "                                                },\n" +
        "                                                {\n" +
        "                                                    \"term\": {\n" +
        "                                                        \"entityType\": \"" + AuditEntityType.Api.name() + "\"\n" +
        "                                                    }\n" +
        "                                                }\n" +
        "                                            ]\n" +
        "                                        }\n" +
        "                                    },\n" +
        "                                    {\n" +
        "                                        \"bool\": {\n" +
        "                                            \"must\": [\n" +
        "                                                {\n" +
        "                                                    \"term\": {\n" +
        "                                                        \"entityId\": \"" + apiId + "\"\n" +
        "                                                    }\n" +
        "                                                },\n" +
        "                                                {\n" +
        "                                                    \"term\": {\n" +
        "                                                        \"type\": \"" + AuditEntityType.Api.name() + "\"\n" +
        "                                                    }\n" +
        "                                                }\n" +
        "                                            ]\n" +
        "                                        }\n" +
        "                                    },\n" +
        "                                    {\n" +
        "                                        \"term\": {\n" +
        "                                            \"apiId\": \"" + apiId + "\"\n" +
        "                                        }\n" +
        "                                    }\n" +
        "                                ]\n" +
        "                            }\n" +
        "                        }\n" +
        "                    ]\n" +
        "                }\n" +
        "            }\n" +
        "        }\n" +
        "    }\n" +
        "}";
        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(query).addIndex(getIndexName())
                .addType("auditEntry")
                .addType("api")
                .addType("apiVersion")
                .addType("apiPolicies")
                .addType("contract")
                .build();
        try {
            JestResult response = esClient.execute(deleteByQuery);
            if (!response.isSucceeded()) {
                throw new StorageException(response.getErrorMessage());
            }
        } catch (Exception e) {
            throw new StorageException(e);
        }
        deleteEntity("api", id(orgId, apiId)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteApiVersion(io.apiman.manager.api.beans.apis.ApiVersionBean)
     */
    @Override
    public void deleteApiVersion(ApiVersionBean version) throws StorageException {
        deleteApiDefinition(version);
        ApiBean api = version.getApi();
        String id = id(api.getOrganization().getId(), api.getId(), version.getVersion());
        deleteEntity("apiVersion", id); //$NON-NLS-1$
        deleteEntity("apiPolicies", id); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteApiDefinition(io.apiman.manager.api.beans.apis.ApiVersionBean)
     */
    @Override
    public void deleteApiDefinition(ApiVersionBean version) throws StorageException {
        String id = id(version.getApi().getOrganization().getId(), version.getApi().getId(), version.getVersion()) + ":def"; //$NON-NLS-1$
        deleteEntity("apiDefinition", id); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deletePlan(io.apiman.manager.api.beans.plans.PlanBean)
     */
    @Override
    @SuppressWarnings("nls")
    public void deletePlan(PlanBean plan) throws StorageException {
        String planId = plan.getId().replace('"', '_');
        String orgId = plan.getOrganization().getId().replace('"', '_');

        String query = "{\n" +
                "  \"query\": {\n" +
                "    \"filtered\": {\n" +
                "      \"query\": {\n" +
                "        \"match_all\": {}\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"or\": [\n" +
                "          {\n" +
                "            \"and\": [\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"entityId\": \"" + planId + "\"\n" +
                "                }\n" +
                "              },\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"entityType\": \"" + AuditEntityType.Plan.name() + "\"\n" +
                "                }\n" +
                "              },\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"organizationId\": \"" + orgId + "\"\n" +
                "                }\n" +
                "              }\n" +
                "            ]\n" +
                "          },\n" +
                "          {\n" +
                "            \"and\": [\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"planId\": \"" + planId + "\"\n" +
                "                }\n" +
                "              },\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"organizationId\": \"" + orgId + "\"\n" +
                "                }\n" +
                "              }\n" +
                "            ]\n" +
                "          },\n" +
                "          {\n" +
                "            \"and\": [\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"entityId\": \"" + planId + "\"\n" +
                "                }\n" +
                "              },\n" +
                "              {\n" +
                "                \"term\": {\n" +
                "                  \"type\": \"" + AuditEntityType.Plan.name() + "\"\n" +
                "                }\n" +
                "              }\n" +
                "            ]\n" +
                "          }\n" +
                "        ]\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}";
        DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(query).addIndex(getIndexName())
                .addType("auditEntry")
                .addType("planVersion")
                .build();
        try {
            JestResult response = esClient.execute(deleteByQuery);
            if (!response.isSucceeded()) {
                throw new StorageException(response.getErrorMessage());
            }
        } catch (Exception e) {
            throw new StorageException(e);
        }
        deleteEntity("plan", id(plan.getOrganization().getId(), plan.getId())); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deletePlanVersion(io.apiman.manager.api.beans.plans.PlanVersionBean)
     */
    @Override
    public void deletePlanVersion(PlanVersionBean version) throws StorageException {
        PlanBean plan = version.getPlan();
        deleteEntity("planVersion", id(plan.getOrganization().getId(), plan.getId(), version.getVersion())); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deletePolicy(io.apiman.manager.api.beans.policies.PolicyBean)
     */
    @Override
    public void deletePolicy(PolicyBean policy) throws StorageException {
        String docType = getPoliciesDocType(policy.getType());
        String pid = id(policy.getOrganizationId(), policy.getEntityId(), policy.getEntityVersion());
        Map source = getEntity(docType, pid);
        if (source == null) {
            throw new StorageException("Policy not found."); //$NON-NLS-1$
        }
        PoliciesBean policies = EsMarshalling.unmarshallPolicies(source);
        if (policies == null) throw new StorageException("Policy not found."); //$NON-NLS-1$
        List policyBeans = policies.getPolicies();
        boolean found = false;
        if (policyBeans != null) {
            for (PolicyBean policyBean : policyBeans) {
                if (policyBean.getId().equals(policy.getId())) {
                    policies.getPolicies().remove(policyBean);
                    found = true;
                    break;
                }
            }
        }
        if (found) {
            updateEntity(docType, pid, EsMarshalling.marshall(policies));
        } else {
            throw new StorageException("Policy not found."); //$NON-NLS-1$
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteGateway(io.apiman.manager.api.beans.gateways.GatewayBean)
     */
    @Override
    public void deleteGateway(GatewayBean gateway) throws StorageException {
        deleteEntity("gateway", gateway.getId()); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deletePlugin(io.apiman.manager.api.beans.plugins.PluginBean)
     */
    @Override
    public void deletePlugin(PluginBean plugin) throws StorageException {
        deleteEntity("plugin", String.valueOf(plugin.getId())); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteDownload(io.apiman.manager.api.beans.download.DownloadBean)
     */
    @Override
    public void deleteDownload(DownloadBean download) throws StorageException {
        deleteEntity("download", download.getId()); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deletePolicyDefinition(io.apiman.manager.api.beans.policies.PolicyDefinitionBean)
     */
    @Override
    public void deletePolicyDefinition(PolicyDefinitionBean policyDef) throws StorageException {
        deleteEntity("policyDef", policyDef.getId()); //$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see io.apiman.manager.api.core.IStorage#deleteRole(io.apiman.manager.api.beans.idm.RoleBean)
     */
    @Override
    public void deleteRole(RoleBean role) throws StorageException {
        deleteEntity("role", role.getId()); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getOrganization(java.lang.String)
     */
    @Override
    public OrganizationBean getOrganization(String id) throws StorageException {
        Map source = getEntity("organization", id); //$NON-NLS-1$
        return EsMarshalling.unmarshallOrganization(source);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getClient(java.lang.String, java.lang.String)
     */
    @Override
    public ClientBean getClient(String organizationId, String id) throws StorageException {
        Map source = getEntity("client", id(organizationId, id)); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        ClientBean bean = EsMarshalling.unmarshallClient(source);
        bean.setOrganization(getOrganization(organizationId));
        return bean;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getClientVersion(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public ClientVersionBean getClientVersion(String organizationId, String clientId,
            String version) throws StorageException {
        Map source = getEntity("clientVersion", id(organizationId, clientId, version)); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        ClientVersionBean bean = EsMarshalling.unmarshallClientVersion(source);
        bean.setClient(getClient(organizationId, clientId));
        return bean;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getContract(java.lang.Long)
     */
    @SuppressWarnings("nls")
    @Override
    public ContractBean getContract(Long id) throws StorageException {
        Map source = getEntity("contract", String.valueOf(id)); //$NON-NLS-1$
        ContractBean contract = EsMarshalling.unmarshallContract(source);
        if (contract == null) {
            return null;
        }
        String clientOrgId = (String) source.get("clientOrganizationId");
        String clientId = (String) source.get("clientId");
        String clientVersion = (String) source.get("clientVersion");
        String apiOrgId = (String) source.get("apiOrganizationId");
        String apiId = (String) source.get("apiId");
        String apiVersion = (String) source.get("apiVersion");
        String planId = (String) source.get("planId");
        String planVersion = (String) source.get("planVersion");
        ClientVersionBean avb = getClientVersion(clientOrgId, clientId, clientVersion);
        ApiVersionBean svb = getApiVersion(apiOrgId, apiId, apiVersion);
        PlanVersionBean pvb = getPlanVersion(apiOrgId, planId, planVersion);
        contract.setClient(avb);
        contract.setPlan(pvb);
        contract.setApi(svb);
        return contract;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getApi(java.lang.String, java.lang.String)
     */
    @Override
    public ApiBean getApi(String organizationId, String id) throws StorageException {
        Map source = getEntity("api", id(organizationId, id)); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        ApiBean bean = EsMarshalling.unmarshallApi(source);
        bean.setOrganization(getOrganization(organizationId));
        return bean;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getApiVersion(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public ApiVersionBean getApiVersion(String organizationId, String apiId, String version)
            throws StorageException {
        Map source = getEntity("apiVersion", id(organizationId, apiId, version)); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        ApiVersionBean bean = EsMarshalling.unmarshallApiVersion(source);
        bean.setApi(getApi(organizationId, apiId));
        return bean;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getApiDefinition(io.apiman.manager.api.beans.apis.ApiVersionBean)
     */
    @Override
    public InputStream getApiDefinition(ApiVersionBean version) throws StorageException {
        String id = id(version.getApi().getOrganization().getId(), version.getApi().getId(), version.getVersion()) + ":def"; //$NON-NLS-1$
        Map source = getEntity("apiDefinition", id); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        ApiDefinitionBean def = EsMarshalling.unmarshallApiDefinition(source);
        if (def == null) return null;
        String data = def.getData();
        return new ByteArrayInputStream(Base64.decodeBase64(data));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getPlan(java.lang.String, java.lang.String)
     */
    @Override
    public PlanBean getPlan(String organizationId, String id) throws StorageException {
        Map source = getEntity("plan", id(organizationId, id)); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        PlanBean bean = EsMarshalling.unmarshallPlan(source);
        bean.setOrganization(getOrganization(organizationId));
        return bean;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getPlanVersion(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public PlanVersionBean getPlanVersion(String organizationId, String planId, String version)
            throws StorageException {
        Map source = getEntity("planVersion", id(organizationId, planId, version)); //$NON-NLS-1$
        if (source == null) {
            return null;
        }
        PlanVersionBean bean = EsMarshalling.unmarshallPlanVersion(source);
        bean.setPlan(getPlan(organizationId, planId));
        return bean;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getPolicy(io.apiman.manager.api.beans.policies.PolicyType, java.lang.String, java.lang.String, java.lang.String, java.lang.Long)
     */
    @Override
    public PolicyBean getPolicy(PolicyType type, String organizationId, String entityId, String version,
            Long id) throws StorageException {
        String docType = getPoliciesDocType(type);
        String pid = id(organizationId, entityId, version);
        Map source = getEntity(docType, pid);
        if (source == null) {
            return null;
        }
        PoliciesBean policies = EsMarshalling.unmarshallPolicies(source);
        if (policies == null) return null;
        List policyBeans = policies.getPolicies();
        if (policyBeans != null) {
            for (PolicyBean policyBean : policyBeans) {
                if (policyBean.getId().equals(id)) {
                    PolicyDefinitionBean def = getPolicyDefinition(policyBean.getDefinition().getId());
                    policyBean.setDefinition(def);
                    return policyBean;
                }
            }
        }
        return null;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getGateway(java.lang.String)
     */
    @Override
    public GatewayBean getGateway(String id) throws StorageException {
        Map source = getEntity("gateway", id); //$NON-NLS-1$
        return EsMarshalling.unmarshallGateway(source);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getDownload(java.lang.String)
     */
    @Override
    public DownloadBean getDownload(String id) throws StorageException {
        Map source = getEntity("download", id); //$NON-NLS-1$
        return EsMarshalling.unmarshallDownload(source);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getPlugin(long)
     */
    @Override
    public PluginBean getPlugin(long id) throws StorageException {
        Map source = getEntity("plugin", String.valueOf(id)); //$NON-NLS-1$
        return EsMarshalling.unmarshallPlugin(source);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getPlugin(java.lang.String, java.lang.String)
     */
    @Override
    public PluginBean getPlugin(String groupId, String artifactId) throws StorageException {
        try {
            @SuppressWarnings("nls")
            QueryBuilder qb = QueryBuilders.filteredQuery(
                    QueryBuilders.matchAllQuery(),
                    FilterBuilders.andFilter(
                            FilterBuilders.termFilter("groupId", groupId),
                            FilterBuilders.termFilter("artifactId", artifactId)
                    )
                );
            SearchSourceBuilder builder = new SearchSourceBuilder().query(qb).size(2);
            List,Void>> hits = listEntities("plugin", builder); //$NON-NLS-1$
            if (hits.size() == 1) {
                Hit,Void> hit = hits.iterator().next();
                return EsMarshalling.unmarshallPlugin(hit.source);
            }
            return null;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getPolicyDefinition(java.lang.String)
     */
    @Override
    public PolicyDefinitionBean getPolicyDefinition(String id) throws StorageException {
        Map source = getEntity("policyDef", id); //$NON-NLS-1$
        return EsMarshalling.unmarshallPolicyDefinition(source);

    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getRole(java.lang.String)
     */
    @Override
    public RoleBean getRole(String id) throws StorageException {
        Map source = getEntity("role", id); //$NON-NLS-1$
        return EsMarshalling.unmarshallRole(source);
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#listPlugins()
     */
    @Override
    public List listPlugins() throws StorageException {
        @SuppressWarnings("nls")
        String[] fields = {"id", "artifactId", "groupId", "version", "classifier", "type", "name",
            "description", "createdBy", "createdOn"};

        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.orFilter(
                    FilterBuilders.missingFilter("deleted"),
                    FilterBuilders.termFilter("deleted", false))
        );
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .fetchSource(fields, null).query(query).sort("name.raw", SortOrder.ASC).size(200); //$NON-NLS-1$
        List,Void>> hits = listEntities("plugin", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            PluginSummaryBean bean = EsMarshalling.unmarshallPluginSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#listGateways()
     */
    @Override
    public List listGateways() throws StorageException {
        @SuppressWarnings("nls")
        String[] fields = {"id", "name", "description","type"};
        SearchSourceBuilder builder = new SearchSourceBuilder().fetchSource(fields, null).sort("name.raw", SortOrder.ASC).size(100); //$NON-NLS-1$
        List,Void>> hits = listEntities("gateway", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            GatewaySummaryBean bean = EsMarshalling.unmarshallGatewaySummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#findOrganizations(io.apiman.manager.api.beans.search.SearchCriteriaBean)
     */
    @Override
    public SearchResultsBean findOrganizations(SearchCriteriaBean criteria)
            throws StorageException {
        return find(criteria, "organization", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public OrganizationSummaryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallOrganizationSummary(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#findClients(io.apiman.manager.api.beans.search.SearchCriteriaBean)
     */
    @Override
    public SearchResultsBean findClients(SearchCriteriaBean criteria)
            throws StorageException {
        return find(criteria, "client", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ClientSummaryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallClientSummary(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#findApis(io.apiman.manager.api.beans.search.SearchCriteriaBean)
     */
    @Override
    public SearchResultsBean findApis(SearchCriteriaBean criteria)
            throws StorageException {
        return find(criteria, "api", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ApiSummaryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallApiSummary(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#findPlans(java.lang.String, io.apiman.manager.api.beans.search.SearchCriteriaBean)
     */
    @Override
    public SearchResultsBean findPlans(String organizationId, SearchCriteriaBean criteria)
            throws StorageException {
        criteria.addFilter("organizationId", organizationId, SearchCriteriaFilterOperator.eq); //$NON-NLS-1$
        return find(criteria, "plan", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public PlanSummaryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallPlanSummary(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#auditEntity(java.lang.String, java.lang.String, java.lang.String, java.lang.Class, io.apiman.manager.api.beans.search.PagingBean)
     */
    @Override
    public  SearchResultsBean auditEntity(String organizationId, String entityId,
            String entityVersion, Class type, PagingBean paging) throws StorageException {
        SearchCriteriaBean criteria = new SearchCriteriaBean();
        if (paging != null) {
            criteria.setPaging(paging);
        } else {
            criteria.setPage(1);
            criteria.setPageSize(20);
        }
        criteria.setOrder("createdOn", false); //$NON-NLS-1$
        if (organizationId != null) {
            criteria.addFilter("organizationId", organizationId, SearchCriteriaFilterOperator.eq); //$NON-NLS-1$
        }
        if (entityId != null) {
            criteria.addFilter("entityId", entityId, SearchCriteriaFilterOperator.eq); //$NON-NLS-1$
        }
        if (entityVersion != null) {
            criteria.addFilter("entityVersion", entityVersion, SearchCriteriaFilterOperator.eq); //$NON-NLS-1$
        }
        if (type != null) {
            AuditEntityType entityType = null;
            if (type == OrganizationBean.class) {
                entityType = AuditEntityType.Organization;
            } else if (type == ClientBean.class) {
                entityType = AuditEntityType.Client;
            } else if (type == ApiBean.class) {
                entityType = AuditEntityType.Api;
            } else if (type == PlanBean.class) {
                entityType = AuditEntityType.Plan;
            }
            if (entityType != null) {
                criteria.addFilter("entityType", entityType.name(), SearchCriteriaFilterOperator.eq); //$NON-NLS-1$
            }
        }

        return find(criteria, "auditEntry", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public AuditEntryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallAuditEntry(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#auditUser(java.lang.String, io.apiman.manager.api.beans.search.PagingBean)
     */
    @Override
    public  SearchResultsBean auditUser(String userId, PagingBean paging)
            throws StorageException {
        SearchCriteriaBean criteria = new SearchCriteriaBean();
        if (paging != null) {
            criteria.setPaging(paging);
        } else {
            criteria.setPage(1);
            criteria.setPageSize(20);
        }
        criteria.setOrder("createdOn", false); //$NON-NLS-1$
        if (userId != null) {
            criteria.addFilter("who", userId, SearchCriteriaFilterOperator.eq); //$NON-NLS-1$
        }

        return find(criteria, "auditEntry", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public AuditEntryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallAuditEntry(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getOrgs(java.util.Set)
     */
    @Override
    public List getOrgs(Set organizationIds) throws StorageException {
        List orgs = new ArrayList<>();
        if (organizationIds == null || organizationIds.isEmpty()) {
            return orgs;
        }
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.termsFilter("id", organizationIds.toArray(new String[organizationIds.size()]))
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("name.raw", SortOrder.ASC)
                .query(query)
                .size(500);
        List,Void>> hits = listEntities("organization", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            OrganizationSummaryBean bean = EsMarshalling.unmarshallOrganizationSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getClientsInOrgs(java.util.Set)
     */
    @Override
    public List getClientsInOrgs(Set organizationIds) throws StorageException {
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("organizationName.raw", SortOrder.ASC)
                .sort("name.raw", SortOrder.ASC)
                .size(500);
        TermsQueryBuilder query = QueryBuilders.termsQuery("organizationId", organizationIds.toArray(new String[organizationIds.size()])); //$NON-NLS-1$
        builder.query(query);
        List,Void>> hits = listEntities("client", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            ClientSummaryBean bean = EsMarshalling.unmarshallClientSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getClientsInOrg(java.lang.String)
     */
    @Override
    public List getClientsInOrg(String organizationId) throws StorageException {
        Set orgs = new HashSet<>();
        orgs.add(organizationId);
        return getClientsInOrgs(orgs);
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getClientVersions(java.lang.String, java.lang.String)
     */
    @Override
    public List getClientVersions(String organizationId,
            String clientId) throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.andFilter(
                    FilterBuilders.termFilter("organizationId", organizationId),
                    FilterBuilders.termFilter("clientId", clientId))
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("createdOn", SortOrder.DESC)
                .query(query)
                .size(500);
        List,Void>> hits = listEntities("clientVersion", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            ClientVersionSummaryBean bean = EsMarshalling.unmarshallClientVersionSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getClientContracts(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public List getClientContracts(String organizationId, String clientId,
            String version) throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.andFilter(
                FilterBuilders.termFilter("clientOrganizationId", organizationId),
                FilterBuilders.termFilter("clientId", clientId),
                FilterBuilders.termFilter("clientVersion", version)
            )
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder().sort("apiOrganizationId", SortOrder.ASC)
                .sort("apiId", SortOrder.ASC).query(query).size(500);
        List,Void>> hits = listEntities("contract", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            ContractSummaryBean bean = EsMarshalling.unmarshallContractSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getApiRegistry(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public ApiRegistryBean getApiRegistry(String organizationId, String clientId, String version)
            throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.andFilter(
                FilterBuilders.termFilter("clientOrganizationId", organizationId),
                FilterBuilders.termFilter("clientId", clientId),
                FilterBuilders.termFilter("clientVersion", version)
            )
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder().sort("id", SortOrder.ASC).query(query)
                .size(500);
        List,Void>> hits = listEntities("contract", builder); //$NON-NLS-1$
        ApiRegistryBean registry = new ApiRegistryBean();
        for (Hit,Void> hit : hits) {
            ApiEntryBean bean = EsMarshalling.unmarshallApiEntry(hit.source);
            ApiVersionBean svb = getApiVersion(bean.getApiOrgId(), bean.getApiId(), bean.getApiVersion());
            Set gateways = svb.getGateways();
            if (gateways != null && !gateways.isEmpty()) {
                ApiGatewayBean sgb = gateways.iterator().next();
                bean.setGatewayId(sgb.getGatewayId());
            }
            registry.getApis().add(bean);
        }
        return registry;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getApisInOrgs(java.util.Set)
     */
    @Override
    public List getApisInOrgs(Set organizationIds) throws StorageException {
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("organizationName.raw", SortOrder.ASC)
                .sort("name.raw", SortOrder.ASC)
                .size(500);
        TermsQueryBuilder query = QueryBuilders.termsQuery("organizationId", organizationIds.toArray(new String[organizationIds.size()])); //$NON-NLS-1$
        builder.query(query);

        List,Void>> hits = listEntities("api", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            ApiSummaryBean bean = EsMarshalling.unmarshallApiSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getApisInOrg(java.lang.String)
     */
    @Override
    public List getApisInOrg(String organizationId) throws StorageException {
        Set orgs = new HashSet<>();
        orgs.add(organizationId);
        return getApisInOrgs(orgs);
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getApiVersions(java.lang.String, java.lang.String)
     */
    @Override
    public List getApiVersions(String organizationId, String apiId)
            throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.andFilter(
                    FilterBuilders.termFilter("organizationId", organizationId),
                    FilterBuilders.termFilter("apiId", apiId))
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("createdOn", SortOrder.DESC)
                .query(query)
                .size(500);
        List,Void>> hits = listEntities("apiVersion", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            ApiVersionSummaryBean bean = EsMarshalling.unmarshallApiVersionSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getApiVersionPlans(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public List getApiVersionPlans(String organizationId, String apiId,
            String version) throws StorageException {
        List rval = new ArrayList<>();
        ApiVersionBean versionBean = getApiVersion(organizationId, apiId, version);
        if (versionBean != null) {
            Set plans = versionBean.getPlans();
            if (plans != null) {
                for (ApiPlanBean spb : plans) {
                    PlanBean planBean = getPlan(organizationId, spb.getPlanId());
                    ApiPlanSummaryBean plan = new ApiPlanSummaryBean();
                    plan.setPlanId(spb.getPlanId());
                    plan.setVersion(spb.getVersion());
                    plan.setPlanName(planBean.getName());
                    plan.setPlanDescription(planBean.getDescription());
                    rval.add(plan);
                }
            }
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getPlansInOrgs(java.util.Set)
     */
    @Override
    public List getPlansInOrgs(Set organizationIds) throws StorageException {
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("organizationName.raw", SortOrder.ASC)
                .sort("name.raw", SortOrder.ASC)
                .size(500);
        TermsQueryBuilder query = QueryBuilders.termsQuery("organizationId", organizationIds.toArray(new String[organizationIds.size()])); //$NON-NLS-1$
        builder.query(query);
        List,Void>> hits = listEntities("plan", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            PlanSummaryBean bean = EsMarshalling.unmarshallPlanSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getPlansInOrg(java.lang.String)
     */
    @Override
    public List getPlansInOrg(String organizationId) throws StorageException {
        Set orgs = new HashSet<>();
        orgs.add(organizationId);
        return getPlansInOrgs(orgs);
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getPlanVersions(java.lang.String, java.lang.String)
     */
    @Override
    public List getPlanVersions(String organizationId, String planId)
            throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.andFilter(
                    FilterBuilders.termFilter("organizationId", organizationId),
                    FilterBuilders.termFilter("planId", planId))
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .sort("createdOn", SortOrder.DESC)
                .query(query)
                .size(500);
        List,Void>> hits = listEntities("planVersion", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            PlanVersionSummaryBean bean = EsMarshalling.unmarshallPlanVersionSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getPolicies(java.lang.String, java.lang.String, java.lang.String, io.apiman.manager.api.beans.policies.PolicyType)
     */
    @Override
    public List getPolicies(String organizationId, String entityId, String version,
            PolicyType type) throws StorageException {
        try {
            String docType = getPoliciesDocType(type);
            String pid = id(organizationId, entityId, version);
            List rval = new ArrayList<>();
            Map source = getEntity(docType, pid);
            if (source == null) {
                return rval;
            }
            PoliciesBean policies = EsMarshalling.unmarshallPolicies(source);
            if (policies == null) return rval;
            List policyBeans = policies.getPolicies();
            if (policyBeans != null) {
                for (PolicyBean policyBean : policyBeans) {
                    PolicyDefinitionBean def = getPolicyDefinition(policyBean.getDefinition().getId());
                    policyBean.setDefinition(def);
                    PolicyTemplateUtil.generatePolicyDescription(policyBean);
                    PolicySummaryBean psb = new PolicySummaryBean();
                    psb.setCreatedBy(policyBean.getCreatedBy());
                    psb.setCreatedOn(policyBean.getCreatedOn());
                    psb.setDescription(policyBean.getDescription());
                    psb.setIcon(def.getIcon());
                    psb.setId(policyBean.getId());
                    psb.setName(policyBean.getName());
                    psb.setPolicyDefinitionId(def.getId());
                    rval.add(psb);
                }
            }
            return rval;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#listPolicyDefinitions()
     */
    @Override
    public List listPolicyDefinitions() throws StorageException {
        @SuppressWarnings("nls")
        String[] fields = {"id", "policyImpl", "name", "description", "icon", "pluginId", "formType"};
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.orFilter(
                    FilterBuilders.missingFilter("deleted"),
                    FilterBuilders.termFilter("deleted", false))
        );
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .fetchSource(fields, null)
                .query(query)
                .sort("name.raw", SortOrder.ASC).size(100); //$NON-NLS-1$
        List,Void>> hits = listEntities("policyDef", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            PolicyDefinitionSummaryBean bean = EsMarshalling.unmarshallPolicyDefinitionSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getContracts(java.lang.String, java.lang.String, java.lang.String, int, int)
     */
    @Override
    public List getContracts(String organizationId, String apiId,
            String version, int page, int pageSize) throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder query = QueryBuilders.filteredQuery(
            QueryBuilders.matchAllQuery(),
            FilterBuilders.andFilter(
                FilterBuilders.termFilter("apiOrganizationId", organizationId),
                FilterBuilders.termFilter("apiId", apiId),
                FilterBuilders.termFilter("apiVersion", version)
            )
        );
        @SuppressWarnings("nls")
        SearchSourceBuilder builder = new SearchSourceBuilder().sort("clientOrganizationId", SortOrder.ASC)
                .sort("clientId", SortOrder.ASC).query(query).size(500);
        List,Void>> hits = listEntities("contract", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            ContractSummaryBean bean = EsMarshalling.unmarshallContractSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getMaxPolicyOrderIndex(java.lang.String, java.lang.String, java.lang.String, io.apiman.manager.api.beans.policies.PolicyType)
     */
    @Override
    public int getMaxPolicyOrderIndex(String organizationId, String entityId, String entityVersion,
            PolicyType type) throws StorageException {
        // We'll figure this out later, when adding a policy.
        return -1;
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#listPluginPolicyDefs(java.lang.Long)
     */
    @Override
    public List listPluginPolicyDefs(Long pluginId) throws StorageException {
        @SuppressWarnings("nls")
        QueryBuilder qb = QueryBuilders.filteredQuery(
                QueryBuilders.matchAllQuery(),
                FilterBuilders.termFilter("pluginId", pluginId)
            );
        @SuppressWarnings("nls")
        String[] fields = {"id", "policyImpl", "name", "description", "icon", "pluginId", "formType"};
        SearchSourceBuilder builder = new SearchSourceBuilder()
                .fetchSource(fields, null)
                .query(qb)
                .sort("name.raw", SortOrder.ASC).size(100); //$NON-NLS-1$
        List,Void>> hits = listEntities("policyDef", builder); //$NON-NLS-1$
        List rval = new ArrayList<>(hits.size());
        for (Hit,Void> hit : hits) {
            PolicyDefinitionSummaryBean bean = EsMarshalling.unmarshallPolicyDefinitionSummary(hit.source);
            rval.add(bean);
        }
        return rval;
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createUser(io.apiman.manager.api.beans.idm.UserBean)
     */
    @Override
    public void createUser(UserBean user) throws StorageException {
        indexEntity("user", user.getUsername(), EsMarshalling.marshall(user)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getUser(java.lang.String)
     */
    @Override
    public UserBean getUser(String userId) throws StorageException {
        Map source = getEntity("user", userId); //$NON-NLS-1$
        return EsMarshalling.unmarshallUser(source);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#updateUser(io.apiman.manager.api.beans.idm.UserBean)
     */
    @Override
    public void updateUser(UserBean user) throws StorageException {
        updateEntity("user", user.getUsername(), EsMarshalling.marshall(user)); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#findUsers(io.apiman.manager.api.beans.search.SearchCriteriaBean)
     */
    @Override
    public SearchResultsBean findUsers(SearchCriteriaBean criteria) throws StorageException {
        return find(criteria, "user",  new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public UserBean unmarshal(Map source) {
                return EsMarshalling.unmarshallUser(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#findRoles(io.apiman.manager.api.beans.search.SearchCriteriaBean)
     */
    @Override
    public SearchResultsBean findRoles(SearchCriteriaBean criteria) throws StorageException {
        return find(criteria, "role", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public RoleBean unmarshal(Map source) {
                return EsMarshalling.unmarshallRole(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#createMembership(io.apiman.manager.api.beans.idm.RoleMembershipBean)
     */
    @Override
    public void createMembership(RoleMembershipBean membership) throws StorageException {
        membership.setId(generateGuid());
        String id = id(membership.getOrganizationId(), membership.getUserId(), membership.getRoleId());
        indexEntity("roleMembership", id, EsMarshalling.marshall(membership), true); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getMembership(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public RoleMembershipBean getMembership(String userId, String roleId, String organizationId) throws StorageException {
        String id = id(organizationId, userId, roleId);
        Map source = getEntity("roleMembership", id); //$NON-NLS-1$
        if (source == null) {
            return null;
        } else {
            return EsMarshalling.unmarshallRoleMembership(source);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteMembership(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public void deleteMembership(String userId, String roleId, String organizationId) throws StorageException {
        String id = id(organizationId, userId, roleId);
        deleteEntity("roleMembership", id); //$NON-NLS-1$
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#deleteMemberships(java.lang.String, java.lang.String)
     */
    @Override
    @SuppressWarnings("nls")
    public void deleteMemberships(String userId, String organizationId) throws StorageException {
        FilteredQueryBuilder query = QueryBuilders.filteredQuery(
                QueryBuilders.matchAllQuery(),
                FilterBuilders.andFilter(
                        FilterBuilders.termFilter("organizationId", organizationId),
                        FilterBuilders.termFilter("userId", userId))
        );
        try {
            String string = query.string();
            // Workaround for bug in FilteredQueryBuilder which does not (yet) wrap
            // the JSON in a query element
            if (string.indexOf("query") < 0 || string.indexOf("query") > 7) {
                string = "{ \"query\" : " + string + "}";
            }
            DeleteByQuery deleteByQuery = new DeleteByQuery.Builder(string).addIndex(getIndexName())
                    .addType("roleMembership").build();
            JestResult response = esClient.execute(deleteByQuery);
            if (!response.isSucceeded()) {
                throw new StorageException(response.getErrorMessage());
            }
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getUserMemberships(java.lang.String)
     */
    @Override
    public Set getUserMemberships(String userId) throws StorageException {
        try {
            @SuppressWarnings("nls")
            QueryBuilder qb = QueryBuilders.filteredQuery(
                QueryBuilders.matchAllQuery(),
                FilterBuilders.termFilter("userId", userId)
            );
            SearchSourceBuilder builder = new SearchSourceBuilder().query(qb).size(500);
            List,Void>> hits = listEntities("roleMembership", builder); //$NON-NLS-1$
            Set rval = new HashSet<>();
            for (Hit,Void> hit : hits) {
                RoleMembershipBean roleMembership = EsMarshalling.unmarshallRoleMembership(hit.source);
                rval.add(roleMembership);
            }
            return rval;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getUserMemberships(java.lang.String, java.lang.String)
     */
    @Override
    public Set getUserMemberships(String userId, String organizationId)
            throws StorageException {
        try {
            @SuppressWarnings("nls")
            QueryBuilder qb = QueryBuilders.filteredQuery(
                QueryBuilders.matchAllQuery(),
                FilterBuilders.andFilter(
                    FilterBuilders.termFilter("userId", userId),
                    FilterBuilders.termFilter("organizationId", organizationId)
                )
            );
            SearchSourceBuilder builder = new SearchSourceBuilder().query(qb).size(500);
            List,Void>> hits = listEntities("roleMembership", builder); //$NON-NLS-1$
            Set rval = new HashSet<>();
            for (Hit,Void> hit : hits) {
                RoleMembershipBean roleMembership = EsMarshalling.unmarshallRoleMembership(hit.source);
                rval.add(roleMembership);
            }
            return rval;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getOrgMemberships(java.lang.String)
     */
    @Override
    public Set getOrgMemberships(String organizationId) throws StorageException {
        try {
            @SuppressWarnings("nls")
            QueryBuilder qb = QueryBuilders.filteredQuery(
                QueryBuilders.matchAllQuery(),
                FilterBuilders.termFilter("organizationId", organizationId)
            );
            SearchSourceBuilder builder = new SearchSourceBuilder().query(qb).size(500);
            List,Void>> hits = listEntities("roleMembership", builder); //$NON-NLS-1$
            Set rval = new HashSet<>();
            for (Hit,Void> hit : hits) {
                RoleMembershipBean roleMembership = EsMarshalling.unmarshallRoleMembership(hit.source);
                rval.add(roleMembership);
            }
            return rval;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * @see io.apiman.manager.api.core.IStorageQuery#getPermissions(java.lang.String)
     */
    @Override
    public Set getPermissions(String userId) throws StorageException {
        try {
            @SuppressWarnings("nls")
            QueryBuilder qb = QueryBuilders.filteredQuery(
                    QueryBuilders.matchAllQuery(),
                    FilterBuilders.termFilter("userId", userId)
                );
            SearchSourceBuilder builder = new SearchSourceBuilder().query(qb).size(500);
            List,Void>> hits = listEntities("roleMembership", builder); //$NON-NLS-1$
            Set rval = new HashSet<>(hits.size());
            if (!hits.isEmpty()) {
                for (Hit,Void> hit : hits) {
                    Map source = hit.source;
                    String roleId = String.valueOf(source.get("roleId")); //$NON-NLS-1$
                    String qualifier = String.valueOf(source.get("organizationId")); //$NON-NLS-1$
                    RoleBean role = getRole(roleId);
                    if (role != null) {
                        for (PermissionType permission : role.getPermissions()) {
                            PermissionBean p = new PermissionBean();
                            p.setName(permission);
                            p.setOrganizationId(qualifier);
                            rval.add(p);
                        }
                    }
                }
            }
            return rval;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Indexes an entity.
     * @param type
     * @param id
     * @param sourceEntity
     * @throws StorageException
     */
    private void indexEntity(String type, String id, XContentBuilder sourceEntity) throws StorageException {
        indexEntity(type, id, sourceEntity, false);
    }

    /**
     * Indexes an entity.
     * @param type
     * @param id
     * @param sourceEntity
     * @param refresh true if the operation should wait for a refresh before it returns
     * @throws StorageException
     */
    @SuppressWarnings("nls")
    private void indexEntity(String type, String id, XContentBuilder sourceEntity, boolean refresh)
            throws StorageException {
        try {
            String json = sourceEntity.string();
            JestResult response = esClient.execute(new Index.Builder(json).refresh(refresh).index(getIndexName())
                    .setParameter(Parameters.OP_TYPE, "create").type(type).id(id).build());
            if (!response.isSucceeded()) {
                throw new StorageException("Failed to index document " + id + " of type " + type + ": " + response.getErrorMessage());
            }
        } catch (StorageException e) {
            throw e;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Gets an entity.  Callers must unmarshal the resulting map.
     * @param type
     * @param id
     * @throws StorageException
     */
    private Map getEntity(String type, String id) throws StorageException {
        try {
            JestResult response = esClient.execute(new Get.Builder(getIndexName(), id).type(type).build());
            if (!response.isSucceeded()) {
                return null;
            }
            return response.getSourceAsObject(Map.class);
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Returns a list of entities.
     * @param type
     * @param searchSourceBuilder
     * @throws StorageException
     */
    private List, Void>> listEntities(String type,
            SearchSourceBuilder searchSourceBuilder) throws StorageException {
        try {
            String query = searchSourceBuilder.string();
            Search search = new Search.Builder(query).addIndex(getIndexName()).addType(type).build();
            SearchResult response = esClient.execute(search);
            @SuppressWarnings({ "rawtypes", "unchecked" })
            List, Void>> thehits = (List) response.getHits(Map.class);
            return thehits;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Deletes an entity.
     * @param type
     * @param id
     * @throws StorageException
     */
    private void deleteEntity(String type, String id) throws StorageException {
        try {
            JestResult response = esClient.execute(new Delete.Builder(id).index(getIndexName()).type(type).build());
            if (!response.isSucceeded()) {
                throw new StorageException("Document could not be deleted because it did not exist:" + response.getErrorMessage()); //$NON-NLS-1$
            }
        } catch (StorageException e) {
            throw e;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Updates a single entity.
     * @param type
     * @param id
     * @param source
     * @throws StorageException
     */
    private void updateEntity(String type, String id, XContentBuilder source) throws StorageException {
        try {
            String doc = source.string();
            /* JestResult response = */esClient.execute(new Index.Builder(doc)
                    .setParameter(Parameters.OP_TYPE, "index").index(getIndexName()).type(type).id(id).build()); //$NON-NLS-1$
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Finds entities using a generic search criteria bean.
     * @param criteria
     * @param type
     * @param unmarshaller
     * @throws StorageException
     */
    private  SearchResultsBean find(SearchCriteriaBean criteria, String type,
            IUnmarshaller unmarshaller) throws StorageException {
        try {
            SearchResultsBean rval = new SearchResultsBean<>();

            // Set some default in the case that paging information was not included in the request.
            PagingBean paging = criteria.getPaging();
            if (paging == null) {
                paging = new PagingBean();
                paging.setPage(1);
                paging.setPageSize(20);
            }
            int page = paging.getPage();
            int pageSize = paging.getPageSize();
            int start = (page - 1) * pageSize;

            SearchSourceBuilder builder = new SearchSourceBuilder().size(pageSize).from(start).fetchSource(true);

            // Sort order
            OrderByBean orderBy = criteria.getOrderBy();
            if (orderBy != null) {
                String name = orderBy.getName();
                if (name.equals("name") || name.equals("fullName")) { //$NON-NLS-1$ //$NON-NLS-2$
                    name += ".raw"; //$NON-NLS-1$
                }
                if (orderBy.isAscending()) {
                    builder.sort(name, SortOrder.ASC);
                } else {
                    builder.sort(name, SortOrder.DESC);
                }
            }

            // Now process the filter criteria
            List filters = criteria.getFilters();
            QueryBuilder q = QueryBuilders.matchAllQuery();
            if (filters != null && !filters.isEmpty()) {

                AndFilterBuilder andFilter = FilterBuilders.andFilter();
                int filterCount = 0;
                for (SearchCriteriaFilterBean filter : filters) {
                    String propertyName = filter.getName();
                    if (filter.getOperator() == SearchCriteriaFilterOperator.eq) {
                        andFilter.add(FilterBuilders.termFilter(propertyName, filter.getValue()));
                        filterCount++;
                    } else if (filter.getOperator() == SearchCriteriaFilterOperator.like) {
                        q = QueryBuilders.wildcardQuery(propertyName, filter.getValue().toLowerCase().replace('%', '*'));
                    } else if (filter.getOperator() == SearchCriteriaFilterOperator.bool_eq) {
                        andFilter.add(FilterBuilders.termFilter(propertyName, "true".equals(filter.getValue()))); //$NON-NLS-1$
                        filterCount++;
                    }
                    // TODO implement the other filter operators here!
                }

                if (filterCount > 0) {
                    q = QueryBuilders.filteredQuery(q, andFilter);
                }
            }
            builder.query(q);


            String query = builder.string();
            Search search = new Search.Builder(query).addIndex(getIndexName())
                    .addType(type).build();
            SearchResult response = esClient.execute(search);
            @SuppressWarnings({ "unchecked", "rawtypes" })
            List, Void>> thehits = (List) response.getHits(Map.class);

            rval.setTotalSize(response.getTotal());
            for (Hit,Void> hit : thehits) {
                Map sourceAsMap = hit.source;
                T bean = unmarshaller.unmarshal(sourceAsMap);
                rval.getBeans().add(bean);
            }
            return rval;
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    /**
     * Generates a (hopefully) unique ID.  Mimics JPA's auto-generated long ID column.
     */
    private static synchronized Long generateGuid() {
        StringBuilder builder = new StringBuilder();
        builder.append(System.currentTimeMillis());
        builder.append(guidCounter++);
        // Reset the counter if it gets too high.  It's always a number
        // between 100 and 999 so that the # of digits in the guid is
        // always the same.
        if (guidCounter > 999) {
            guidCounter = 100;
        }
        return Long.parseLong(builder.toString());
    }

    /**
     * Returns the policies document type to use given the policy type.
     * @param type
     */
    private static String getPoliciesDocType(PolicyType type) {
        String docType = "planPolicies"; //$NON-NLS-1$
        if (type == PolicyType.Api) {
            docType = "apiPolicies"; //$NON-NLS-1$
        } else if (type == PolicyType.Client) {
            docType = "clientPolicies"; //$NON-NLS-1$
        }
        return docType;
    }

    /**
     * A composite ID created from an organization ID and entity ID.
     * @param organizationId
     * @param entityId
     */
    private static String id(String organizationId, String entityId) {
        return organizationId + ":" + entityId; //$NON-NLS-1$
    }

    /**
     * A composite ID created from an organization ID, entity ID, and version.
     * @param organizationId
     * @param entityId
     * @param version
     */
    private static String id(String organizationId, String entityId, String version) {
        return organizationId + ':' + entityId + ':' + version;
    }

    @Override
    public Iterator getAllOrganizations() throws StorageException {
        return getAll("organization", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public OrganizationBean unmarshal(Map source) {
                return EsMarshalling.unmarshallOrganization(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllPlans(java.lang.String)
     */
    @Override
    public Iterator getAllPlans(String organizationId) throws StorageException {
        return getAll("plan", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public PlanBean unmarshal(Map source) {
                return EsMarshalling.unmarshallPlan(source);
            }
        }, matchOrgQuery(organizationId));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllClients(java.lang.String)
     */
    @Override
    public Iterator getAllClients(String organizationId) throws StorageException {
        return getAll("client", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ClientBean unmarshal(Map source) {
                return EsMarshalling.unmarshallClient(source);
            }
        }, matchOrgQuery(organizationId));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllApis(java.lang.String)
     */
    @Override
    public Iterator getAllApis(String organizationId) throws StorageException {
        return getAll("api", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ApiBean unmarshal(Map source) {
                return EsMarshalling.unmarshallApi(source);
            }
        }, matchOrgQuery(organizationId));
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllPlanVersions(java.lang.String, java.lang.String)
     */
    @SuppressWarnings("nls")
    @Override
    public Iterator getAllPlanVersions(String organizationId, String planId)
            throws StorageException {
        String query = "{" +
                "  \"query\": {" +
                "    \"filtered\": { " +
                "      \"filter\": {" +
                "        \"and\" : [" +
                "          {" +
                "            \"term\": { \"organizationId\": \"" + organizationId + "\" }" +
                "          }," +
                "          {" +
                "            \"term\": { \"planId\": \"" + planId + "\" }" +
                "          }" +
                "      ]" +
                "      }" +
                "    }" +
                "  }" +
                "}";
        return getAll("planVersion", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public PlanVersionBean unmarshal(Map source) {
                return EsMarshalling.unmarshallPlanVersion(source);
            }
        }, query);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllApiVersions(java.lang.String, java.lang.String)
     */
    @SuppressWarnings("nls")
    @Override
    public Iterator getAllApiVersions(String organizationId, String apiId)
            throws StorageException {
        String query = "{" +
                "  \"query\": {" +
                "    \"filtered\": { " +
                "      \"filter\": {" +
                "        \"and\" : [" +
                "          {" +
                "            \"term\": { \"organizationId\": \"" + organizationId + "\" }" +
                "          }," +
                "          {" +
                "            \"term\": { \"apiId\": \"" + apiId + "\" }" +
                "          }" +
                "      ]" +
                "      }" +
                "    }" +
                "  }" +
                "}";
        return getAll("apiVersion", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ApiVersionBean unmarshal(Map source) {
                return EsMarshalling.unmarshallApiVersion(source);
            }
        }, query);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllClientVersions(java.lang.String, java.lang.String)
     */
    @SuppressWarnings("nls")
    @Override
    public Iterator getAllClientVersions(String organizationId,
            String clientId) throws StorageException {
        String query = "{" +
                "  \"query\": {" +
                "    \"filtered\": { " +
                "      \"filter\": {" +
                "        \"and\" : [" +
                "          {" +
                "            \"term\": { \"organizationId\": \"" + organizationId + "\" }" +
                "          }," +
                "          {" +
                "            \"term\": { \"clientId\": \"" + clientId + "\" }" +
                "          }" +
                "      ]" +
                "      }" +
                "    }" +
                "  }" +
                "}";
        return getAll("clientVersion", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ClientVersionBean unmarshal(Map source) {
                return EsMarshalling.unmarshallClientVersion(source);
            }
        }, query);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllContracts(java.lang.String, java.lang.String, java.lang.String)
     */
    @SuppressWarnings("nls")
    @Override
    public Iterator getAllContracts(String organizationId, String clientId, String version)
            throws StorageException {
        String query = "{" +
                "  \"query\": {" +
                "    \"filtered\": {" +
                "      \"filter\": {" +
                "        \"and\" : [" +
                "          {" +
                "            \"term\": { \"clientOrganizationId\": \"" + organizationId + "\" }" +
                "          }," +
                "          {" +
                "            \"term\": { \"clientId\": \"" + clientId + "\" }" +
                "          }," +
                "          {" +
                "            \"term\": { \"clientVersion\": \"" + version + "\" }" +
                "          }" +
                "      ]" +
                "      }" +
                "    }" +
                "  }" +
                "}";
        return getAll("contract", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public ContractBean unmarshal(Map source) {
                ContractBean contract = EsMarshalling.unmarshallContract(source);
                String apiOrgId = (String) source.get("apiOrganizationId");
                String apiId = (String) source.get("apiId");
                String apiVersion = (String) source.get("apiVersion");
                String planId = (String) source.get("planId");
                String planVersion = (String) source.get("planVersion");

                ApiVersionBean svb = new ApiVersionBean();
                svb.setVersion(apiVersion);
                svb.setApi(new ApiBean());
                svb.getApi().setOrganization(new OrganizationBean());
                svb.getApi().setId(apiId);
                svb.getApi().getOrganization().setId(apiOrgId);

                PlanVersionBean pvb = new PlanVersionBean();
                pvb.setVersion(planVersion);
                pvb.setPlan(new PlanBean());
                pvb.getPlan().setOrganization(new OrganizationBean());
                pvb.getPlan().setId(planId);
                pvb.getPlan().getOrganization().setId(apiOrgId);

                contract.setPlan(pvb);
                contract.setApi(svb);
                return contract;
            }
        }, query);
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllPolicies(java.lang.String, java.lang.String, java.lang.String, io.apiman.manager.api.beans.policies.PolicyType)
     */
    @Override
    public Iterator getAllPolicies(String organizationId, String entityId, String version,
            PolicyType type) throws StorageException {
        try {
            String docType = getPoliciesDocType(type);
            String pid = id(organizationId, entityId, version);
            Map source = getEntity(docType, pid);
            PoliciesBean policies = EsMarshalling.unmarshallPolicies(source);
            if (policies == null || policies.getPolicies() == null) {
                return new ArrayList().iterator();
            }
            List policyBeans = policies.getPolicies();
            // TODO resolve the policy def, since we know we'll only have the definition ID here
            for (PolicyBean policyBean : policyBeans) {
                PolicyDefinitionBean def = getPolicyDefinition(policyBean.getDefinition().getId());
                if (def != null) {
                    policyBean.setDefinition(def);
                }
            }
            return policyBeans.iterator();
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    @Override
    public Iterator getAllGateways() throws StorageException {
        return getAll("gateway", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public GatewayBean unmarshal(Map source) {
                return EsMarshalling.unmarshallGateway(source);
            }
        });
    }

    @Override
    public Iterator getAllUsers() throws StorageException {
        return getAll("user", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public UserBean unmarshal(Map source) {
                return EsMarshalling.unmarshallUser(source);
            }
        });
    }

    @Override
    public Iterator getAllRoles() throws StorageException {
        return getAll("role", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public RoleBean unmarshal(Map source) {
                return EsMarshalling.unmarshallRole(source);
            }
        });
    }

    @Override
    public Iterator getAllContracts(OrganizationBean organizationBean, int lim) throws StorageException {
        return getAll("contract", EsMarshalling::unmarshallContract, matchOrgQuery(organizationBean.getId())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllClientVersions(OrganizationBean organizationBean, int lim) throws StorageException {
        return getAll("clientVersion", EsMarshalling::unmarshallClientVersion, matchOrgQuery(organizationBean.getId())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllClientVersions(OrganizationBean organizationBean, ClientStatus status, int lim) throws StorageException {
        return getAll("clientVersion", EsMarshalling::unmarshallClientVersion, matchOrgAndStatusQuery(organizationBean.getId(), status.name())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllApiVersions(OrganizationBean organizationBean, int lim) throws StorageException {
        return getAll("apiVersion", EsMarshalling::unmarshallApiVersion, matchOrgQuery(organizationBean.getId())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllApiVersions(OrganizationBean organizationBean, ApiStatus status, int lim) throws StorageException {
        return getAll("apiVersion", EsMarshalling::unmarshallApiVersion, matchOrgAndStatusQuery(organizationBean.getId(), status.name())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllPlanVersions(OrganizationBean organizationBean, int lim) throws StorageException {
        return getAll("planVersion", EsMarshalling::unmarshallPlanVersion, matchOrgQuery(organizationBean.getId())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllPlanVersions(OrganizationBean organizationBean, PlanStatus status, int lim) throws StorageException {
        return getAll("planVersion", EsMarshalling::unmarshallPlanVersion, matchOrgAndStatusQuery(organizationBean.getId(), status.name())); //$NON-NLS-1$
    }

    @Override
    public Iterator getAllMemberships(String organizationId) throws StorageException {
        return getAll("roleMembership", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public RoleMembershipBean unmarshal(Map source) {
                return EsMarshalling.unmarshallRoleMembership(source);
            }
        }, matchOrgQuery(organizationId));
    }

    @Override
    public Iterator getAllAuditEntries(String organizationId) throws StorageException {
        return getAll("auditEntry", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public AuditEntryBean unmarshal(Map source) {
                return EsMarshalling.unmarshallAuditEntry(source);
            }
        }, matchOrgQuery(organizationId));
    }

    @Override
    public Iterator getAllPlugins() throws StorageException {
        return getAll("plugin", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public PluginBean unmarshal(Map source) {
                return EsMarshalling.unmarshallPlugin(source);
            }
        });
    }

    /**
     * @see io.apiman.manager.api.core.IStorage#getAllPolicyDefinitions()
     */
    @Override
    public Iterator getAllPolicyDefinitions() throws StorageException {
        return getAll("policyDef", new IUnmarshaller() { //$NON-NLS-1$
            @Override
            public PolicyDefinitionBean unmarshal(Map source) {
                return EsMarshalling.unmarshallPolicyDefinition(source);
            }
        });
    }

    /**
     * Returns an iterator over all instances of the given entity type.
     * @param entityType
     * @param unmarshaller
     * @throws StorageException
     */
    private  Iterator getAll(String entityType, IUnmarshaller unmarshaller) throws StorageException {
        String query = matchAllQuery();
        return getAll(entityType, unmarshaller, query);
    }

    /**
     * Returns an iterator over all instances of the given entity type.
     * @param entityType
     * @param unmarshaller
     * @param query
     * @throws StorageException
     */
    private  Iterator getAll(String entityType, IUnmarshaller unmarshaller, String query) throws StorageException {
        return new EntityIterator<>(entityType, unmarshaller, query);
    }

    /**
     * A simple, internal unmarshaller interface.
     * @author [email protected]
     */
    private static interface IUnmarshaller {
        /**
         * Unmarshal the source map into an entity.
         * @param source the source map
         * @return the unmarshalled instance of 
         */
        public T unmarshal(Map source);
    }

    /**
     * Allows iterating over all entities of a given type.
     * @author [email protected]
     */
    @SuppressWarnings("nls")
    private class EntityIterator implements Iterator {

        private String query;
        private String entityType;
        private IUnmarshaller unmarshaller;
        private String scrollId = null;
        private List, Void>> hits;
        private int nextHitIdx;;

        /**
         * Constructor.
         * @param entityType the entity type
         * @param unmarshaller the unmarshaller
         * @param query the query
         * @throws StorageException when storage fails
         */
        public EntityIterator(String entityType, IUnmarshaller unmarshaller, String query) throws StorageException {
            this.entityType = entityType;
            this.unmarshaller = unmarshaller;
            this.query = query;
            initScroll();
            this.nextHitIdx = 0;
        }

        /**
         * @see java.util.Iterator#hasNext()
         */
        @Override
        public boolean hasNext() {
            if (hits == null || this.nextHitIdx >= hits.size()) {
                try {
                    fetch();
                } catch (StorageException e) {
                    throw new RuntimeException(e);
                }
                this.nextHitIdx = 0;
            }
            return !hits.isEmpty();
        }

        /**
         * @see java.util.Iterator#next()
         */
        @Override
        public T next() {
            Hit, Void> hit = hits.get(nextHitIdx++);
            return unmarshaller.unmarshal(hit.source);
        }

        /**
         * @see java.util.Iterator#remove()
         */
        @Override
        public void remove() {
            // Not implemented.
        }

        private void initScroll() throws StorageException {
            try {
                Search search = new Search.Builder(query).addIndex(getIndexName()).addType(entityType)
                        .setSearchType(SearchType.SCAN).setParameter(Parameters.SCROLL, "1m").build();
                SearchResult response = esClient.execute(search);
                scrollId = response.getJsonObject().get("_scroll_id").getAsString();
            } catch (IOException e) {
                throw new StorageException(e);
            }
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        private void fetch() throws StorageException {
            try {
                Builder builder = new SearchScroll.Builder(scrollId, "1m")
                        .setParameter(Parameters.SIZE, 1);
                SearchScroll scroll = new SearchScroll(builder) {
                    @Override
                    public JestResult createNewElasticSearchResult(String responseBody, int statusCode,
                            String reasonPhrase, Gson gson) {
                        return createNewElasticSearchResult(new SearchResult(gson), responseBody, statusCode, reasonPhrase, gson);
                    }
                };
                SearchResult response = (SearchResult) esClient.execute(scroll);
                this.hits = (List) response.getHits(Map.class);
            } catch (IOException e) {
                throw new StorageException(e);
            }
        }

    }

    @SuppressWarnings("nls")
    private String matchOrgAndStatusQuery(String organizationId, String status) {
        return  "{" +
                "  \"query\": {" +
                "    \"filtered\": { " +
                "      \"filter\": {" +
                "        \"and\" : [" +
                "          {" +
                "            \"term\": { \"organizationId\": \"" + organizationId + "\" }" +
                "          }," +
                "          {" +
                "            \"term\": { \"status\": \"" + status + "\" }" +
                "          }" +
                "      ]" +
                "      }" +
                "    }" +
                "  }" +
                "}";
    }

    /**
     * @return an ES query to match all documents
     */
    @SuppressWarnings("nls")
    private String matchAllQuery() {
        return "{" +
                "  \"query\": {" +
                "    \"match_all\": {}" +
                "  }" +
                "}";
    }

    @SuppressWarnings("nls")
    private String matchOrgQuery(String organizationId) {
        return "{" +
                "  \"query\": {" +
                "    \"filtered\": { " +
                "      \"filter\": {" +
                "        \"term\": { \"organizationId\": \"" + organizationId + "\" }" +
                "      }" +
                "    }" +
                "  }" +
                "}";
    }

    /**
     * @return the indexName
     */
    public String getIndexName() {
        return indexName;
    }

    /**
     * @param indexName the indexName to set
     */
    public void setIndexName(String indexName) {
        this.indexName = indexName;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy