package io.divolte.server.recordmapping; import io.divolte.server.config.UserAgentParserConfiguration; import io.divolte.server.config.ValidatedConfiguration; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.function.Function; import net.sf.uadetector.ReadableUserAgent; import net.sf.uadetector.UserAgentStringParser; import net.sf.uadetector.service.UADetectorServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault public final class UserAgentParserAndCache { private final static Logger logger = LoggerFactory.getLogger(UserAgentParserAndCache.class); private final LoadingCache<String,ReadableUserAgent> cache; public UserAgentParserAndCache(final ValidatedConfiguration vc) { final UserAgentStringParser parser = parserBasedOnTypeConfig(vc.configuration().global.mapper.userAgentParser.type); this.cache = sizeBoundCacheFromLoadingFunction(parser::parse, vc.configuration().global.mapper.userAgentParser.cacheSize); logger.info("User agent parser data version: {}", parser.getDataVersion()); } public Optional<ReadableUserAgent> tryParse(final String userAgentString) { try { return Optional.of(cache.get(userAgentString)); } catch (final ExecutionException e) { logger.debug("Failed to parse user agent string for: " + userAgentString); return Optional.empty(); } } private static UserAgentStringParser parserBasedOnTypeConfig(UserAgentParserConfiguration.ParserType type) { switch (type) { case CACHING_AND_UPDATING: logger.info("Using caching and updating user agent parser."); return UADetectorServiceFactory.getCachingAndUpdatingParser(); case ONLINE_UPDATING: logger.info("Using online updating user agent parser."); return UADetectorServiceFactory.getOnlineUpdatingParser(); case NON_UPDATING: logger.info("Using non-updating (resource module based) user agent parser."); return UADetectorServiceFactory.getResourceModuleParser(); default: throw new RuntimeException("Invalid user agent parser type. Valid values are: caching_and_updating, online_updating, non_updating."); } } private static <K,V> LoadingCache<K, V> sizeBoundCacheFromLoadingFunction(Function<K, V> loader, int size) { return CacheBuilder .newBuilder() .maximumSize(size) .initialCapacity(size) .build(new CacheLoader<K, V>() { @Override public V load(K key) throws Exception { return loader.apply(key); } }); } }