io.gravitee.gateway.services.sync.SyncManager.java Source code

Java tutorial

Introduction

Here is the source code for io.gravitee.gateway.services.sync.SyncManager.java

Source

/**
 * Copyright (C) 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.services.sync;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gravitee.definition.model.Path;
import io.gravitee.gateway.env.GatewayConfiguration;
import io.gravitee.gateway.handlers.api.definition.Api;
import io.gravitee.gateway.handlers.api.definition.Plan;
import io.gravitee.gateway.handlers.api.manager.ApiManager;
import io.gravitee.repository.exceptions.TechnicalException;
import io.gravitee.repository.management.api.ApiRepository;
import io.gravitee.repository.management.api.EventRepository;
import io.gravitee.repository.management.api.PlanRepository;
import io.gravitee.repository.management.api.search.EventCriteria;
import io.gravitee.repository.management.model.Event;
import io.gravitee.repository.management.model.EventType;
import io.gravitee.repository.management.model.LifecycleState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.text.Collator;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author David BRASSELY (david.brassely at graviteesource.com)
 * @author Titouan COMPIEGNE (titouan.compiegne at graviteesource.com)
 * @author GraviteeSource Team
 */
public class SyncManager {

    private final Logger logger = LoggerFactory.getLogger(SyncManager.class);

    @Autowired
    private ApiRepository apiRepository;

    @Autowired
    private PlanRepository planRepository;

    @Autowired
    private EventRepository eventRepository;

    @Autowired
    private ApiManager apiManager;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private GatewayConfiguration gatewayConfiguration;

    public void refresh() {
        logger.debug("Refreshing gateway state...");

        try {
            Set<io.gravitee.repository.management.model.Api> apis = apiRepository.findAll();

            // Determine deployed APIs store into events payload
            Set<Api> deployedApis = getDeployedApis(apis);

            Map<String, Api> apisMap = deployedApis.stream().filter(api -> api != null && hasMatchingTags(api))
                    .collect(Collectors.toMap(Api::getId, api -> api));

            // Determine APIs to undeploy
            Set<String> apiToRemove = apiManager.apis().stream()
                    .filter(api -> !apisMap.containsKey(api.getId()) || !hasMatchingTags(api)).map(Api::getId)
                    .collect(Collectors.toSet());

            apiToRemove.forEach(apiManager::undeploy);

            // Determine APIs to update
            apisMap.keySet().stream().filter(apiId -> apiManager.get(apiId) != null).forEach(apiId -> {
                // Get local cached API
                Api deployedApi = apiManager.get(apiId);

                // Get API from store
                Api remoteApi = apisMap.get(apiId);

                if (deployedApi.getDeployedAt().before(remoteApi.getDeployedAt())) {
                    apiManager.update(remoteApi);
                }
            });

            // Determine APIs to deploy
            apisMap.keySet().stream().filter(api -> apiManager.get(api) == null).forEach(api -> {
                Api newApi = apisMap.get(api);
                apiManager.deploy(newApi);
            });
        } catch (TechnicalException te) {
            logger.error("Unable to sync instance", te);
        }
    }

    private boolean hasMatchingTags(Api api) {
        final Optional<List<String>> optTagList = gatewayConfiguration.shardingTags();

        if (optTagList.isPresent()) {
            List<String> tagList = optTagList.get();
            if (api.getTags() != null) {
                final List<String> inclusionTags = tagList.stream().map(String::trim)
                        .filter(tag -> !tag.startsWith("!")).collect(Collectors.toList());

                final List<String> exclusionTags = tagList.stream().map(String::trim)
                        .filter(tag -> tag.startsWith("!")).map(tag -> tag.substring(1))
                        .collect(Collectors.toList());

                if (inclusionTags.stream().filter(exclusionTags::contains).count() > 0) {
                    throw new IllegalArgumentException("You must not configure a tag to be included and excluded");
                }

                final boolean hasMatchingTags = inclusionTags.stream()
                        .anyMatch(tag -> api.getTags().stream().anyMatch(apiTag -> {
                            final Collator collator = Collator.getInstance();
                            collator.setStrength(Collator.NO_DECOMPOSITION);
                            return collator.compare(tag, apiTag) == 0;
                        })) || (!exclusionTags.isEmpty() && exclusionTags.stream()
                                .noneMatch(tag -> api.getTags().stream().anyMatch(apiTag -> {
                                    final Collator collator = Collator.getInstance();
                                    collator.setStrength(Collator.NO_DECOMPOSITION);
                                    return collator.compare(tag, apiTag) == 0;
                                })));

                if (!hasMatchingTags) {
                    logger.info("The API {} has been ignored because not in configured tags {}", api.getName(),
                            tagList);
                }
                return hasMatchingTags;
            }
            logger.info("Tags {} are configured on gateway instance but not found on the API {}", tagList,
                    api.getName());
            return false;
        }
        // no tags configured on this gateway instance
        return true;
    }

    private Set<Api> getDeployedApis(Set<io.gravitee.repository.management.model.Api> apis) {
        Set<Api> deployedApis = new HashSet<>();

        List<Event> events = eventRepository.search(new EventCriteria.Builder()
                .types(EventType.PUBLISH_API, EventType.UNPUBLISH_API, EventType.START_API, EventType.STOP_API)
                .build());

        if (events != null && !events.isEmpty()) {
            try {
                for (io.gravitee.repository.management.model.Api api : apis) {
                    for (Event _event : events) {
                        if (api.getId()
                                .equals(_event.getProperties().get(Event.EventProperties.API_ID.getValue()))) {
                            if (!EventType.UNPUBLISH_API.equals(_event.getType())) {
                                io.gravitee.repository.management.model.Api payloadApi = objectMapper.readValue(
                                        _event.getPayload(), io.gravitee.repository.management.model.Api.class);
                                deployedApis.add(convert(payloadApi));
                            }
                            break;
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("Error while determining deployed APIs store into events payload", e);
            }
        }

        return deployedApis;
    }

    private Api convert(io.gravitee.repository.management.model.Api remoteApi) {
        try {
            String definition = remoteApi.getDefinition();
            if (definition != null && !definition.isEmpty()) {
                Api api = objectMapper.readValue(definition, Api.class);

                api.setId(remoteApi.getId());
                api.setName(remoteApi.getName());
                api.setVersion(remoteApi.getVersion());
                api.setEnabled(remoteApi.getLifecycleState() == LifecycleState.STARTED);
                api.setDeployedAt(remoteApi.getUpdatedAt());

                try {
                    // Deploy only published plan
                    api.setPlans(planRepository.findByApi(api.getId()).stream()
                            .filter(plan -> plan
                                    .getStatus() == io.gravitee.repository.management.model.Plan.Status.PUBLISHED)
                            .map(this::convert).collect(Collectors.toList()));
                } catch (TechnicalException te) {
                    logger.error("Unexpected error while adding plan to the API: {} [{}]", remoteApi.getName(),
                            remoteApi.getId(), te);
                }
                return api;
            }
        } catch (IOException ioe) {
            logger.error("Unable to prepare API definition from repository", ioe);
        }

        return null;
    }

    private Plan convert(io.gravitee.repository.management.model.Plan repoPlan) {
        Plan plan = new Plan();

        plan.setId(repoPlan.getId());
        plan.setName(repoPlan.getName());

        if (repoPlan.getSecurity() != null) {
            plan.setSecurity(repoPlan.getSecurity().name());
        } else {
            // TODO: must be handle by a migration script
            plan.setSecurity("api_key");
        }

        try {
            if (repoPlan.getDefinition() != null && !repoPlan.getDefinition().trim().isEmpty()) {
                HashMap<String, Path> paths = objectMapper.readValue(repoPlan.getDefinition(),
                        new TypeReference<HashMap<String, Path>>() {
                        });

                plan.setPaths(paths);
            }
        } catch (IOException ioe) {
            logger.error("Unexpected error while converting plan: {}", plan, ioe);
        }

        return plan;
    }

    public void setApiRepository(ApiRepository apiRepository) {
        this.apiRepository = apiRepository;
    }

    public void setEventRepository(EventRepository eventRepository) {
        this.eventRepository = eventRepository;
    }

    public void setApiManager(ApiManager apiManager) {
        this.apiManager = apiManager;
    }
}