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

io.gravitee.rest.api.service.impl.search.SearchEngineServiceImpl Maven / Gradle / Ivy

There is a newer version: 3.10.0
Show newest version
/**
 * 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.rest.api.service.impl.search;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.gravitee.repository.exceptions.TechnicalException;
import io.gravitee.repository.management.model.MessageRecipient;
import io.gravitee.rest.api.model.ApiPageEntity;
import io.gravitee.rest.api.model.PageEntity;
import io.gravitee.rest.api.model.UserEntity;
import io.gravitee.rest.api.model.api.ApiEntity;
import io.gravitee.rest.api.model.command.CommandSearchIndexerEntity;
import io.gravitee.rest.api.model.command.CommandTags;
import io.gravitee.rest.api.model.command.NewCommandEntity;
import io.gravitee.rest.api.model.search.Indexable;
import io.gravitee.rest.api.service.ApiService;
import io.gravitee.rest.api.service.CommandService;
import io.gravitee.rest.api.service.PageService;
import io.gravitee.rest.api.service.UserService;
import io.gravitee.rest.api.service.exceptions.AbstractNotFoundException;
import io.gravitee.rest.api.service.exceptions.TechnicalManagementException;
import io.gravitee.rest.api.service.impl.search.lucene.DocumentSearcher;
import io.gravitee.rest.api.service.impl.search.lucene.DocumentTransformer;
import io.gravitee.rest.api.service.impl.search.lucene.SearchEngineIndexer;
import io.gravitee.rest.api.service.search.SearchEngineService;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

/**
 * @author David BRASSELY (david.brassely at graviteesource.com)
 * @author Nicolas GERAUD (nicolas.geraud at graviteesource.com)
 * @author GraviteeSource Team
 */
@Component
public class SearchEngineServiceImpl implements SearchEngineService {

    /**
     * Logger.
     */
    private final Logger logger = LoggerFactory.getLogger(SearchEngineServiceImpl.class);

    @Autowired
    private SearchEngineIndexer indexer;

    @Autowired
    private Collection transformers;

    @Autowired
    private Collection searchers;

    @Autowired
    @Lazy
    private CommandService commandService;

    @Autowired
    @Lazy
    private ApiService apiService;

    @Autowired
    @Lazy
    private PageService pageService;

    @Autowired
    @Lazy
    private UserService userService;

    private ObjectMapper mapper = new ObjectMapper();

    private static final String ACTION_INDEX = "I";
    private static final String ACTION_DELETE = "D";

    @Async
    @Override
    public void index(Indexable source, boolean locally) {
        indexLocally(source);
        if (!locally) {
            CommandSearchIndexerEntity content = new CommandSearchIndexerEntity();
            content.setAction(ACTION_INDEX);
            content.setId(source.getId());
            content.setClazz(source.getClass().getName());

            sendCommands(content);
        }
    }

    @Async
    @Override
    public void delete(Indexable source, boolean locally) {
        deleteLocally(source);
        if (!locally) {
            CommandSearchIndexerEntity content = new CommandSearchIndexerEntity();
            content.setAction(ACTION_DELETE);
            content.setId(source.getId());
            content.setClazz(source.getClass().getName());

            sendCommands(content);
        }
    }

    @Override
    public void process(CommandSearchIndexerEntity content) {
        if (ACTION_DELETE.equals(content.getAction())) {
            try {
                Indexable source = createInstance(content.getClazz());
                source.setId(content.getId());
                deleteLocally(source);
            } catch (Exception ex) {
                throw new TechnicalManagementException(
                    "Unable to delete document for content [ " + content.getId() + " - " + content.getClazz() + " ]",
                    ex
                );
            }
        } else if (ACTION_INDEX.equals(content.getAction())) {
            Indexable source = getSource(content.getClazz(), content.getId());
            if (source != null) {
                indexLocally(source);
            }
        }
    }

    private void sendCommands(CommandSearchIndexerEntity content) {
        try {
            NewCommandEntity msg = new NewCommandEntity();
            msg.setTags(Collections.singletonList(CommandTags.DATA_TO_INDEX));
            msg.setTo(MessageRecipient.MANAGEMENT_APIS.name());
            msg.setTtlInSeconds(60);
            msg.setContent(mapper.writeValueAsString(content));
            commandService.send(msg);
        } catch (JsonProcessingException e) {
            logger.error("Unexpected error while sending a message", e);
        }
    }

    private Indexable getSource(String clazz, String id) {
        try {
            if (ApiEntity.class.getName().equals(clazz)) {
                return apiService.findById(id);
            } else if (PageEntity.class.getName().equals(clazz) || ApiPageEntity.class.getName().equals(clazz)) {
                return pageService.findById(id);
            } else if (UserEntity.class.getName().equals(clazz)) {
                return userService.findById(id);
            }
        } catch (final AbstractNotFoundException nfe) {
            // ignore not found exception because may be due to synchronization not yet processed by DBs
        }
        return null;
    }

    private void indexLocally(Indexable source) {
        transformers
            .stream()
            .filter(transformer -> transformer.handle(source.getClass()))
            .findFirst()
            .ifPresent(
                transformer -> {
                    try {
                        indexer.index(transformer.transform(source));
                    } catch (TechnicalException te) {
                        logger.error("Unexpected error while indexing a document", te);
                    }
                }
            );
    }

    private void deleteLocally(Indexable source) {
        transformers
            .stream()
            .filter(transformer -> transformer.handle(source.getClass()))
            .findFirst()
            .ifPresent(
                transformer -> {
                    try {
                        indexer.remove(transformer.transform(source));
                    } catch (TechnicalException te) {
                        logger.error("Unexpected error while deleting a document", te);
                    }
                }
            );
    }

    private Indexable createInstance(String className) throws Exception {
        try {
            final Class clazz = Class.forName(className);
            Assert.isAssignable(Indexable.class, clazz);
            return (Indexable) clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
            logger.error("Unable to instantiate class: {}", className, ex);
            throw ex;
        }
    }

    @Override
    public SearchResult search(io.gravitee.rest.api.service.search.query.Query query) {
        Optional results = searchers
            .stream()
            .filter(searcher -> searcher.handle(query.getRoot()))
            .findFirst()
            .flatMap(
                searcher -> {
                    try {
                        return Optional.of(searcher.search(query));
                    } catch (TechnicalException te) {
                        logger.error("Unexpected error while searching a document", te);
                        return Optional.empty();
                    }
                }
            );

        return results.orElse(null);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy