-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds and uses a Caffeine-based caching parser for the user_agent proc…
…essor. Resolves #4618 Signed-off-by: David Venable <[email protected]>
- Loading branch information
Showing
7 changed files
with
238 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
...in/java/org/opensearch/dataprepper/plugins/processor/useragent/CaffeineCachingParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.dataprepper.plugins.processor.useragent; | ||
|
||
import com.github.benmanes.caffeine.cache.Cache; | ||
import com.github.benmanes.caffeine.cache.Caffeine; | ||
import ua_parser.Client; | ||
import ua_parser.Device; | ||
import ua_parser.OS; | ||
import ua_parser.Parser; | ||
import ua_parser.UserAgent; | ||
|
||
import java.util.function.Function; | ||
|
||
/** | ||
* A superclass of {@link Parser} which uses Caffeine as a cache. | ||
*/ | ||
class CaffeineCachingParser extends Parser { | ||
private final Cache<String, Client> clientCache; | ||
private final Cache<String, UserAgent> userAgentCache; | ||
private final Cache<String, Device> deviceCache; | ||
private final Cache<String, OS> osCache; | ||
|
||
/** | ||
* Constructs a new instance with a given cache size. | ||
* | ||
* @param cacheSize The size of the cache as a count of items. | ||
*/ | ||
CaffeineCachingParser(final long cacheSize) { | ||
userAgentCache = createCache(cacheSize); | ||
clientCache = createCache(cacheSize); | ||
deviceCache = createCache(cacheSize); | ||
osCache = createCache(cacheSize); | ||
} | ||
|
||
@Override | ||
public Client parse(final String agentString) { | ||
return parseCaching(agentString, clientCache, super::parse); | ||
} | ||
|
||
@Override | ||
public UserAgent parseUserAgent(final String agentString) { | ||
return parseCaching(agentString, userAgentCache, super::parseUserAgent); | ||
} | ||
|
||
@Override | ||
public Device parseDevice(final String agentString) { | ||
return parseCaching(agentString, deviceCache, super::parseDevice); | ||
} | ||
|
||
@Override | ||
public OS parseOS(final String agentString) { | ||
return parseCaching(agentString, osCache, super::parseOS); | ||
} | ||
|
||
private <T> T parseCaching( | ||
final String agentString, | ||
final Cache<String, T> cache, | ||
final Function<String, T> parseFunction) { | ||
if (agentString == null) { | ||
return null; | ||
} | ||
return cache.get(agentString, parseFunction); | ||
} | ||
|
||
private static <T> Cache<String, T> createCache(final long maximumSize) { | ||
return Caffeine.newBuilder() | ||
.maximumSize(maximumSize) | ||
.build(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
...ava/org/opensearch/dataprepper/plugins/processor/useragent/CaffeineCachingParserTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package org.opensearch.dataprepper.plugins.processor.useragent; | ||
|
||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import ua_parser.Client; | ||
import ua_parser.Device; | ||
import ua_parser.OS; | ||
import ua_parser.UserAgent; | ||
|
||
import static org.hamcrest.CoreMatchers.equalTo; | ||
import static org.hamcrest.CoreMatchers.notNullValue; | ||
import static org.hamcrest.CoreMatchers.nullValue; | ||
import static org.hamcrest.CoreMatchers.sameInstance; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
|
||
@SuppressWarnings("StringOperationCanBeSimplified") | ||
class CaffeineCachingParserTest { | ||
private static final String KNOWN_USER_AGENT_STRING = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Mobile/15E148 Safari/604.1"; | ||
long cacheSize; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
cacheSize = 1000; | ||
} | ||
|
||
private CaffeineCachingParser createObjectUnderTest() { | ||
return new CaffeineCachingParser(cacheSize); | ||
} | ||
|
||
@Test | ||
void parse_returns_expected_results() { | ||
final Client client = createObjectUnderTest().parse(KNOWN_USER_AGENT_STRING); | ||
|
||
assertThat(client, notNullValue()); | ||
assertThat(client.userAgent, notNullValue()); | ||
assertThat(client.userAgent.family, equalTo("Mobile Safari")); | ||
assertThat(client.userAgent.major, equalTo("13")); | ||
assertThat(client.device.family, equalTo("iPhone")); | ||
assertThat(client.os.family, equalTo("iOS")); | ||
} | ||
|
||
@Test | ||
void parse_with_null_returns_null() { | ||
assertThat(createObjectUnderTest().parse(null), | ||
nullValue()); | ||
} | ||
|
||
@Test | ||
void parse_called_multiple_times_returns_same_instance() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final String userAgentString = KNOWN_USER_AGENT_STRING; | ||
final Client client = objectUnderTest.parse(userAgentString); | ||
|
||
assertThat(client, notNullValue()); | ||
|
||
assertThat(objectUnderTest.parse(new String(userAgentString)), sameInstance(client)); | ||
assertThat(objectUnderTest.parse(new String(userAgentString)), sameInstance(client)); | ||
assertThat(objectUnderTest.parse(new String(userAgentString)), sameInstance(client)); | ||
} | ||
|
||
@Test | ||
void parseUserAgent_returns_expected_results() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final UserAgent userAgent = objectUnderTest.parseUserAgent(KNOWN_USER_AGENT_STRING); | ||
|
||
assertThat(userAgent, notNullValue()); | ||
assertThat(userAgent.family, equalTo("Mobile Safari")); | ||
assertThat(userAgent.major, equalTo("13")); | ||
} | ||
|
||
@Test | ||
void parseUserAgent_with_null_returns_null() { | ||
assertThat(createObjectUnderTest().parseUserAgent(null), | ||
nullValue()); | ||
} | ||
|
||
@Test | ||
void parseUserAgent_called_multiple_times_returns_same_instance() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final String userAgentString = KNOWN_USER_AGENT_STRING; | ||
final UserAgent userAgent = objectUnderTest.parseUserAgent(userAgentString); | ||
|
||
assertThat(userAgent, notNullValue()); | ||
|
||
assertThat(objectUnderTest.parseUserAgent(new String(userAgentString)), sameInstance(userAgent)); | ||
assertThat(objectUnderTest.parseUserAgent(new String(userAgentString)), sameInstance(userAgent)); | ||
assertThat(objectUnderTest.parseUserAgent(new String(userAgentString)), sameInstance(userAgent)); | ||
} | ||
|
||
@Test | ||
void parseDevice_returns_expected_results() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final Device device = objectUnderTest.parseDevice(KNOWN_USER_AGENT_STRING); | ||
|
||
assertThat(device, notNullValue()); | ||
assertThat(device.family, equalTo("iPhone")); | ||
} | ||
|
||
@Test | ||
void parseDevice_with_null_returns_null() { | ||
assertThat(createObjectUnderTest().parseDevice(null), | ||
nullValue()); | ||
} | ||
|
||
@Test | ||
void parseDevice_called_multiple_times_returns_same_instance() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final String userAgentString = KNOWN_USER_AGENT_STRING; | ||
final Device device = objectUnderTest.parseDevice(userAgentString); | ||
|
||
assertThat(device, notNullValue()); | ||
|
||
assertThat(objectUnderTest.parseDevice(new String(userAgentString)), sameInstance(device)); | ||
assertThat(objectUnderTest.parseDevice(new String(userAgentString)), sameInstance(device)); | ||
assertThat(objectUnderTest.parseDevice(new String(userAgentString)), sameInstance(device)); | ||
} | ||
|
||
@Test | ||
void parseOS_returns_expected_results() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final OS os = objectUnderTest.parseOS(KNOWN_USER_AGENT_STRING); | ||
|
||
assertThat(os, notNullValue()); | ||
assertThat(os.family, equalTo("iOS")); | ||
assertThat(os.major, equalTo("13")); | ||
} | ||
|
||
@Test | ||
void parseOS_with_null_returns_null() { | ||
assertThat(createObjectUnderTest().parseOS(null), | ||
nullValue()); | ||
} | ||
|
||
@Test | ||
void parseOS_called_multiple_times_returns_same_instance() { | ||
final CaffeineCachingParser objectUnderTest = createObjectUnderTest(); | ||
|
||
final String userAgentString = KNOWN_USER_AGENT_STRING; | ||
final OS os = objectUnderTest.parseOS(userAgentString); | ||
|
||
assertThat(os, notNullValue()); | ||
|
||
assertThat(objectUnderTest.parseOS(new String(userAgentString)), sameInstance(os)); | ||
assertThat(objectUnderTest.parseOS(new String(userAgentString)), sameInstance(os)); | ||
assertThat(objectUnderTest.parseOS(new String(userAgentString)), sameInstance(os)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters