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

io.gravitee.gateway.handlers.api.services.SubscriptionCacheService Maven / Gradle / Ivy

There is a newer version: 4.5.5
Show newest version
/*
 * Copyright © 2015 The Gravitee team (http://gravitee.io)
 *
 * 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.gravitee.gateway.handlers.api.services;

import static io.gravitee.gateway.reactive.api.policy.SecurityToken.TokenType.API_KEY;
import static io.gravitee.gateway.reactive.api.policy.SecurityToken.TokenType.CLIENT_ID;
import static io.gravitee.repository.management.model.Subscription.Status.ACCEPTED;

import io.gravitee.gateway.api.service.ApiKeyService;
import io.gravitee.gateway.api.service.Subscription;
import io.gravitee.gateway.api.service.SubscriptionService;
import io.gravitee.gateway.reactive.api.policy.SecurityToken;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
public class SubscriptionCacheService implements SubscriptionService {

    private final ApiKeyService apiKeyService;

    // Caches only contains active subscriptions
    private final Map cacheByApiClientId = new ConcurrentHashMap<>();
    private final Map cacheBySubscriptionId = new ConcurrentHashMap<>();
    private final Map> cacheByApiId = new ConcurrentHashMap<>();

    @Override
    public Optional getByApiAndSecurityToken(String api, SecurityToken securityToken, String plan) {
        if (securityToken.getTokenType().equals(API_KEY.name())) {
            return apiKeyService.getByApiAndKey(api, securityToken.getTokenValue()).flatMap(apiKey -> getById(apiKey.getSubscription()));
        } else if (securityToken.getTokenType().equals(CLIENT_ID.name())) {
            return getByApiAndClientIdAndPlan(api, securityToken.getTokenValue(), plan);
        } else {
            return Optional.empty();
        }
    }

    @Override
    public Optional getByApiAndClientIdAndPlan(String api, String clientId, String plan) {
        return Optional.ofNullable(cacheByApiClientId.get(buildClientIdCacheKey(api, clientId, plan)));
    }

    @Override
    public Optional getById(String subscriptionId) {
        return Optional.ofNullable(cacheBySubscriptionId.get(subscriptionId));
    }

    @Override
    public void register(final Subscription subscription) {
        if (ACCEPTED.name().equals(subscription.getStatus())) {
            if (subscription.getClientId() != null) {
                registerFromClientId(subscription);
            } else {
                registerFromId(subscription);
            }
        } else {
            unregister(subscription);
        }
    }

    private void registerFromClientId(final Subscription subscription) {
        final String idKey = subscription.getId();
        final String clientIdKey = buildClientIdCacheKey(subscription);
        // Index the subscription without plan id to allow search without plan criteria.
        final String clientIdKeyWithoutPlan = buildClientIdCacheKey(subscription.getApi(), subscription.getClientId(), null);

        Subscription cachedSubscription = cacheBySubscriptionId.get(idKey);

        // remove previous subscription client_id from cache if client_id has changed
        if (
            cachedSubscription != null &&
            cachedSubscription.getClientId() != null &&
            !cachedSubscription.getClientId().equals(subscription.getClientId())
        ) {
            unregisterFromClientId(cachedSubscription);
        }

        log.debug(
            "Load accepted subscription with client Id  [id: {}] [api: {}] [plan: {}] [application: {}]",
            subscription.getId(),
            subscription.getApi(),
            subscription.getPlan(),
            subscription.getApi()
        );
        // Update subscription
        cacheBySubscriptionId.put(idKey, subscription);
        addKeyForApi(subscription.getApi(), idKey);
        // Put new client_id
        cacheByApiClientId.put(clientIdKey, subscription);
        addKeyForApi(subscription.getApi(), clientIdKey);
        cacheByApiClientId.put(clientIdKeyWithoutPlan, subscription);
        addKeyForApi(subscription.getApi(), clientIdKeyWithoutPlan);
    }

    private void registerFromId(final Subscription subscription) {
        String cacheKey = subscription.getId();
        log.debug(
            "Load accepted subscription [id: {}] [api: {}] [plan: {}] [application: {}]",
            subscription.getId(),
            subscription.getApi(),
            subscription.getPlan(),
            subscription.getApi()
        );
        cacheBySubscriptionId.put(cacheKey, subscription);
        addKeyForApi(subscription.getApi(), cacheKey);
    }

    private void addKeyForApi(final String apiId, final String cacheKey) {
        Set subscriptionsByApi = cacheByApiId.get(apiId);
        if (subscriptionsByApi == null) {
            subscriptionsByApi = new HashSet<>();
        }
        subscriptionsByApi.add(cacheKey);
        cacheByApiId.put(apiId, subscriptionsByApi);
    }

    private void removeKeyForApi(final String apiId, final String cacheKey) {
        Set keysByApi = cacheByApiId.get(apiId);
        if (keysByApi != null && keysByApi.remove(cacheKey)) {
            if (keysByApi.isEmpty()) {
                cacheByApiId.remove(apiId);
            } else {
                cacheByApiId.put(apiId, keysByApi);
            }
        }
    }

    @Override
    public void unregister(final Subscription subscription) {
        log.debug(
            "Unload subscription [id: {}] [api: {}] [plan: {}] [application: {}]",
            subscription.getId(),
            subscription.getApi(),
            subscription.getPlan(),
            subscription.getApi()
        );
        final String idKey = subscription.getId();
        Subscription removeSubscription = cacheBySubscriptionId.remove(idKey);
        if (removeSubscription != null) {
            removeKeyForApi(subscription.getApi(), idKey);
            unregisterFromClientId(removeSubscription);
        }
        // In case new one has different client id than the one in cache
        unregisterFromClientId(subscription);
    }

    private void unregisterFromClientId(final Subscription subscription) {
        if (subscription.getClientId() != null) {
            final String clientIdKey = buildClientIdCacheKey(subscription);
            if (cacheByApiClientId.remove(clientIdKey) != null) {
                removeKeyForApi(subscription.getApi(), clientIdKey);
            }
            final String clientIdKeyWithoutPlan = buildClientIdCacheKey(subscription.getApi(), subscription.getClientId(), null);
            if (cacheByApiClientId.remove(clientIdKeyWithoutPlan) != null) {
                removeKeyForApi(subscription.getApi(), clientIdKeyWithoutPlan);
            }
        }
    }

    @Override
    public void unregisterByApiId(final String apiId) {
        log.debug("Unload all subscriptions by api [api_id: {}]", apiId);
        Set subscriptionsByApi = cacheByApiId.remove(apiId);
        if (subscriptionsByApi != null) {
            subscriptionsByApi.forEach(cacheKey -> {
                Subscription subscription = cacheBySubscriptionId.remove(cacheKey);
                if (subscription != null) {
                    log.debug(
                        "Unload subscription [id: {}] [api: {}] [plan: {}] [application: {}]",
                        subscription.getId(),
                        subscription.getApi(),
                        subscription.getPlan(),
                        subscription.getApi()
                    );
                }
                cacheByApiClientId.remove(cacheKey);
            });
        }
    }

    String buildClientIdCacheKey(Subscription subscription) {
        return buildClientIdCacheKey(subscription.getApi(), subscription.getClientId(), subscription.getPlan());
    }

    String buildClientIdCacheKey(String api, String clientId, String plan) {
        return String.format("%s.%s.%s", api, clientId, plan);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy