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

com.sportradar.unifiedodds.sdk.caching.impl.ci.TournamentStageCIImpl Maven / Gradle / Ivy

/*
 * Copyright (C) Sportradar AG. See LICENSE for full license governing this code
 */

package com.sportradar.unifiedodds.sdk.caching.impl.ci;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.sportradar.uf.sportsapi.datamodel.SAPICompetitors;
import com.sportradar.uf.sportsapi.datamodel.SAPITournament;
import com.sportradar.uf.sportsapi.datamodel.SAPITournamentInfoEndpoint;
import com.sportradar.uf.sportsapi.datamodel.SAPITournamentLength;
import com.sportradar.unifiedodds.sdk.BookingManager;
import com.sportradar.unifiedodds.sdk.ExceptionHandlingStrategy;
import com.sportradar.unifiedodds.sdk.caching.DataRouterManager;
import com.sportradar.unifiedodds.sdk.caching.StageCI;
import com.sportradar.unifiedodds.sdk.caching.ci.ReferenceIdCI;
import com.sportradar.unifiedodds.sdk.caching.ci.SportEventConditionsCI;
import com.sportradar.unifiedodds.sdk.caching.ci.VenueCI;
import com.sportradar.unifiedodds.sdk.entities.BookingStatus;
import com.sportradar.unifiedodds.sdk.entities.Competitor;
import com.sportradar.unifiedodds.sdk.entities.Reference;
import com.sportradar.unifiedodds.sdk.entities.StageType;
import com.sportradar.unifiedodds.sdk.exceptions.ObjectNotFoundException;
import com.sportradar.unifiedodds.sdk.exceptions.internal.CommunicationException;
import com.sportradar.unifiedodds.sdk.exceptions.internal.DataRouterStreamException;
import com.sportradar.unifiedodds.sdk.impl.dto.SportEventStatusDTO;
import com.sportradar.utils.SdkHelper;
import com.sportradar.utils.URN;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * Created on 19/10/2017.
 * // TODO @eti: Javadoc
 */
class TournamentStageCIImpl implements StageCI {
    private static final Logger logger = LoggerFactory.getLogger(TournamentStageCIImpl.class);

    /**
     * A {@link Locale} specifying the default language
     */
    private final Locale defaultLocale;

    /**
     * An {@link URN} specifying the id of the associated sport event
     */
    private final URN id;

    /**
     * An indication on how should be the SDK exceptions handled
     */
    private final ExceptionHandlingStrategy exceptionHandlingStrategy;

    /**
     * The {@link DataRouterManager} which is used to trigger data fetches
     */
    private final DataRouterManager dataRouterManager;

    /**
     * The {@link Date} specifying when the sport event associated with the current
     * instance was scheduled
     */
    private Date scheduled;

    /**
     * The {@link Date} specifying when the sport event associated with the current
     * instance was scheduled to end
     */
    private Date scheduledEnd;

    /**
     * A {@link List} of competitor identifiers that participate in the sport event
     * associated with the current instance
     */
    private List competitorIds;

    /**
     * A {@link Map} of competitors id and their references that participate in the sport event
     * associated with the current instance
     */
    private Map competitorsReferences;

    /**
     * A {@link Map} storing the available sport event names
     */
    private final Map sportEventNames = Maps.newConcurrentMap();

    /**
     * A {@link URN} specifying the id of the parent category
     */
    private URN categoryId;

    /**
     * A {@link List} of locales that are already fully cached - only when the full tournament info endpoint is cached
     */
    private List cachedLocales = Collections.synchronizedList(new ArrayList<>());

    /**
     * An {@link ReentrantLock} used to synchronize summary request operations
     */
    private final ReentrantLock fetchRequestLock = new ReentrantLock();

