[JAVA-19668] (#13721)

* [JAVA-19668] Moved libraries-5 module to jdk9-and-above profile

* [JAVA-19668] Moved libraries-4 module to jdk9-and-above profile

* [JAVA-19668] Moved libraries-6 module to jdk9-and-above profile

* [JAVA-19668] Moved libraries module to jdk9-and-above profile + reverted changes fro libraries-6

* [JAVA-19668] Created libraries-jdk8 module

* [JAVA-19668] Clean up
This commit is contained in:
panos-kakos
2023-04-03 20:22:11 +03:00
committed by GitHub
parent d260f51d8b
commit 61cf039ec1
21 changed files with 275 additions and 119 deletions

View File

@@ -12,8 +12,8 @@ Remember, for advanced libraries like [Jackson](/jackson) and [JUnit](/testing-m
- [Guide to Resilience4j](https://www.baeldung.com/resilience4j)
- [Implementing a FTP-Client in Java](https://www.baeldung.com/java-ftp-client)
- [Introduction to Functional Java](https://www.baeldung.com/java-functional-library)
- [A Guide to the Reflections Library](https://www.baeldung.com/reflections-library)
- [Introduction to Protonpack](https://www.baeldung.com/java-protonpack)
- [Guide to Simple Binary Encoding](https://www.baeldung.com/java-sbe)
- [Java-R Integration](https://www.baeldung.com/java-r-integration)
- [Using libphonenumber to Validate Phone Numbers](https://www.baeldung.com/java-libphonenumber)
- [Apache Commons Collections vs Google Guava](https://www.baeldung.com/apache-commons-collections-vs-guava)

View File

@@ -53,12 +53,6 @@
<version>${mockftpserver.version}</version>
<scope>test</scope>
</dependency>
<!-- Reflections -->
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${reflections.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
@@ -106,6 +100,11 @@
<artifactId>modelmapper</artifactId>
<version>${org.modelmapper.version}</version>
</dependency>
<dependency>
<groupId>org.agrona</groupId>
<artifactId>agrona</artifactId>
<version>1.17.1</version>
</dependency>
</dependencies>
<repositories>
@@ -119,6 +118,60 @@
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<includeProjectDependencies>false</includeProjectDependencies>
<includePluginDependencies>true</includePluginDependencies>
<mainClass>uk.co.real_logic.sbe.SbeTool</mainClass>
<systemProperties>
<systemProperty>
<key>sbe.output.dir</key>
<value>${project.build.directory}/generated-sources/java</value>
</systemProperty>
</systemProperties>
<arguments>
<argument>${project.basedir}/src/main/resources/schema.xml</argument>
</arguments>
<workingDirectory>${project.build.directory}/generated-sources/java</workingDirectory>
</configuration>
<dependencies>
<dependency>
<groupId>uk.co.real-logic</groupId>
<artifactId>sbe-tool</artifactId>
<version>1.27.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/java/</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@@ -137,7 +190,6 @@
<properties>
<javapoet.version>1.10.0</javapoet.version>
<reflections.version>0.9.11</reflections.version>
<mockftpserver.version>2.7.1</mockftpserver.version>
<functionaljava.version>4.8.1</functionaljava.version>
<resilience4j.version>0.12.1</resilience4j.version>

View File

@@ -0,0 +1,98 @@
package com.baeldung.sbe;
import java.util.StringJoiner;
import com.baeldung.sbe.stub.Currency;
import com.baeldung.sbe.stub.Market;
public class MarketData {
private final int amount;
private final double price;
private final Market market;
private final Currency currency;
private final String symbol;
public MarketData(int amount, double price, Market market, Currency currency, String symbol) {
this.amount = amount;
this.price = price;
this.market = market;
this.currency = currency;
this.symbol = symbol;
}
public static class Builder {
private int amount;
public Builder amount(int amount) {
this.amount = amount;
return this;
}
private double price;
public Builder price(double price) {
this.price = price;
return this;
}
private Market market;
public Builder market(Market market) {
this.market = market;
return this;
}
private Currency currency;
public Builder currency(Currency currency) {
this.currency = currency;
return this;
}
private String symbol;
public Builder symbol(String symbol) {
this.symbol = symbol;
return this;
}
public MarketData build() {
return new MarketData(amount, price, market, currency, symbol);
}
}
public static Builder builder() {
return new Builder();
}
public int getAmount() {
return amount;
}
public double getPrice() {
return price;
}
public Market getMarket() {
return market;
}
public Currency getCurrency() {
return currency;
}
public String getSymbol() {
return symbol;
}
@Override
public String toString() {
return new StringJoiner(", ", MarketData.class.getSimpleName() + "[", "]").add("amount=" + amount)
.add("price=" + price)
.add("market=" + market)
.add("currency=" + currency)
.add("symbol='" + symbol + "'")
.toString();
}
}

View File

@@ -0,0 +1,48 @@
package com.baeldung.sbe;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import com.baeldung.sbe.stub.Currency;
import com.baeldung.sbe.stub.Market;
public class MarketDataSource implements Iterator<MarketData> {
private final LinkedList<MarketData> dataQueue = new LinkedList<>();
public MarketDataSource() {
// adding some test data into queue
this.dataQueue.addAll(Arrays.asList(MarketData.builder()
.amount(1)
.market(Market.NASDAQ)
.symbol("AAPL")
.price(134.12)
.currency(Currency.USD)
.build(), MarketData.builder()
.amount(2)
.market(Market.NYSE)
.symbol("IBM")
.price(128.99)
.currency(Currency.USD)
.build(), MarketData.builder()
.amount(1)
.market(Market.NASDAQ)
.symbol("AXP")
.price(34.87)
.currency(Currency.EUR)
.build()));
}
@Override
public boolean hasNext() {
return !this.dataQueue.isEmpty();
}
@Override
public MarketData next() {
final MarketData data = this.dataQueue.pop();
this.dataQueue.add(data);
return data;
}
}

View File

@@ -0,0 +1,92 @@
package com.baeldung.sbe;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MarketDataStreamServer {
private static final Logger log = LoggerFactory.getLogger(MarketDataStreamServer.class);
ByteBuffer buffer = ByteBuffer.allocate(128);
final AtomicLong writePos = new AtomicLong();
ScheduledExecutorService writerThread = Executors.newScheduledThreadPool(1);
ScheduledExecutorService readerThreadPool = Executors.newScheduledThreadPool(2);
private class Client {
final String name;
final ByteBuffer readOnlyBuffer;
final AtomicLong readPos = new AtomicLong();
Client(String name, ByteBuffer source) {
this.name = name;
this.readOnlyBuffer = source.asReadOnlyBuffer();
}
void readTradeData() {
while (readPos.get() < writePos.get()) {
try {
final int pos = this.readOnlyBuffer.position();
final MarketData data = MarketDataUtil.readAndDecode(this.readOnlyBuffer);
readPos.addAndGet(this.readOnlyBuffer.position() - pos);
log.info("<readTradeData> client: {}, read/write gap: {}, data: {}", name, writePos.get() - readPos.get(), data);
} catch (IndexOutOfBoundsException e) {
this.readOnlyBuffer.clear(); // ring buffer
} catch (Exception e) {
log.error("<readTradeData> cannot read from buffer {}", readOnlyBuffer);
}
}
if (this.readOnlyBuffer.remaining() == 0) {
this.readOnlyBuffer.clear(); // ring buffer
}
}
void read() {
readerThreadPool.scheduleAtFixedRate(this::readTradeData, 1, 1, TimeUnit.SECONDS);
}
}
private Client newClient(String name) {
return new Client(name, buffer);
}
private void writeTradeData(MarketData data) {
try {
final int writtenBytes = MarketDataUtil.encodeAndWrite(buffer, data);
writePos.addAndGet(writtenBytes);
log.info("<writeTradeData> buffer size remaining: %{}, data: {}", 100 * buffer.remaining() / buffer.capacity(), data);
} catch (IndexOutOfBoundsException e) {
buffer.clear(); // ring buffer
writeTradeData(data);
} catch (Exception e) {
log.error("<writeTradeData> cannot write into buffer {}", buffer);
}
}
private void run(MarketDataSource source) {
writerThread.scheduleAtFixedRate(() -> {
if (source.hasNext()) {
writeTradeData(source.next());
}
}, 1, 2, TimeUnit.SECONDS);
}
public static void main(String[] args) {
MarketDataStreamServer server = new MarketDataStreamServer();
Client client1 = server.newClient("client1");
client1.read();
Client client2 = server.newClient("client2");
client2.read();
server.run(new MarketDataSource());
}
}

View File

@@ -0,0 +1,78 @@
package com.baeldung.sbe;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import com.baeldung.sbe.stub.MessageHeaderDecoder;
import com.baeldung.sbe.stub.MessageHeaderEncoder;
import com.baeldung.sbe.stub.TradeDataDecoder;
import com.baeldung.sbe.stub.TradeDataEncoder;
public class MarketDataUtil {
public static int encodeAndWrite(ByteBuffer buffer, MarketData marketData) {
final int pos = buffer.position();
final UnsafeBuffer directBuffer = new UnsafeBuffer(buffer);
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
final TradeDataEncoder dataEncoder = new TradeDataEncoder();
final BigDecimal priceDecimal = BigDecimal.valueOf(marketData.getPrice());
final int priceMantis = priceDecimal.scaleByPowerOfTen(priceDecimal.scale())
.intValue();
final int priceExponent = priceDecimal.scale() * -1;
final TradeDataEncoder encoder = dataEncoder.wrapAndApplyHeader(directBuffer, pos, headerEncoder);
encoder.amount(marketData.getAmount());
encoder.quote()
.market(marketData.getMarket())
.currency(marketData.getCurrency())
.symbol(marketData.getSymbol())
.price()
.mantissa(priceMantis)
.exponent((byte) priceExponent);
// set position
final int encodedLength = headerEncoder.encodedLength() + encoder.encodedLength();
buffer.position(pos + encodedLength);
return encodedLength;
}
public static MarketData readAndDecode(ByteBuffer buffer) {
final int pos = buffer.position();
final UnsafeBuffer directBuffer = new UnsafeBuffer(buffer);
final MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
final TradeDataDecoder dataDecoder = new TradeDataDecoder();
dataDecoder.wrapAndApplyHeader(directBuffer, pos, headerDecoder);
// set position
final int encodedLength = headerDecoder.encodedLength() + dataDecoder.encodedLength();
buffer.position(pos + encodedLength);
final double price = BigDecimal.valueOf(dataDecoder.quote()
.price()
.mantissa())
.scaleByPowerOfTen(dataDecoder.quote()
.price()
.exponent())
.doubleValue();
return MarketData.builder()
.amount(dataDecoder.amount())
.symbol(dataDecoder.quote()
.symbol())
.market(dataDecoder.quote()
.market())
.currency(dataDecoder.quote()
.currency())
.price(price)
.build();
}
}

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
package="com.baeldung.sbe.stub" id="1" version="0" semanticVersion="5.2" description="A schema represents stock market data.">
<types>
<composite name="messageHeader" description="Message identifiers and length of message root.">
<type name="blockLength" primitiveType="uint16"/>
<type name="templateId" primitiveType="uint16"/>
<type name="schemaId" primitiveType="uint16"/>
<type name="version" primitiveType="uint16"/>
</composite>
<enum name="Market" encodingType="uint8">
<validValue name="NYSE" description="New York Stock Exchange">0</validValue>
<validValue name="NASDAQ" description="National Association of Securities Dealers Automated Quotations">1</validValue>
</enum>
<type name="Symbol" primitiveType="char" length="4" characterEncoding="ASCII" description="Stock symbol"/>
<composite name="Decimal">
<type name="mantissa" primitiveType="uint64" minValue="0"/>
<type name="exponent" primitiveType="int8"/>
</composite>
<enum name="Currency" encodingType="uint8">
<validValue name="USD" description="US Dollar">0</validValue>
<validValue name="EUR" description="Euro">1</validValue>
</enum>
<composite name="Quote" description="A quote represents the price of a stock in a market">
<ref name="market" type="Market"/>
<ref name="symbol" type="Symbol"/>
<ref name="price" type="Decimal"/>
<ref name="currency" type="Currency"/>
</composite>
</types>
<sbe:message name="TradeData" id="1" description="Represents a quote and amount of trade">
<field name="quote" id="1" type="Quote"/>
<field name="amount" id="2" type="uint16"/>
</sbe:message>
</sbe:messageSchema>

View File

@@ -1,5 +1,6 @@
package com.baeldung.r;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;

View File

@@ -0,0 +1,75 @@
package com.baeldung.test;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import org.agrona.concurrent.UnsafeBuffer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.baeldung.sbe.MarketData;
import com.baeldung.sbe.stub.Currency;
import com.baeldung.sbe.stub.Market;
import com.baeldung.sbe.stub.MessageHeaderDecoder;
import com.baeldung.sbe.stub.MessageHeaderEncoder;
import com.baeldung.sbe.stub.TradeDataDecoder;
import com.baeldung.sbe.stub.TradeDataEncoder;
public class EncodeDecodeMarketDataUnitTest {
private MarketData marketData;
@BeforeEach
public void setup() {
marketData = new MarketData(2, 128.99, Market.NYSE, Currency.USD, "IBM");
}
@Test
public void givenMarketData_whenEncode_thenDecodedValuesMatch() {
// our buffer to write encoded data, initial cap. 128 bytes
UnsafeBuffer buffer = new UnsafeBuffer(ByteBuffer.allocate(128));
// necessary encoders
MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
TradeDataEncoder dataEncoder = new TradeDataEncoder();
// we parse price data (double) into two parts: mantis and exponent
BigDecimal priceDecimal = BigDecimal.valueOf(marketData.getPrice());
int priceMantissa = priceDecimal.scaleByPowerOfTen(priceDecimal.scale())
.intValue();
int priceExponent = priceDecimal.scale() * -1;
// encode data
TradeDataEncoder encoder = dataEncoder.wrapAndApplyHeader(buffer, 0, headerEncoder);
encoder.amount(marketData.getAmount());
encoder.quote()
.market(marketData.getMarket())
.currency(marketData.getCurrency())
.symbol(marketData.getSymbol())
.price()
.mantissa(priceMantissa)
.exponent((byte) priceExponent);
// necessary decoders
MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
TradeDataDecoder dataDecoder = new TradeDataDecoder();
// decode data
dataDecoder.wrapAndApplyHeader(buffer, 0, headerDecoder);
// decode price data (from mantissa and exponent) into a double
double price = BigDecimal.valueOf(dataDecoder.quote()
.price()
.mantissa())
.scaleByPowerOfTen(dataDecoder.quote()
.price()
.exponent())
.doubleValue();
// ensure we have the exact same values
Assertions.assertEquals(2, dataDecoder.amount());
Assertions.assertEquals("IBM", dataDecoder.quote()
.symbol());
Assertions.assertEquals(Market.NYSE, dataDecoder.quote()
.market());
Assertions.assertEquals(Currency.USD, dataDecoder.quote()
.currency());
Assertions.assertEquals(128.99, price);
}
}