BAEL-3446 (#12872)
* BAEL-3446 a simple market data streaming app for the article. * BAEL-3446 add unit tests for the article code. * BAEL-3446 increase version, activate module in the parent pom, update readme. * BAEL-3446 improve the code semantically. * BAEL-3446 remove unnecessary files * BAEL-3446 fix continuation indentation as 2 * BAEL-3446 put new module in the correct profile Co-authored-by: Yavuz Tas <ytas@vwd.com>
This commit is contained in:
		
							parent
							
								
									2879088ee7
								
							
						
					
					
						commit
						125d9893ab
					
				
							
								
								
									
										11
									
								
								libraries-7/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								libraries-7/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | ## Libraries-7 | ||||||
|  | 
 | ||||||
|  | This module contains articles about various Java libraries.  | ||||||
|  | These are small libraries that are relatively easy to use and do not require any separate module of their own. | ||||||
|  | 
 | ||||||
|  | The code examples related to different libraries are each in their own module. | ||||||
|  | 
 | ||||||
|  | Remember, for advanced libraries like [Jackson](/jackson) and [JUnit](/testing-modules) we already have separate modules. Please make sure to have a look at the existing modules in such cases. | ||||||
|  | 
 | ||||||
|  | ### Relevant articles | ||||||
|  | - More articles [[<-- prev]](/libraries-6) | ||||||
							
								
								
									
										81
									
								
								libraries-7/pom.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								libraries-7/pom.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project xmlns="http://maven.apache.org/POM/4.0.0" | ||||||
|  |     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||||
|  |     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||||
|  |     <modelVersion>4.0.0</modelVersion> | ||||||
|  |     <artifactId>libraries-7</artifactId> | ||||||
|  | 
 | ||||||
|  |     <parent> | ||||||
|  |         <artifactId>parent-modules</artifactId> | ||||||
|  |         <groupId>com.baeldung</groupId> | ||||||
|  |         <version>1.0.0-SNAPSHOT</version> | ||||||
|  |     </parent> | ||||||
|  | 
 | ||||||
|  |     <dependencies> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.agrona</groupId> | ||||||
|  |             <artifactId>agrona</artifactId> | ||||||
|  |             <version>1.17.1</version> | ||||||
|  |         </dependency> | ||||||
|  |     </dependencies> | ||||||
|  | 
 | ||||||
|  |     <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> | ||||||
|  |         </plugins> | ||||||
|  |     </build> | ||||||
|  | 
 | ||||||
|  | </project> | ||||||
							
								
								
									
										98
									
								
								libraries-7/src/main/java/com/baeldung/sbe/MarketData.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								libraries-7/src/main/java/com/baeldung/sbe/MarketData.java
									
									
									
									
									
										Normal 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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -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(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								libraries-7/src/main/resources/schema.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								libraries-7/src/main/resources/schema.xml
									
									
									
									
									
										Normal 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> | ||||||
| @ -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); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								pom.xml
									
									
									
									
									
								
							| @ -433,6 +433,7 @@ | |||||||
|                 <module>language-interop</module> |                 <module>language-interop</module> | ||||||
|                 <module>libraries-2</module> |                 <module>libraries-2</module> | ||||||
|                 <module>libraries-3</module> |                 <module>libraries-3</module> | ||||||
|  |                 <module>libraries-7</module> | ||||||
| 
 | 
 | ||||||
|                 <module>libraries-apache-commons</module> |                 <module>libraries-apache-commons</module> | ||||||
|                 <module>libraries-apache-commons-collections</module> |                 <module>libraries-apache-commons-collections</module> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user