    TournamentStageCIImpl(URN id, DataRouterManager dataRouterManager, Locale defaultLocale, ExceptionHandlingStrategy exceptionHandlingStrategy, SAPITournament endpointData, Locale dataLocale) {
        Preconditions.checkNotNull(id);
        Preconditions.checkNotNull(dataRouterManager);
        Preconditions.checkNotNull(defaultLocale);
        Preconditions.checkNotNull(exceptionHandlingStrategy);
        Preconditions.checkNotNull(endpointData);
        Preconditions.checkNotNull(dataLocale);

        this.id = id;
        this.dataRouterManager = dataRouterManager;
        this.defaultLocale = defaultLocale;
        this.exceptionHandlingStrategy = exceptionHandlingStrategy;

        if (endpointData.getName() != null) {
            this.sportEventNames.put(dataLocale, endpointData.getName());
        }
        else{
            this.sportEventNames.put(dataLocale, "");
        }

        this.categoryId = URN.parse(endpointData.getCategory().getId());
        this.scheduled = endpointData.getScheduled() == null ? null :
                endpointData.getScheduled().toGregorianCalendar().getTime();
        this.scheduledEnd = endpointData.getScheduledEnd() == null ? null :
                endpointData.getScheduledEnd().toGregorianCalendar().getTime();

        if ((this.scheduled == null || this.scheduledEnd == null) && endpointData.getTournamentLength() != null) {
            SAPITournamentLength tournamentLength = endpointData.getTournamentLength();
            this.scheduled = tournamentLength.getStartDate() == null ? null :
                    tournamentLength.getStartDate().toGregorianCalendar().getTime();
            this.scheduledEnd = tournamentLength.getEndDate() == null ? null :
                    tournamentLength.getEndDate().toGregorianCalendar().getTime();
        }
    }

    TournamentStageCIImpl(URN id, DataRouterManager dataRouterManager, Locale defaultLocale, ExceptionHandlingStrategy exceptionHandlingStrategy, SAPITournamentInfoEndpoint endpointData, Locale dataLocale) {
        this(id, dataRouterManager, defaultLocale, exceptionHandlingStrategy, endpointData.getTournament(), dataLocale);

        Preconditions.checkNotNull(endpointData);
        Preconditions.checkNotNull(dataLocale);

        SAPICompetitors endpointCompetitors = endpointData.getCompetitors() != null ?
                endpointData.getCompetitors() :
                endpointData.getTournament().getCompetitors();
        this.competitorIds = endpointCompetitors == null ? null :
                Collections.synchronizedList(endpointCompetitors.getCompetitor().stream()
                        .map(c -> URN.parse(c.getId())).collect(Collectors.toList()));

        cachedLocales.add(dataLocale);
    }

    /**
     * Returns the {@link URN} representing id of the related entity
     *
     * @return the {@link URN} representing id of the related entity
     */
    @Override
    public URN getId() {
        return id;
    }

    /**
     * Returns a {@link Map} of translated sport event names
     * The name of race objects is the "name" attribute from the fixture endpoint.
     *
     * @param locales the {@link Locale}s in which the name should be provided
     * @return the sport event name if available; otherwise null
     */
    @Override
    public Map getNames(List locales) {
        if (sportEventNames.keySet().containsAll(locales)) {
            return ImmutableMap.copyOf(sportEventNames);
        }

        if (cachedLocales.containsAll(locales)) {
            return ImmutableMap.copyOf(sportEventNames);
        }

        requestMissingStageTournamentData(locales);

        return ImmutableMap.copyOf(sportEventNames);
    }

    /**
     * Determines whether the current instance has translations for the specified languages
     *
     * @param localeList a {@link List} specifying the required languages
     * @return true if the current instance contains data in the required locals, otherwise false.
     */
    @Override
    public boolean hasTranslationsLoadedFor(List localeList) {
        return cachedLocales.containsAll(localeList);
    }

    /**
     * Returns the identifier of the stage parent
     *
     * @return the {@link URN} identifier of the parent stage if available; otherwise null
     */
    @Override
    public URN getParentStageId() {
        // tournament info endpoint can not have a "parent" stage
        return null;
    }

    /**
     * Returns a {@link List} of known child stages identifiers
     *
     * @return a {@link List} known child stages identifiers if available; otherwise null
     */
    @Override
    public List getStagesIds() {
        List stageIds = null;
        try {
            stageIds = dataRouterManager.requestEventsFor(defaultLocale, id);
        } catch (CommunicationException e) {
            handleException("getStagesIds", e);
        }
        return stageIds;
    }

    /**
     * Returns a {@link StageType} indicating the type of the associated stage
     *
     * @return a {@link StageType} indicating the type of the associated stage
     */
    @Override
    public StageType getStageType() {
        return StageType.Parent;
    }

