Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (C) Sportradar AG. See LICENSE for full license governing this code
*/
package com.sportradar.unifiedodds.sdk;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
import com.sportradar.unifiedodds.sdk.caching.*;
import com.sportradar.unifiedodds.sdk.caching.impl.DataRouterImpl;
import com.sportradar.unifiedodds.sdk.cfg.*;
import com.sportradar.unifiedodds.sdk.di.CustomisableSDKModule;
import com.sportradar.unifiedodds.sdk.di.MasterInjectionModule;
import com.sportradar.unifiedodds.sdk.entities.BookmakerDetails;
import com.sportradar.unifiedodds.sdk.exceptions.InitException;
import com.sportradar.unifiedodds.sdk.exceptions.InvalidBookmakerDetailsException;
import com.sportradar.unifiedodds.sdk.impl.AMQPConnectionFactory;
import com.sportradar.unifiedodds.sdk.impl.OddsFeedSessionImpl;
import com.sportradar.unifiedodds.sdk.impl.SDKProducerManager;
import com.sportradar.unifiedodds.sdk.impl.SDKTaskScheduler;
import com.sportradar.unifiedodds.sdk.impl.apireaders.WhoAmIReader;
import com.sportradar.unifiedodds.sdk.replay.ReplayManager;
import com.sportradar.utils.URN;
import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.AbstractMap.SimpleEntry;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
/**
* The main SDK object, this is the starting point of the UF SDK.
*/
public class OddsFeed {
/**
* The logger instance used for the OddsFeed logs
*/
private static final Logger logger = LoggerFactory.getLogger(OddsFeed.class);
/**
* The injector used by this feed instance
*/
private final Injector injector;
/**
* The OddsFeed main configuration file
*/
private final SDKInternalConfiguration oddsFeedConfiguration;
/**
* A HashSet that contains all the created sessions
*/
private final HashSet createdSessionData = new HashSet<>();
/**
* A {@link SDKProducerManager} instance used to manage available producers
*/
private SDKProducerManager producerManager;
/**
* The container of the sports data
*/
private SportsInfoManager sportsInfoManager;
/**
* The container of all the market descriptions
*/
private MarketDescriptionManager marketDescriptionManager;
/**
* The CashOut probabilities manager used to access probabilities data
*/
private CashOutProbabilitiesManager cashOutProbabilitiesManager;
/**
* The instance used to request specific event recoveries
*/
private EventRecoveryRequestIssuer recoveryRequestIssuer;
/**
* The instance used to perform booking calendar operations
*/
private BookingManager bookingManager;
/**
* The instance used to perform custom bet operations
*/
private CustomBetManager customBetManager;
/**
* The instance used to get bookmaker and used token details
*/
private BookmakerDetails bookmakerDetails;
/**
* Indicates if the feed object was initialized
*/
private boolean feedInitialized = false;
/**
* Field that marks if the feed was already opened
*/
private boolean feedOpened = false;
/**
* The most basic feed constructor
*
* @param listener {@link SDKGlobalEventsListener} that handles global feed events
* @param config {@link OddsFeedConfiguration}, the configuration class used to configure the new feed,
* the configuration can be obtained using {@link #getOddsFeedConfigurationBuilder()}
*/
public OddsFeed(SDKGlobalEventsListener listener, OddsFeedConfiguration config) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(config);
logger.info("OddsFeed instance created with \n{}", config);
this.oddsFeedConfiguration = new SDKInternalConfiguration(config, config.getEnvironment() == Environment.Replay, new SDKConfigurationPropertiesReader(), new SDKConfigurationYamlReader());
this.injector = createSdkInjector(listener, null);
checkLocales();
}
/**
* The following constructor is used to crate the OddsFeed instance directly with the internal configuration
*
* @param listener {@link SDKGlobalEventsListener} that handles global feed events
* @param config {@link SDKInternalConfiguration}, the configuration class used to configure the new feed
*/
protected OddsFeed(SDKGlobalEventsListener listener, SDKInternalConfiguration config) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(config);
logger.info("OddsFeed instance created with \n{}", config);
this.oddsFeedConfiguration = config;
this.injector = createSdkInjector(listener, null);
}
/**
* The following constructor is used to crate the OddsFeed instance with a custom injection module
*
* @param listener {@link SDKGlobalEventsListener} that handles global feed events
* @param config {@link OddsFeedConfiguration}, the configuration class used to configure the new feed
* @param customisableSDKModule the customised injection module
*/
protected OddsFeed(SDKGlobalEventsListener listener, OddsFeedConfiguration config, CustomisableSDKModule customisableSDKModule) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(config);
logger.info("OddsFeed instance created with \n{}", config);
this.oddsFeedConfiguration = new SDKInternalConfiguration(config, new SDKConfigurationPropertiesReader(), new SDKConfigurationYamlReader());
this.injector = createSdkInjector(listener, customisableSDKModule);
}
/**
* The following constructor is used to crate the OddsFeed instance directly with the internal configuration and
* the customisable module
*
* @param listener {@link SDKGlobalEventsListener} that handles global feed events
* @param config {@link SDKInternalConfiguration}, the configuration class used to configure the new feed
* @param customisableSDKModule the customised injection module
*/
protected OddsFeed(SDKGlobalEventsListener listener, SDKInternalConfiguration config, CustomisableSDKModule customisableSDKModule) {
Preconditions.checkNotNull(listener);
Preconditions.checkNotNull(config);
logger.info("OddsFeed instance created with \n{}", config);
this.oddsFeedConfiguration = config;
this.injector = createSdkInjector(listener, customisableSDKModule);
}
/**
* The following constructor should be used only for testing purposes
*
* @param injector a predefined injector
* @param config {@link SDKInternalConfiguration}, the configuration class used to configure the new feed
*/
protected OddsFeed(SDKInternalConfiguration config, Injector injector) {
Preconditions.checkNotNull(config);
Preconditions.checkNotNull(injector);
logger.info("OddsFeed instance created with \n{}", config);
this.oddsFeedConfiguration = config;
this.injector = injector;
logger.warn("OddsFeed initialised with a provided predefined injector");
}
/**
* Returns a builder used to make {@link OddsFeedConfiguration} instances
*
* @deprecated in favour of the {@link #getOddsFeedConfigurationBuilder()}
*
* @return a builder used to make {@link OddsFeedConfiguration} instances
*/
@Deprecated
public static ConfigurationAccessTokenSetter getConfigurationBuilder() {
return new OddsFeedConfigurationBuilderImpl(new SDKConfigurationPropertiesReader());
}
/**
* Returns a builder used to make {@link OddsFeedConfiguration} instances
*
* @since 2.0.5
*
* @return a builder used to make {@link OddsFeedConfiguration} instances
*/
public static TokenSetter getOddsFeedConfigurationBuilder() {
return new TokenSetterImpl(new SDKConfigurationPropertiesReader(), new SDKConfigurationYamlReader());
}
/**
* Builder used to create the required sessions
*
* @return current feed session builder
*/
public OddsFeedSessionBuilder getSessionBuilder() {
this.initOddsFeedInstance(); // init so the initial token validation gets triggered
return new OddsFeedSessionBuilderImpl(this);
}
/**
* Returns the {@link MarketDescriptionManager} used to access markets data trough our API
*
* @return {@link MarketDescriptionManager} used to access markets data
*/
public MarketDescriptionManager getMarketDescriptionManager(){
this.initOddsFeedInstance();
return this.marketDescriptionManager;
}
/**
* Returns the {@link SportsInfoManager} helper that contains useful methods for specific event data retrieval
*
* @return {@link SportsInfoManager} used to access various sports data
*/
public SportsInfoManager getSportsInfoManager(){
this.initOddsFeedInstance();
return this.sportsInfoManager;
}
/**
* Returns the {@link ProducerManager} instance used to manage available producers
*
* @return a {@link ProducerManager} instance used to manage available producers
*/
public ProducerManager getProducerManager() {
this.initOddsFeedInstance();
return producerManager;
}
/**
* Returns the {@link CashOutProbabilitiesManager} instance used to access probabilities data
*
* @return a {@link CashOutProbabilitiesManager} instance which can be used to access probabilities data
*/
public CashOutProbabilitiesManager getCashOutProbabilitiesManager() {
this.initOddsFeedInstance();
return cashOutProbabilitiesManager;
}
/**
* Returns the {@link EventRecoveryRequestIssuer} instance which provides utility methods used to initialize
* event message recoveries
*
* @return the {@link EventRecoveryRequestIssuer} instance associated with the current feed instance
*/
public EventRecoveryRequestIssuer getEventRecoveryRequestIssuer() {
this.initOddsFeedInstance();
return recoveryRequestIssuer;
}
/**
* Returns the {@link BookingManager} instance which can be used to perform booking calendar operations
*
* @return the {@link BookingManager} associated with the current {@link OddsFeed} instance
*/
public BookingManager getBookingManager() {
this.initOddsFeedInstance();
return bookingManager;
}
/**
* Returns the {@link CustomBetManager} instance which can be used to perform custom bet operations
*
* @return the {@link CustomBetManager} associated with the current {@link OddsFeed} instance
*/
public CustomBetManager getCustomBetManager() {
this.initOddsFeedInstance();
return customBetManager;
}
/**
* Returns the {@link BookmakerDetails} instance with bookmaker and token info
*
* @return the {@link BookmakerDetails} associated with the current {@link OddsFeed} instance
*/
public BookmakerDetails getBookmakerDetails() {
this.initOddsFeedInstance();
return bookmakerDetails;
}
/**
* This method opens/starts the feed with all the built sessions and
* creates the various tasks needed for optimal OddsFeed operation
*
* @throws InitException if the feed fails to initialize
*/
public void open() throws InitException {
if (!this.feedOpened) {
this.initOddsFeedInstance();
if (!createdSessionData.isEmpty()) {
// disable the producers that are not requested by specified message interests
Set requestedProducers = new HashSet<>();
for (SessionData createdSession : createdSessionData) {
requestedProducers.addAll(createdSession.messageInterest.getPossibleSourceProducers(producerManager.getAvailableProducers()));
}
producerManager.getAvailableProducers().keySet().forEach(id -> {
if (!requestedProducers.contains(id)) {
producerManager.disableProducer(id);
}
});
Map> sessionRoutingKeys =
OddsFeedRoutingKeyBuilder.generateKeys(createdSessionData.stream()
.collect(Collectors.toMap(Object::hashCode, v -> new SimpleEntry<>(v.messageInterest, v.eventIds))), oddsFeedConfiguration);
try {
boolean aliveRoutingKeySessionPresent = createdSessionData.stream()
.anyMatch(cs -> cs.messageInterest == MessageInterest.SystemAliveMessages);
if (!aliveRoutingKeySessionPresent) {
OddsFeedSessionImpl systemMessagesSession = injector.getInstance(OddsFeedSessionImpl.class);
SessionData firstCreatedSession = createdSessionData.stream()
.findFirst()
.orElseThrow(() -> new IllegalStateException("Feed created without sessions?"));
systemMessagesSession.open(
Lists.newArrayList(MessageInterest.SystemAliveMessages.getRoutingKeys()),
MessageInterest.SystemAliveMessages,
firstCreatedSession.listener
);
}
for (SessionData sessionData : createdSessionData) {
sessionData.session.open(
sessionRoutingKeys.get(sessionData.hashCode()),
sessionData.messageInterest,
sessionData.listener
);
}
injector.getInstance(RecoveryManager.class).init();
injector.getInstance(SDKTaskScheduler.class).open();
} catch (IOException exception) {
throw new InitException("Unexpected issue initializing OddsFeed", exception);
}
} else {
logger.warn("Feed opened without sessions");
}
this.feedOpened = true;
this.producerManager.open();
} else {
throw new InitException("Feed can not be reopened once it has been closed");
}
}
/**
* Method used to close the feed and all its sessions
*
* @throws IOException if the AMQP connection closure fails
*/
public void close() throws IOException {
if (!this.feedOpened) {
throw new IllegalStateException("Can't close an already closed OddsFeed instance");
}
logger.warn("OddsFeed.close invoked - closing the feed instance");
AMQPConnectionFactory amqpConnectionFactory = injector.getInstance(AMQPConnectionFactory.class);
if (amqpConnectionFactory.isConnectionOpen())
amqpConnectionFactory.close();
injector.getInstance(CloseableHttpClient.class).close();
injector.getInstance(SDKTaskScheduler.class).shutdownNow();
injector.getInstance(Key.get(ScheduledExecutorService.class, Names.named("DedicatedRecoveryManagerExecutor"))).shutdownNow();
injector.getInstance(Key.get(ExecutorService.class, Names.named("DedicatedRabbitMqExecutor"))).shutdownNow();
}
public List getAvailableLanguages() {
String[] languages = "it,en,de,fr,se,es,ru,zh,ja,hr,tr,sk,sl,no,da,nl,pl,pt,cs,fi,th,hu,bg,ek,ro,et,lv,bs,sr,ml,lt,id,vi,ko,aa,ka,br,zht,ukr,aze,heb,kaz,sqi,srl".split(",");
return Arrays.stream(languages)
.sorted()
.map(Locale::forLanguageTag)
.collect(Collectors.toList());
}
private void checkLocales() {
List availableLanguages = getAvailableLanguages();
List unsupportedLocales = oddsFeedConfiguration.getDesiredLocales().stream()
.filter(l -> !availableLanguages.contains(l))
.collect(Collectors.toList());
if (!unsupportedLocales.isEmpty())
logger.warn("Unsupported locales: {}", unsupportedLocales);
}
private void initOddsFeedInstance() {
if (feedInitialized) {
return;
}
String version = injector.getInstance(Key.get(String.class, Names.named("version")));
logger.info("Initializing the OddsFeed instance (Sportradar Unified Odds SDK {})", version);
// validate the client token
WhoAmIReader whoAmI = injector.getInstance(WhoAmIReader.class);
try {
whoAmI.validateBookmakerDetails();
} catch (IllegalStateException e) {
throw new InvalidBookmakerDetailsException("Feed initialization failed", e);
}
// Hack for now, until we implement the Cache manager
DataRouter dataRouter = injector.getInstance(DataRouter.class);
if (dataRouter instanceof DataRouterImpl) {
((DataRouterImpl) dataRouter).setDataListeners(
Lists.newArrayList(
(DataRouterListener) injector.getInstance(SportEventCache.class),
(DataRouterListener) injector.getInstance(SportsDataCache.class),
(DataRouterListener) injector.getInstance(ProfileCache.class),
(DataRouterListener) injector.getInstance(SportEventStatusCache.class)
));
}
this.sportsInfoManager = injector.getInstance(SportsInfoManager.class);
this.marketDescriptionManager = injector.getInstance(MarketDescriptionManager.class);
this.producerManager = injector.getInstance(SDKProducerManager.class);
this.recoveryRequestIssuer = injector.getInstance(EventRecoveryRequestIssuer.class);
this.cashOutProbabilitiesManager = injector.getInstance(CashOutProbabilitiesManager.class);
this.bookingManager = injector.getInstance(BookingManager.class);
this.customBetManager = injector.getInstance(CustomBetManager.class);
this.bookmakerDetails = whoAmI.getBookmakerDetails();
feedInitialized = true;
}
private Injector createSdkInjector(SDKGlobalEventsListener listener, CustomisableSDKModule customisableSDKModule) {
return Guice.createInjector(new MasterInjectionModule(listener, this.oddsFeedConfiguration, customisableSDKModule));
}
private void createSession(OddsFeedSessionImpl session, MessageInterest oddsInterest, Set eventIds, OddsFeedListener listener) {
if (this.feedOpened){
throw new IllegalStateException("Sessions can not be created once the feed has been opened");
} else {
SessionData sessionData = new SessionData(session, oddsInterest, eventIds, listener);
createdSessionData.add(sessionData);
}
}
/**
* Returns the replay manager for the current feed that can be used to add SportEvents and test-scenarios to replay.
*
* @return - the replay manager for the current feed that can be used to add SportEvents and test-scenarios to replay
*/
protected ReplayManager getReplayManager() {
if (!oddsFeedConfiguration.isReplaySession()) {
return null;
} else {
initOddsFeedInstance();
return injector.getInstance(ReplayManager.class);
}
}
class SessionData {
private final OddsFeedSessionImpl session;
private final MessageInterest messageInterest;
private final Set eventIds;
private final OddsFeedListener listener;
SessionData(OddsFeedSessionImpl session, MessageInterest messageInterest, Set eventIds, OddsFeedListener listener) {
this.session = session;
this.messageInterest = messageInterest;
this.eventIds = eventIds;
this.listener = listener;
}
}
class OddsFeedSessionBuilderImpl implements OddsFeedSessionBuilder {
private OddsFeed oddsFeed;
private OddsFeedListener mainOddsFeedListener;
private MessageInterest msgInterestLevel;
private HashSet eventIds;
private HashSet specificOddsFeedListeners;
OddsFeedSessionBuilderImpl(OddsFeed oddsFeed) {
this.oddsFeed = oddsFeed;
}
@Override
public OddsFeedSessionBuilder setListener(OddsFeedListener listener) {
this.mainOddsFeedListener = listener;
return this;
}
@Override
public OddsFeedSessionBuilder setMessageInterest(MessageInterest msgInterest) {
this.msgInterestLevel = msgInterest;
return this;
}
@Override
public OddsFeedSessionBuilder setSpecificListeners(HashSet specificOddsFeedListeners){
if (this.specificOddsFeedListeners == null)
this.specificOddsFeedListeners = new HashSet<>();
this.specificOddsFeedListeners.addAll(specificOddsFeedListeners);
return this;
}
@Override
public OddsFeedSessionBuilder setSpecificListeners(GenericOddsFeedListener specificOddsFeedListener){
if (this.specificOddsFeedListeners == null)
this.specificOddsFeedListeners = new HashSet<>();
this.specificOddsFeedListeners.add(specificOddsFeedListener);
return this;
}
@Override
public OddsFeedSessionBuilder setSpecificEventsOnly(Set specificEventsOnly) {
this.msgInterestLevel = MessageInterest.SpecifiedMatchesOnly;
if (this.eventIds == null)
this.eventIds = new HashSet<>();
this.eventIds.addAll(specificEventsOnly);
return this;
}
@Override
public OddsFeedSessionBuilder setSpecificEventsOnly(URN specificEventsOnly) {
return setSpecificEventsOnly(Collections.singleton(specificEventsOnly));
}
@Override
public OddsFeedSession build() {
// TODO @eti: handle specific event listeners
OddsFeedSessionImpl session = injector.getInstance(OddsFeedSessionImpl.class);
this.oddsFeed.createSession(session, msgInterestLevel, eventIds, mainOddsFeedListener);
this.msgInterestLevel = null;
this.eventIds = null;
this.mainOddsFeedListener = null;
this.specificOddsFeedListeners = null;
return session;
}
}
}