    /**
     * Returns the {@link URN} specifying the id of the parent category
     *
     * @return the {@link URN} specifying the id of the parent category
     */
    @Override
    public URN getCategoryId() {
        if (categoryId != null) {
            return categoryId;
        }

        if (!cachedLocales.isEmpty()) {
            return categoryId;
        }

        requestMissingStageTournamentData(Collections.singletonList(defaultLocale));

        return categoryId;
    }

    /**
     * Returns a {@link BookingStatus} enum member providing booking status of the current instance
     *
     * @return a {@link BookingStatus} enum member providing booking status of the current instance
     */
    @Override
    public BookingStatus getBookingStatus() {
        return BookingStatus.Unavailable;
    }

    /**
     * Returns a {@link List} of competitor identifiers that participate in the sport event
     * associated with the current instance
     *
     * @param locales a {@link List} of {@link Locale} in which the competitor data should be provided
     * @return a {@link List} of competitor identifiers that participate in the sport event
     * associated with the current instance
     */
    @Override
    public List getCompetitorIds(List locales) {
        if (cachedLocales.containsAll(locales)) {
            return competitorIds == null ? null : ImmutableList.copyOf(competitorIds);
        }

        requestMissingStageTournamentData(locales);

        return competitorIds == null ? null : ImmutableList.copyOf(competitorIds);
    }

    /**
     * Returns a {@link VenueCI} instance representing a venue where the sport event associated with the
     * current instance will take place
     *
     * @param locales a {@link List} of {@link Locale} specifying the languages to which the returned instance should be translated
     * @return a {@link VenueCI} instance representing a venue where the associated sport event
     */
    @Override
    public VenueCI getVenue(List locales) {
        // no venue info
        return null;
    }

    /**
     * Returns a {@link SportEventConditionsCI} instance representing live conditions of the sport event associated with the current instance
     *
     * @param locales a {@link List} of {@link Locale} specifying the languages to which the returned instance should be translated
     * @return a {@link SportEventConditionsCI} instance representing live conditions of the sport event associated with the current instance
     */
    @Override
    public SportEventConditionsCI getConditions(List locales) {
        // no conditions info
        return null;
    }

    /**
     * Fetch a {@link SportEventStatusDTO} via event summary
     */
    @Override
    public void fetchSportEventStatus() {
    }

    /**
     * Returns the {@link Date} specifying when the sport event associated with the current
     * instance was scheduled
     *
     * @return if available, the {@link Date} specifying when the sport event associated with the current
     * instance was scheduled; otherwise null;
     */
    @Override
    public Date getScheduled() {
        if (scheduled != null) {
            return scheduled;
        }

        if (!cachedLocales.isEmpty()) {
            return scheduled;
        }

        requestMissingStageTournamentData(Collections.singletonList(defaultLocale));

        return scheduled;
    }

    /**
     * Returns the {@link Date} specifying when the sport event associated with the current
     * instance was scheduled to end
     *
     * @return if available, the {@link Date} specifying when the sport event associated with the current
     * instance was scheduled to end; otherwise null;
     */
    @Override
    public Date getScheduledEnd() {
        if (scheduledEnd != null) {
            return scheduledEnd;
        }

        if (!cachedLocales.isEmpty()) {
            return scheduledEnd;
        }

        requestMissingStageTournamentData(Collections.singletonList(defaultLocale));

        return scheduledEnd;
    }

    @Override
    public  void merge(T endpointData, Locale dataLocale) {
        if (endpointData instanceof SAPITournamentInfoEndpoint) {
            internalMerge((SAPITournamentInfoEndpoint) endpointData, dataLocale);
        } else if (endpointData instanceof SAPITournament) {
            internalMerge((SAPITournament) endpointData, dataLocale);
        }
    }

    /**
     * Method that gets triggered when the associated event gets booked trough the {@link BookingManager}
     */
    @Override
    public void onEventBooked() {
        // tournament can not be booked
    }

    /**
     * Returns list of {@link URN} of {@link Competitor} and associated {@link Reference} for this sport event
     *
     * @return list of {@link URN} of {@link Competitor} and associated {@link Reference} for this sport event
     */
    @Override
    public Map getCompetitorsReferences() {
        if(competitorsReferences == null || cachedLocales.isEmpty()) {
            requestMissingStageTournamentData(Collections.singletonList(defaultLocale));
        }

        return competitorsReferences == null
                ? null
                : ImmutableMap.copyOf(competitorsReferences);
    }

    private void internalMerge(SAPITournamentInfoEndpoint endpointData, Locale dataLocale) {
        Preconditions.checkNotNull(endpointData);
        Preconditions.checkNotNull(dataLocale);

        if (cachedLocales.contains(dataLocale)) {
            logger.info("TournamentStageCI [{}] already contains TournamentInfo data for language {}", id, dataLocale);
        }

        SAPICompetitors endpointCompetitors = endpointData.getCompetitors() != null ?
                endpointData.getCompetitors() :
                endpointData.getTournament().getCompetitors();

        if (endpointCompetitors != null) {
            this.competitorIds = Collections.synchronizedList(endpointCompetitors
                    .getCompetitor().stream().map(c -> URN.parse(c.getId()))
                    .collect(Collectors.toList()));
        }

        internalMerge(endpointData.getTournament(), dataLocale);

        cachedLocales.add(dataLocale);
    }

    private void internalMerge(SAPITournament endpointData, Locale dataLocale) {
        Preconditions.checkNotNull(endpointData);
        Preconditions.checkNotNull(dataLocale);

        if (endpointData.getName() != null) {
            this.sportEventNames.put(dataLocale, endpointData.getName());
        }
        else{
            this.sportEventNames.put(dataLocale, "");
        }

        if (endpointData.getCategory() != null) {
            this.categoryId = URN.parse(endpointData.getCategory().getId());
        }

        this.scheduled = endpointData.getScheduled() == null ? null :
                endpointData.getScheduled().toGregorianCalendar().getTime();
        this.scheduledEnd = endpointData.getScheduledEnd() == null ? null :
                endpointData.getScheduledEnd().toGregorianCalendar().getTime();

        if ((this.scheduled == null || this.scheduledEnd == null) && endpointData.getTournamentLength() != null) {
            SAPITournamentLength tournamentLength = endpointData.getTournamentLength();
            this.scheduled = tournamentLength.getStartDate() == null ? null :
                    tournamentLength.getStartDate().toGregorianCalendar().getTime();
            this.scheduledEnd = tournamentLength.getEndDate() == null ? null :
                    tournamentLength.getEndDate().toGregorianCalendar().getTime();
        }
    }

    /**
     * Requests the data for the missing translations
     *
     * @param requiredLocales a {@link List} of locales in which the tournament data should be translated
     */
    private void requestMissingStageTournamentData(List requiredLocales) {
        Preconditions.checkNotNull(requiredLocales);

        List missingLocales = SdkHelper.findMissingLocales(cachedLocales, requiredLocales);
        if (missingLocales.isEmpty()) {
            return;
        }

        fetchRequestLock.lock();
        try {
            // recheck missing locales after lock
            missingLocales = SdkHelper.findMissingLocales(cachedLocales, requiredLocales);
            if (missingLocales.isEmpty()) {
                return;
            }

            logger.debug("Fetching missing stage tournament data for id='{}' for languages '{}'",
                    id, missingLocales.stream()
                            .map(Locale::getLanguage).collect(Collectors.joining(", ")));

            missingLocales.forEach(l -> {
                try {
                    dataRouterManager.requestSummaryEndpoint(l, id, this);
                } catch (CommunicationException e) {
                    throw new DataRouterStreamException(e.getMessage(), e);
                }
            });
        } catch (DataRouterStreamException e) {
            handleException(String.format("requestMissingStageTournamentData(%s)", missingLocales), e);
        } finally {
            fetchRequestLock.unlock();
        }
    }

    private void handleException(String request, Exception e) {
        if (exceptionHandlingStrategy == ExceptionHandlingStrategy.Throw) {
            if (e == null) {
                throw new ObjectNotFoundException("TournamentStageCI[" + id + "], request(" + request + ")");
            } else {
                throw new ObjectNotFoundException(request, e);
            }
        } else {
            if (e == null) {
                logger.warn("Error providing TournamentStageCI[{}] request({})", id, request);
            } else {
                logger.warn("Error providing TournamentStageCI[{}] request({}), ex:", id, request, e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy