the type of the POJO
10 | * @param the type of the POJO field
11 | */
12 | public final class CSVMapping {
13 |
14 | private final BiConsumer mappingConsumer;
15 |
16 | /**
17 | * Instantiates a new {@link CSVMapping}.
18 | *
19 | * @param fieldSetter the field setter {@link BiConsumer}. The first argument is the POJO instance and
20 | * the second argument is the converted field to be set in the instance.
21 | * @param stringToFieldConverter the string to field converter {@link Function}. The return type is the converted
22 | * POJO field instance and the argument is the CSV value {@link String}.
23 | */
24 | public CSVMapping(BiConsumer fieldSetter, Function stringToFieldConverter) {
25 | mappingConsumer = (instance, value) -> fieldSetter.accept(instance, stringToFieldConverter.apply(value));
26 | }
27 |
28 | /**
29 | * Instantiates a new {@link CSVMapping}.
30 | *
31 | * @param csvValueConsumer a {@link BiConsumer} that will takes a POJO instance as the first argument, and a CSV
32 | * {@link String} value as the second argument. This is so that fields inside the POJO can
33 | * be directly set according to the passed in CSV {@link String} value.
34 | */
35 | public CSVMapping(BiConsumer csvValueConsumer) {
36 | mappingConsumer = csvValueConsumer;
37 | }
38 |
39 | /**
40 | * Applies the mapping. Note this could throw a variety of {@link Exception}s.
41 | *
42 | * @param instance the POJO instance
43 | * @param value the CSV value
44 | */
45 | public void apply(T instance, String value) {
46 | mappingConsumer.accept(instance, value);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/schema_json/feed/streaming/level1/enums/summary_update/summary_update_field.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "string",
3 | "title": "Enums for {@link net.jacobpeterson.iqfeed4j.model.feed.streaming.level1.SummaryUpdate} fields for an Update message. See Dynamic Fieldsets",
4 | "enum": [
5 | "Symbol",
6 | "Exchange ID",
7 | "Last",
8 | "Change",
9 | "Percent Change",
10 | "Total Volume",
11 | "High",
12 | "Low",
13 | "Bid",
14 | "Ask",
15 | "Bid Size",
16 | "Ask Size",
17 | "Tick",
18 | "Range",
19 | "Open Interest",
20 | "Open",
21 | "Close",
22 | "Spread",
23 | "Settle",
24 | "Delay",
25 | "Restricted Code",
26 | "Net Asset Value",
27 | "Average Maturity",
28 | "Seven Day Yield",
29 | "Extended Trading Change",
30 | "Extended Trading Difference",
31 | "Price-Earnings Ratio",
32 | "Percent Off Average Volume",
33 | "Bid Change",
34 | "Ask Change",
35 | "Change From Open",
36 | "Market Open",
37 | "Volatility",
38 | "Market Capitalization",
39 | "Fraction Display Code",
40 | "Decimal Precision",
41 | "Days to Expiration",
42 | "Previous Day Volume",
43 | "Open Range 1",
44 | "Close Range 1",
45 | "Open Range 2",
46 | "Close Range 2",
47 | "Number of Trades Today",
48 | "VWAP",
49 | "TickID",
50 | "Financial Status Indicator",
51 | "Settlement Date",
52 | "Bid Market Center",
53 | "Ask Market Center",
54 | "Available Regions",
55 | "Last Size",
56 | "Last Time",
57 | "Last Market Center",
58 | "Most Recent Trade",
59 | "Most Recent Trade Size",
60 | "Most Recent Trade Time",
61 | "Most Recent Trade Conditions",
62 | "Most Recent Trade Market Center",
63 | "Extended Trade",
64 | "Extended Trade Size",
65 | "Extended Trade Time",
66 | "Extended Trade Market Center",
67 | "Message Contents",
68 | "Ask Time",
69 | "Bid Time",
70 | "Last Date",
71 | "Extended Trade Date",
72 | "Most Recent Trade Date",
73 | "Most Recent Trade Aggressor",
74 | "Most Recent Trade Day Code"
75 | ]
76 | }
77 |
--------------------------------------------------------------------------------
/schema_json/feed/streaming/admin/client_statistics.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "title": "Status message that gives information about each client connected to IQFeed and is received once per second after being turned on. See Admin Port System Messages",
4 | "properties": {
5 | "Type": {
6 | "type": "string",
7 | "enum": [
8 | "0",
9 | "1",
10 | "2",
11 | "3"
12 | ],
13 | "javaEnums": [
14 | {
15 | "name": "ADMIN"
16 | },
17 | {
18 | "name": "LEVEL_1"
19 | },
20 | {
21 | "name": "LEVEL_2"
22 | },
23 | {
24 | "name": "LOOKUP"
25 | }
26 | ],
27 | "title": "The type of client."
28 | },
29 | "ClientID": {
30 | "existingJavaType": "java.lang.Integer",
31 | "title": "Numeric identifier (unique by client type) for each client."
32 | },
33 | "ClientName": {
34 | "existingJavaType": "java.lang.String",
35 | "title": "Name of the client. This is set by the 3rd party app using the S,SET CLIENT NAME command on the socket connection it is connected to."
36 | },
37 | "StartTime": {
38 | "existingJavaType": "java.time.LocalDateTime",
39 | "title": "Time the client connected to IQFeed."
40 | },
41 | "Symbols": {
42 | "existingJavaType": "java.lang.Integer",
43 | "title": "Number of symbols being watched by client. Only valid for Level 1 and Level 2 client types."
44 | },
45 | "RegionalSymbols": {
46 | "existingJavaType": "java.lang.Integer",
47 | "title": "Number of regional symbols being watched by client. Only valid for Level 1 client type."
48 | },
49 | "KiloBytesReceived": {
50 | "existingJavaType": "java.lang.Double",
51 | "title": "Number of kilobytes received from the client."
52 | },
53 | "KiloBytesSent": {
54 | "existingJavaType": "java.lang.Double",
55 | "title": "Number of kilobytes sent to the client."
56 | },
57 | "KiloBytesQueued": {
58 | "existingJavaType": "java.lang.Double",
59 | "title": "Number of kilobytes queued by IQConnect waiting to be delivered to the client."
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/message/MultiMessageAccumulator.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.message;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Iterator;
5 | import java.util.List;
6 | import java.util.concurrent.CompletableFuture;
7 | import java.util.concurrent.ExecutionException;
8 |
9 | /**
10 | * This will accumulate all data/messages in memory to be consumed later.
11 | */
12 | public class MultiMessageAccumulator extends MultiMessageListener {
13 |
14 | protected final CompletableFuture completionFuture;
15 | protected final List messages;
16 |
17 | /**
18 | * Instantiates a new {@link MultiMessageAccumulator}.
19 | */
20 | public MultiMessageAccumulator() {
21 | completionFuture = new CompletableFuture<>();
22 | messages = new ArrayList<>();
23 | }
24 |
25 | @Override
26 | public void onMessageReceived(T message) {
27 | messages.add(message);
28 | }
29 |
30 | @Override
31 | public void onMessageException(Exception exception) {
32 | completionFuture.completeExceptionally(exception);
33 | }
34 |
35 | @Override
36 | public void onEndOfMultiMessage() {
37 | completionFuture.complete(null);
38 | }
39 |
40 | /**
41 | * Gets the {@link List} for all the messages in {@link #messages}. Note this will block until
42 | * {@link #onEndOfMultiMessage()} has been called by the underlying feed.
43 | *
44 | * @return an {@link List}
45 | *
46 | * @throws ExecutionException thrown for {@link ExecutionException}s
47 | * @throws InterruptedException thrown for {@link InterruptedException}s
48 | */
49 | public List getMessages() throws ExecutionException, InterruptedException {
50 | completionFuture.get();
51 | return messages;
52 | }
53 |
54 | /**
55 | * Calls {@link #getMessages()} {@link List#iterator()}.
56 | *
57 | * @return an {@link Iterator}
58 | *
59 | * @throws ExecutionException thrown for {@link ExecutionException}s
60 | * @throws InterruptedException thrown for {@link InterruptedException}s
61 | */
62 | public Iterator getIterator() throws ExecutionException, InterruptedException {
63 | return getMessages().iterator();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/DirectListCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
4 |
5 | import java.util.List;
6 | import java.util.function.Function;
7 | import java.util.function.Supplier;
8 |
9 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
10 |
11 | /**
12 | * {@link DirectListCSVMapper} maps a CSV list to a {@link List} using a direct type conversion.
13 | */
14 | public class DirectListCSVMapper extends AbstractListCSVMapper {
15 |
16 | protected final Supplier extends List> listInstantiator;
17 | protected final Function stringToTypeConverter;
18 |
19 | /**
20 | * Instantiates a new {@link DirectListCSVMapper}.
21 | *
22 | * @param listInstantiator a {@link Supplier} to instantiate a new {@link List}
23 | * @param stringToTypeConverter a {@link Function} that will takes a CSV value {@link String} as the argument, and
24 | * returns the converted CSV value type.
25 | */
26 | public DirectListCSVMapper(Supplier extends List> listInstantiator,
27 | Function stringToTypeConverter) {
28 | super(null);
29 |
30 | this.listInstantiator = listInstantiator;
31 | this.stringToTypeConverter = stringToTypeConverter;
32 | }
33 |
34 | /**
35 | *
36 | * Note: this will map to a list with the {@link #stringToTypeConverter} applied.
37 | */
38 | @Override
39 | public List mapToList(String[] csv, int offset) {
40 | List mappedList = listInstantiator.get();
41 |
42 | for (int csvIndex = offset; csvIndex < csv.length; csvIndex++) {
43 | if (!valueNotWhitespace(csv, csvIndex)) { // Add null for empty CSV values
44 | mappedList.add(null);
45 | continue;
46 | }
47 |
48 | // accept() could throw a variety of exceptions
49 | try {
50 | mappedList.add(stringToTypeConverter.apply(csv[csvIndex]));
51 | } catch (Exception exception) {
52 | throw new CSVMappingException(csvIndex - offset, offset, exception);
53 | }
54 | }
55 |
56 | return mappedList;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/story/NewsStory.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.story;
2 |
3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
4 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
5 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers.NewsHeadlineSymbolsDeserializer;
6 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers.NewsStoryIsLinkDeserializer;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * {@link NewsStory} represents a news story from a news feed.
12 | */
13 | public class NewsStory {
14 |
15 | @JacksonXmlProperty(localName = "is_link")
16 | @JsonDeserialize(using = NewsStoryIsLinkDeserializer.class)
17 | protected Boolean isLink;
18 | @JacksonXmlProperty(localName = "story_text")
19 | protected String text;
20 | @JacksonXmlProperty(localName = "symbols")
21 | @JsonDeserialize(using = NewsHeadlineSymbolsDeserializer.class)
22 | protected List symbols;
23 |
24 | /**
25 | * Instantiates a new {@link NewsStory}.
26 | */
27 | public NewsStory() {}
28 |
29 | /**
30 | * Gets {@link #isLink}.
31 | *
32 | * @return the link
33 | */
34 | public Boolean isLink() {
35 | return isLink;
36 | }
37 |
38 | /**
39 | * Sets {@link #isLink}.
40 | *
41 | * @param link the link
42 | */
43 | public void setLink(Boolean link) {
44 | isLink = link;
45 | }
46 |
47 | /**
48 | * Gets {@link #text}.
49 | *
50 | * @return the text
51 | */
52 | public String getText() {
53 | return text;
54 | }
55 |
56 | /**
57 | * Sets {@link #text}.
58 | *
59 | * @param text the text
60 | */
61 | public void setText(String text) {
62 | this.text = text;
63 | }
64 |
65 | /**
66 | * Gets {@link #symbols}.
67 | *
68 | * @return the symbols
69 | */
70 | public List getSymbols() {
71 | return symbols;
72 | }
73 |
74 | /**
75 | * Sets {@link #symbols}.
76 | *
77 | * @param symbols the symbols
78 | */
79 | public void setSymbols(List symbols) {
80 | this.symbols = symbols;
81 | }
82 |
83 | @Override
84 | public String toString() {
85 | return "NewsStory{" +
86 | "isLink=" + isLink +
87 | ", text='" + text + '\'' +
88 | ", symbols=" + symbols +
89 | '}';
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/schema_json/feed/lookup/historical/tick.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "title": "POJO for HTX, HTD, and HTT requests. See HistoricalViaTCPIP",
4 | "properties": {
5 | "Timestamp": {
6 | "existingJavaType": "java.time.LocalDateTime",
7 | "title": "The timestamp."
8 | },
9 | "Last": {
10 | "existingJavaType": "java.lang.Double",
11 | "title": "The last price."
12 | },
13 | "LastSize": {
14 | "existingJavaType": "java.lang.Long",
15 | "title": "The last size."
16 | },
17 | "TotalVolume": {
18 | "existingJavaType": "java.lang.Long",
19 | "title": "The total volume."
20 | },
21 | "Bid": {
22 | "existingJavaType": "java.lang.Double",
23 | "title": "The bid price."
24 | },
25 | "Ask": {
26 | "existingJavaType": "java.lang.Double",
27 | "title": "The ask price."
28 | },
29 | "TickID": {
30 | "existingJavaType": "java.lang.Long",
31 | "title": "The tick ID."
32 | },
33 | "BasisForLast": {
34 | "type": "string",
35 | "enum": [
36 | "C",
37 | "E",
38 | "O",
39 | "S"
40 | ],
41 | "javaEnums": [
42 | {
43 | "name": "LAST_QUALIFIED_TRADE"
44 | },
45 | {
46 | "name": "EXTENDED_TRADE"
47 | },
48 | {
49 | "name": "OTHER_TRADE"
50 | },
51 | {
52 | "name": "SETTLE"
53 | }
54 | ],
55 | "title": "The basis for last trade."
56 | },
57 | "TradeMarketCenter": {
58 | "existingJavaType": "java.lang.Short",
59 | "title": "The Market Center the trade occurred at."
60 | },
61 | "TradeConditions": {
62 | "existingJavaType": "java.util.List",
63 | "title": "The trade conditions. Format: One to four, 2 digit hex numbers."
64 | },
65 | "TradeAggressor": {
66 | "type": "string",
67 | "enum": [
68 | "0",
69 | "1",
70 | "2",
71 | "3"
72 | ],
73 | "javaEnums": [
74 | {
75 | "name": "INVALID"
76 | },
77 | {
78 | "name": "BUY"
79 | },
80 | {
81 | "name": "SELL"
82 | },
83 | {
84 | "name": "NEITHER_BUY_NOR_SELL"
85 | }
86 | ],
87 | "title": "The trade aggressor."
88 | },
89 | "DayCode": {
90 | "existingJavaType": "java.lang.Integer",
91 | "title": "The day of month the trade applies to."
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/schema_json/feed/streaming/level1/enums/log_level/log_level.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "string",
3 | "title": "Enums for log levels. See IQConnect Logging",
4 | "enum": [
5 | "Admin",
6 | "L1Data",
7 | "L1Request",
8 | "L1System",
9 | "L1Error",
10 | "L2Data",
11 | "L2Request",
12 | "L2System",
13 | "L2Error",
14 | "LookupData",
15 | "LookupRequest",
16 | "LookupError",
17 | "Information",
18 | "Debug",
19 | "Connectivity"
20 | ],
21 | "javaEnums": [
22 | {
23 | "title": "Admin log entries are non-data and non-request related log entries. Enabled by default."
24 | },
25 | {
26 | "title": "L1Data log entries consists of all data transferred over the Level 1 Port."
27 | },
28 | {
29 | "title": "L1Request log entries consists of all requests received from client apps on the Level 1 Port."
30 | },
31 | {
32 | "title": "L1System log entries consists of all system messages received from and sent to the client on the Level 1 Port."
33 | },
34 | {
35 | "title": "L1Error log entries consists of all errors sent to the client on the Level 1 Port."
36 | },
37 | {
38 | "title": "L2Data log entries consists of all data transferred over the Level 2 Port."
39 | },
40 | {
41 | "title": "L2Request log entries consists of all requests received from client apps on the Level 2 Port."
42 | },
43 | {
44 | "title": "L2System log entries consists of all system messages received from and sent to the client on the Level 2 Port."
45 | },
46 | {
47 | "title": "L2Error log entries consists of all errors sent to the client on the Level 2 Port."
48 | },
49 | {
50 | "title": "LookupData log entries consists of all data transferred over the Lookup Port."
51 | },
52 | {
53 | "title": "LookupRequest log entries consists of all requests received from client apps on the Lookup Port."
54 | },
55 | {
56 | "title": "LookupError log entries consists of all errors sent to the client on the Lookup Port."
57 | },
58 | {
59 | "title": "Information logging enables basic information, such as username and product id to be logged to the file. Enabled by default."
60 | },
61 | {
62 | "title": "Debug logs enable more granular logging to aid in troubleshooting issues."
63 | },
64 | {
65 | "title": "Connectivity log entries are related to the connection/reconnection between IQConnect and the server. Enabled by default."
66 | }
67 | ]
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/AbstractServerConnectionFeed.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.streaming;
2 |
3 | import com.google.common.base.Splitter;
4 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed;
5 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType;
6 | import net.jacobpeterson.iqfeed4j.model.feed.streaming.common.enums.ServerConnectionStatus;
7 | import org.slf4j.Logger;
8 |
9 | /**
10 | * {@link AbstractServerConnectionFeed} is an {@link AbstractFeed} for feeds that receive the S,SERVER
11 | * CONNECTED>LF< or S,SERVER DISCONNECTED>LF< messages.
12 | */
13 | public abstract class AbstractServerConnectionFeed extends AbstractFeed {
14 |
15 | protected ServerConnectionStatus serverConnectionStatus;
16 |
17 | /**
18 | * Instantiates a new {@link AbstractServerConnectionFeed}.
19 | *
20 | * @param logger the {@link Logger}
21 | * @param feedName the feed name
22 | * @param hostname the hostname
23 | * @param port the port
24 | * @param csvSplitter the CSV {@link Splitter}
25 | * @param validateProtocolVersion true to send and validate the {@link #CURRENTLY_SUPPORTED_PROTOCOL_VERSION}
26 | * @param sendClientName true to send the client feedName
27 | */
28 | public AbstractServerConnectionFeed(Logger logger, String feedName, String hostname, int port, Splitter csvSplitter,
29 | boolean validateProtocolVersion, boolean sendClientName) {
30 | super(logger, feedName, hostname, port, csvSplitter, validateProtocolVersion, sendClientName);
31 | }
32 |
33 | /**
34 | * Checks and sets {@link #serverConnectionStatus} if a {@link ServerConnectionStatus} is present. Note this will
35 | * not check if the CSV message is a {@link FeedMessageType#SYSTEM} message so that should be done before calling
36 | * this method.
37 | *
38 | * @param systemMessageType the {@link FeedMessageType#SYSTEM} message type
39 | *
40 | * @return true if the message was a {@link ServerConnectionStatus} message, false otherwise
41 | */
42 | protected boolean checkServerConnectionStatusMessage(String systemMessageType) {
43 | try {
44 | serverConnectionStatus = ServerConnectionStatus.fromValue(systemMessageType);
45 |
46 | if (serverConnectionStatus == ServerConnectionStatus.SERVER_CONNECTED) {
47 | logger.info("Server is connected.");
48 | } else {
49 | logger.warn("Server is disconnected!");
50 | }
51 |
52 | return true;
53 | } catch (IllegalArgumentException ignored) {
54 | return false;
55 | }
56 | }
57 |
58 | /**
59 | * Gets {@link #serverConnectionStatus}.
60 | *
61 | * @return the {@link ServerConnectionStatus}
62 | */
63 | public ServerConnectionStatus getServerConnectionStatus() {
64 | return serverConnectionStatus;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/StreamingCSVMappers.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.streaming;
2 |
3 | import net.jacobpeterson.iqfeed4j.model.feed.streaming.common.FeedStatistics;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper;
5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.index.IndexCSVMapper;
6 |
7 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.DateTimeConverters.MONTH3_DAY_TIME_AM_PM;
8 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.DOUBLE;
9 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.INTEGER;
10 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.STRING;
11 |
12 | /**
13 | * {@link StreamingCSVMappers} contains {@link AbstractCSVMapper}s for streaming feeds.
14 | */
15 | public final class StreamingCSVMappers {
16 |
17 | /**
18 | * A {@link IndexCSVMapper} for {@link FeedStatistics}.
19 | */
20 | public static final IndexCSVMapper FEED_STATISTICS_CSV_MAPPER;
21 |
22 | static {
23 | FEED_STATISTICS_CSV_MAPPER = new IndexCSVMapper<>(FeedStatistics::new);
24 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setServerIP, STRING);
25 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setServerPort, INTEGER);
26 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setMaxSymbols, INTEGER);
27 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setNumberOfSymbols, INTEGER);
28 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setClientsConnected, INTEGER);
29 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setSecondsSinceLastUpdate, INTEGER);
30 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setReconnections, INTEGER);
31 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setAttemptedReconnections, INTEGER);
32 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setStartTime, MONTH3_DAY_TIME_AM_PM);
33 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setMarketTime, MONTH3_DAY_TIME_AM_PM);
34 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setStatus, FeedStatistics.Status::fromValue);
35 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setIQFeedVersion, STRING);
36 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setLoginID, STRING);
37 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setTotalKiloBytesReceived, DOUBLE);
38 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setKiloBytesPerSecReceived, DOUBLE);
39 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setAvgKiloBytesPerSecRecv, DOUBLE);
40 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setTotalKiloBytesSent, DOUBLE);
41 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setKiloBytesPerSecSent, DOUBLE);
42 | FEED_STATISTICS_CSV_MAPPER.addMapping(FeedStatistics::setAvgKiloBytesPerSecSent, DOUBLE);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/ListCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
4 |
5 | import java.util.List;
6 | import java.util.function.BiConsumer;
7 | import java.util.function.Supplier;
8 | import java.util.regex.Pattern;
9 |
10 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
11 |
12 | /**
13 | * {@link ListCSVMapper} maps a CSV list to a POJO {@link List}.
14 | */
15 | public class ListCSVMapper extends AbstractListCSVMapper {
16 |
17 | protected final Supplier extends List> listInstantiator;
18 | protected final BiConsumer csvValueConsumer;
19 | protected final Pattern skipPattern;
20 |
21 | /**
22 | * Instantiates a new {@link ListCSVMapper}.
23 | *
24 | * @param listInstantiator a {@link Supplier} to instantiate a new {@link List}
25 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO
26 | * @param csvValueConsumer a {@link BiConsumer} that takes a new POJO instance as the first argument, and the CSV
27 | * list {@link String} value as the second argument. This is so that fields inside the POJO
28 | * can be set according to the passed in CSV list {@link String} value.
29 | * @param skipPattern when a CSV value matches this given {@link Pattern} in {@link #mapToList(String[], int)},
30 | * then it is skipped (null to not check)
31 | */
32 | public ListCSVMapper(Supplier extends List> listInstantiator, Supplier pojoInstantiator,
33 | BiConsumer csvValueConsumer, Pattern skipPattern) {
34 | super(pojoInstantiator);
35 |
36 | this.listInstantiator = listInstantiator;
37 | this.csvValueConsumer = csvValueConsumer;
38 | this.skipPattern = skipPattern;
39 | }
40 |
41 | /**
42 | *
43 | * Note: this will map to a list of POJOs with the {@link #csvValueConsumer} applied.
44 | */
45 | @Override
46 | public List mapToList(String[] csv, int offset) {
47 | List mappedList = listInstantiator.get();
48 |
49 | for (int csvIndex = offset; csvIndex < csv.length; csvIndex++) {
50 | if (!valueNotWhitespace(csv, csvIndex)) { // Add null for empty CSV values
51 | mappedList.add(null);
52 | continue;
53 | }
54 |
55 | // Skip over matched 'skipPattern's
56 | if (skipPattern != null && skipPattern.matcher(csv[csvIndex]).matches()) {
57 | continue;
58 | }
59 |
60 | T instance = pojoInstantiator.get();
61 |
62 | // accept() could throw a variety of exceptions
63 | try {
64 | csvValueConsumer.accept(instance, csv[csvIndex]);
65 | mappedList.add(instance);
66 | } catch (Exception exception) {
67 | throw new CSVMappingException(csvIndex - offset, offset, exception);
68 | }
69 | }
70 |
71 | return mappedList;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo. 1>&2
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
48 | echo. 1>&2
49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
50 | echo location of your Java installation. 1>&2
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo. 1>&2
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
62 | echo. 1>&2
63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
64 | echo location of your Java installation. 1>&2
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/schema_json/feed/lookup/market_summary/end_of_day_snapshot.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "title": "POJO for EDS (End of Day Summary) requests. See Market Summary via TCP/IP",
4 | "properties": {
5 | "Symbol": {
6 | "existingJavaType": "java.lang.String"
7 | },
8 | "Exchange": {
9 | "existingJavaType": "java.lang.Integer"
10 | },
11 | "Type": {
12 | "existingJavaType": "java.lang.Integer"
13 | },
14 | "Last": {
15 | "existingJavaType": "java.lang.Double"
16 | },
17 | "TradeSize": {
18 | "existingJavaType": "java.lang.Double"
19 | },
20 | "TradedMarket": {
21 | "existingJavaType": "java.lang.Integer"
22 | },
23 | "TradeDate": {
24 | "existingJavaType": "java.time.LocalDate"
25 | },
26 | "TradeTime": {
27 | "existingJavaType": "java.time.LocalTime"
28 | },
29 | "Open": {
30 | "existingJavaType": "java.lang.Double"
31 | },
32 | "High": {
33 | "existingJavaType": "java.lang.Double"
34 | },
35 | "Low": {
36 | "existingJavaType": "java.lang.Double"
37 | },
38 | "Close": {
39 | "existingJavaType": "java.lang.Double"
40 | },
41 | "Bid": {
42 | "existingJavaType": "java.lang.Double"
43 | },
44 | "BidMarket": {
45 | "existingJavaType": "java.lang.Integer"
46 | },
47 | "BidSize": {
48 | "existingJavaType": "java.lang.Double"
49 | },
50 | "Ask": {
51 | "existingJavaType": "java.lang.Double"
52 | },
53 | "AskMarket": {
54 | "existingJavaType": "java.lang.Integer"
55 | },
56 | "AskSize": {
57 | "existingJavaType": "java.lang.Double"
58 | },
59 | "Volume": {
60 | "existingJavaType": "java.lang.Double"
61 | },
62 | "PDayVolume": {
63 | "existingJavaType": "java.lang.Double"
64 | },
65 | "UpVolume": {
66 | "existingJavaType": "java.lang.Double"
67 | },
68 | "DownVolume": {
69 | "existingJavaType": "java.lang.Double"
70 | },
71 | "NeutralVolume": {
72 | "existingJavaType": "java.lang.Double"
73 | },
74 | "TradeCount": {
75 | "existingJavaType": "java.lang.Double"
76 | },
77 | "UpTrades": {
78 | "existingJavaType": "java.lang.Double"
79 | },
80 | "DownTrades": {
81 | "existingJavaType": "java.lang.Double"
82 | },
83 | "NeutralTrades": {
84 | "existingJavaType": "java.lang.Double"
85 | },
86 | "VWAP": {
87 | "existingJavaType": "java.lang.Double"
88 | },
89 | "MutualDiv": {
90 | "existingJavaType": "java.lang.Double"
91 | },
92 | "SevenDayYield": {
93 | "existingJavaType": "java.lang.Double"
94 | },
95 | "OpenInterest": {
96 | "existingJavaType": "java.lang.Double"
97 | },
98 | "Settlement": {
99 | "existingJavaType": "java.lang.Double"
100 | },
101 | "SettlementDate": {
102 | "existingJavaType": "java.time.LocalDate"
103 | },
104 | "ExpirationDate": {
105 | "existingJavaType": "java.time.LocalDate"
106 | },
107 | "Strike": {
108 | "existingJavaType": "java.lang.Double"
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/schema_json/feed/lookup/market_summary/five_minute_snapshot.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "title": "POJO for 5MS (5 Minute Snapshot Summary) requests. See Market Summary via TCP/IP",
4 | "properties": {
5 | "Symbol": {
6 | "existingJavaType": "java.lang.String"
7 | },
8 | "Exchange": {
9 | "existingJavaType": "java.lang.Integer"
10 | },
11 | "Type": {
12 | "existingJavaType": "java.lang.Integer"
13 | },
14 | "Last": {
15 | "existingJavaType": "java.lang.Double"
16 | },
17 | "TradeSize": {
18 | "existingJavaType": "java.lang.Double"
19 | },
20 | "TradedMarket": {
21 | "existingJavaType": "java.lang.Integer"
22 | },
23 | "TradeDate": {
24 | "existingJavaType": "java.time.LocalDate"
25 | },
26 | "TradeTime": {
27 | "existingJavaType": "java.time.LocalTime"
28 | },
29 | "Open": {
30 | "existingJavaType": "java.lang.Double"
31 | },
32 | "High": {
33 | "existingJavaType": "java.lang.Double"
34 | },
35 | "Low": {
36 | "existingJavaType": "java.lang.Double"
37 | },
38 | "Close": {
39 | "existingJavaType": "java.lang.Double"
40 | },
41 | "Bid": {
42 | "existingJavaType": "java.lang.Double"
43 | },
44 | "BidMarket": {
45 | "existingJavaType": "java.lang.Integer"
46 | },
47 | "BidSize": {
48 | "existingJavaType": "java.lang.Double"
49 | },
50 | "Ask": {
51 | "existingJavaType": "java.lang.Double"
52 | },
53 | "AskMarket": {
54 | "existingJavaType": "java.lang.Integer"
55 | },
56 | "AskSize": {
57 | "existingJavaType": "java.lang.Double"
58 | },
59 | "Volume": {
60 | "existingJavaType": "java.lang.Double"
61 | },
62 | "PDayVolume": {
63 | "existingJavaType": "java.lang.Double"
64 | },
65 | "UpVolume": {
66 | "existingJavaType": "java.lang.Double"
67 | },
68 | "DownVolume": {
69 | "existingJavaType": "java.lang.Double"
70 | },
71 | "NeutralVolume": {
72 | "existingJavaType": "java.lang.Double"
73 | },
74 | "TradeCount": {
75 | "existingJavaType": "java.lang.Double"
76 | },
77 | "UpTrades": {
78 | "existingJavaType": "java.lang.Double"
79 | },
80 | "DownTrades": {
81 | "existingJavaType": "java.lang.Double"
82 | },
83 | "NeutralTrades": {
84 | "existingJavaType": "java.lang.Double"
85 | },
86 | "VWAP": {
87 | "existingJavaType": "java.lang.Double"
88 | },
89 | "MutualDiv": {
90 | "existingJavaType": "java.lang.Double"
91 | },
92 | "SevenDayYield": {
93 | "existingJavaType": "java.lang.Double"
94 | },
95 | "OpenInterest": {
96 | "existingJavaType": "java.lang.Double"
97 | },
98 | "Settlement": {
99 | "existingJavaType": "java.lang.Double"
100 | },
101 | "SettlementDate": {
102 | "existingJavaType": "java.time.LocalDate"
103 | },
104 | "ExpirationDate": {
105 | "existingJavaType": "java.time.LocalDate"
106 | },
107 | "Strike": {
108 | "existingJavaType": "java.lang.Double"
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/configuration/NewsSource.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.configuration;
2 |
3 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
4 |
5 | /**
6 | * {@link NewsSource} represents a type of news source.
7 | */
8 | public class NewsSource {
9 |
10 | @JacksonXmlProperty(localName = "type", isAttribute = true)
11 | protected String id;
12 | @JacksonXmlProperty(localName = "name", isAttribute = true)
13 | protected String name;
14 | @JacksonXmlProperty(localName = "auth_code", isAttribute = true)
15 | protected String authCode;
16 | @JacksonXmlProperty(localName = "icon_id", isAttribute = true)
17 | protected Integer iconID;
18 | @JacksonXmlProperty(localName = "fixed_page", isAttribute = true)
19 | protected Boolean fixedPage;
20 |
21 | /**
22 | * Instantiates a new {@link NewsSource}.
23 | */
24 | public NewsSource() {}
25 |
26 | /**
27 | * Gets {@link #id}.
28 | *
29 | * @return the ID
30 | */
31 | public String getID() {
32 | return id;
33 | }
34 |
35 | /**
36 | * Sets {@link #id}.
37 | *
38 | * @param id the ID
39 | */
40 | public void setID(String id) {
41 | this.id = id;
42 | }
43 |
44 | /**
45 | * Gets {@link #name}.
46 | *
47 | * @return the name
48 | */
49 | public String getName() {
50 | return name;
51 | }
52 |
53 | /**
54 | * Sets {@link #name}.
55 | *
56 | * @param name the name
57 | */
58 | public void setName(String name) {
59 | this.name = name;
60 | }
61 |
62 | /**
63 | * Gets {@link #authCode}.
64 | *
65 | * @return the auth code
66 | */
67 | public String getAuthCode() {
68 | return authCode;
69 | }
70 |
71 | /**
72 | * Sets {@link #authCode}.
73 | *
74 | * @param authCode the auth code
75 | */
76 | public void setAuthCode(String authCode) {
77 | this.authCode = authCode;
78 | }
79 |
80 | /**
81 | * Gets {@link #iconID}.
82 | *
83 | * @return the icon ID
84 | */
85 | public Integer getIconID() {
86 | return iconID;
87 | }
88 |
89 | /**
90 | * Sets {@link #iconID}.
91 | *
92 | * @param iconID the icon ID
93 | */
94 | public void setIconID(Integer iconID) {
95 | this.iconID = iconID;
96 | }
97 |
98 | /**
99 | * Gets {@link #fixedPage}.
100 | *
101 | * @return the fixed page
102 | */
103 | public Boolean isFixedPage() {
104 | return fixedPage;
105 | }
106 |
107 | /**
108 | * Sets {@link #fixedPage}.
109 | *
110 | * @param fixedPage the fixed page
111 | */
112 | public void setFixedPage(Boolean fixedPage) {
113 | this.fixedPage = fixedPage;
114 | }
115 |
116 | @Override
117 | public String toString() {
118 | return "NewsSource{" +
119 | "id='" + id + '\'' +
120 | ", name='" + name + '\'' +
121 | ", authCode='" + authCode + '\'' +
122 | ", iconID=" + iconID +
123 | ", fixedPage=" + fixedPage +
124 | '}';
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/news/xml/headline/NewsHeadline.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.headline;
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat;
4 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
5 | import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
6 | import net.jacobpeterson.iqfeed4j.feed.lookup.news.xml.deserializers.NewsHeadlineSymbolsDeserializer;
7 |
8 | import java.time.LocalDateTime;
9 | import java.util.List;
10 |
11 | /**
12 | * {@link NewsHeadline} represents a news headline from a news feed.
13 | */
14 | public class NewsHeadline {
15 |
16 | @JacksonXmlProperty(localName = "id")
17 | protected String id;
18 | @JacksonXmlProperty(localName = "source")
19 | protected String source;
20 | @JacksonXmlProperty(localName = "timestamp")
21 | @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyyMMddHHmmss")
22 | protected LocalDateTime timestamp;
23 | @JacksonXmlProperty(localName = "symbols")
24 | @JsonDeserialize(using = NewsHeadlineSymbolsDeserializer.class)
25 | protected List symbols;
26 | @JacksonXmlProperty(localName = "text")
27 | protected String text;
28 |
29 | /**
30 | * Instantiates a new {@link NewsHeadline}.
31 | */
32 | public NewsHeadline() {}
33 |
34 | /**
35 | * Gets {@link #id}.
36 | *
37 | * @return the ID
38 | */
39 | public String getID() {
40 | return id;
41 | }
42 |
43 | /**
44 | * Sets {@link #id}.
45 | *
46 | * @param id the ID
47 | */
48 | public void setID(String id) {
49 | this.id = id;
50 | }
51 |
52 | /**
53 | * Gets {@link #source}.
54 | *
55 | * @return the source
56 | */
57 | public String getSource() {
58 | return source;
59 | }
60 |
61 | /**
62 | * Sets {@link #source}.
63 | *
64 | * @param source the source
65 | */
66 | public void setSource(String source) {
67 | this.source = source;
68 | }
69 |
70 | /**
71 | * Gets {@link #timestamp}.
72 | *
73 | * @return the timestamp
74 | */
75 | public LocalDateTime getTimestamp() {
76 | return timestamp;
77 | }
78 |
79 | /**
80 | * Sets {@link #timestamp}.
81 | *
82 | * @param timestamp the timestamp
83 | */
84 | public void setTimestamp(LocalDateTime timestamp) {
85 | this.timestamp = timestamp;
86 | }
87 |
88 | /**
89 | * Gets {@link #symbols}.
90 | *
91 | * @return the symbols
92 | */
93 | public List getSymbols() {
94 | return symbols;
95 | }
96 |
97 | /**
98 | * Sets {@link #symbols}.
99 | *
100 | * @param symbols the symbols
101 | */
102 | public void setSymbols(List symbols) {
103 | this.symbols = symbols;
104 | }
105 |
106 | /**
107 | * Gets {@link #text}.
108 | *
109 | * @return the text
110 | */
111 | public String getText() {
112 | return text;
113 | }
114 |
115 | /**
116 | * Sets {@link #text}.
117 | *
118 | * @param text the text
119 | */
120 | public void setText(String text) {
121 | this.text = text;
122 | }
123 |
124 | @Override
125 | public String toString() {
126 | return "NewsHeadline{" +
127 | "id='" + id + '\'' +
128 | ", source='" + source + '\'' +
129 | ", timestamp=" + timestamp +
130 | ", symbols=" + symbols +
131 | ", text='" + text + '\'' +
132 | '}';
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/schema_json/feed/streaming/common/feed_statistics.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "title": "Status message that gives information about the feed operation and is received once per second. See Admin Port System Messages",
4 | "properties": {
5 | "ServerIP": {
6 | "existingJavaType": "java.lang.String",
7 | "title": "IP address for least loaded Quote Server."
8 | },
9 | "ServerPort": {
10 | "existingJavaType": "java.lang.Integer",
11 | "title": "Port for least loaded Quote Server."
12 | },
13 | "MaxSymbols": {
14 | "existingJavaType": "java.lang.Integer",
15 | "title": "The maximum # of symbols that can be watched at any given time."
16 | },
17 | "NumberOfSymbols": {
18 | "existingJavaType": "java.lang.Integer",
19 | "title": "The # of symbols that are currently being watched."
20 | },
21 | "ClientsConnected": {
22 | "existingJavaType": "java.lang.Integer",
23 | "title": "The # of clients that are currently connected."
24 | },
25 | "SecondsSinceLastUpdate": {
26 | "existingJavaType": "java.lang.Integer",
27 | "title": "The # of seconds since the last update from the Quote server ."
28 | },
29 | "Reconnections": {
30 | "existingJavaType": "java.lang.Integer",
31 | "title": "The # of times that IQFeed has reconnected."
32 | },
33 | "AttemptedReconnections": {
34 | "existingJavaType": "java.lang.Integer",
35 | "title": "The # of times that IQFeed has attempted to reconnect, but failed."
36 | },
37 | "StartTime": {
38 | "existingJavaType": "java.time.LocalDateTime",
39 | "title": "The time that the connection (or reconnection) to IQFeed was made."
40 | },
41 | "MarketTime": {
42 | "existingJavaType": "java.time.LocalDateTime",
43 | "title": "The current time of the market."
44 | },
45 | "Status": {
46 | "type": "string",
47 | "enum": [
48 | "Connected",
49 | "Not Connected"
50 | ],
51 | "title": "Represents whether IQFeed is connected or not."
52 | },
53 | "IQFeedVersion": {
54 | "existingJavaType": "java.lang.String",
55 | "title": "Represents the version of IQFeed that is running."
56 | },
57 | "LoginID": {
58 | "existingJavaType": "java.lang.String",
59 | "title": "The Login ID that is currently logged in."
60 | },
61 | "TotalKiloBytesReceived": {
62 | "existingJavaType": "java.lang.Double",
63 | "title": "Found in the \"Internet Bandwidth\" section of the IQConnection Manager. Formula: total bytes received / 1024."
64 | },
65 | "KiloBytesPerSecReceived": {
66 | "existingJavaType": "java.lang.Double",
67 | "title": "Found in the \"Internet Bandwidth\" section of the IQConnection Manager. Formula: bytes received in the past second / 1024."
68 | },
69 | "AvgKiloBytesPerSecRecv": {
70 | "existingJavaType": "java.lang.Double",
71 | "title": "Found in the \"Internet Bandwidth\" section of the IQConnection Manager. Formula: total KB's received / total seconds."
72 | },
73 | "TotalKiloBytesSent": {
74 | "existingJavaType": "java.lang.Double",
75 | "title": "Found in the \"Local Bandwidth\" section of the IQConnection Manager. Formula: total bytes sent / 1024."
76 | },
77 | "KiloBytesPerSecSent": {
78 | "existingJavaType": "java.lang.Double",
79 | "title": "Found in the \"Local Bandwidth\" section of the IQConnection Manager. Formula: bytes sent in the past second / 1024."
80 | },
81 | "AvgKiloBytesPerSecSent": {
82 | "existingJavaType": "java.lang.Double",
83 | "title": "Found in the \"Local Bandwidth\" section of the IQConnection Manager. Formula: total KB's sent / total seconds"
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/RequestIDFeedHelper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed;
2 |
3 | import net.jacobpeterson.iqfeed4j.feed.lookup.AbstractLookupFeed;
4 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType;
5 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedSpecialMessage;
6 |
7 | import java.util.Collections;
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueEquals;
12 |
13 | /**
14 | * {@link RequestIDFeedHelper} is a helper for an {@link AbstractFeed} that uses Request IDs.
15 | */
16 | public class RequestIDFeedHelper {
17 |
18 | private final Set requestIDs;
19 |
20 | /**
21 | * Instantiates a new {@link AbstractLookupFeed}.
22 | */
23 | public RequestIDFeedHelper() {
24 | requestIDs = Collections.synchronizedSet(new HashSet<>());
25 | }
26 |
27 | /**
28 | * Checks for a request ID error message format.
29 | *
30 | * e.g. [Request ID], E, <Error Text>
31 | *
32 | * @param csv the CSV
33 | * @param requestID the request ID
34 | *
35 | * @return true if the CSV represents an {@link FeedMessageType#ERROR} message
36 | */
37 | public boolean isRequestErrorMessage(String[] csv, String requestID) {
38 | return valueEquals(csv, 0, requestID) && valueEquals(csv, 1, FeedMessageType.ERROR.value());
39 | }
40 |
41 | /**
42 | * Check if a message matches the following format:
43 | *
44 | * [Request ID], {@link FeedSpecialMessage#END_OF_MESSAGE}
45 | *
46 | * @param csv the CSV
47 | * @param requestID the request ID
48 | *
49 | * @return true if the message represents an {@link FeedSpecialMessage#END_OF_MESSAGE} message
50 | */
51 | public boolean isRequestEndOfMessage(String[] csv, String requestID) {
52 | return valueEquals(csv, 0, requestID) && valueEquals(csv, 1, FeedSpecialMessage.END_OF_MESSAGE.value());
53 | }
54 |
55 | /**
56 | * Check if a message matches the following format:
57 | *
58 | * [Request ID], E, {@link FeedSpecialMessage#NO_DATA_ERROR}
59 | *
60 | * @param csv the CSV
61 | *
62 | * @return true if the message represents a {@link FeedSpecialMessage#NO_DATA_ERROR} message
63 | */
64 | public boolean isRequestNoDataError(String[] csv) {
65 | return valueEquals(csv, 2, FeedSpecialMessage.NO_DATA_ERROR.value());
66 | }
67 |
68 | /**
69 | * Check if a message matches the following format:
70 | *
71 | * [Request ID], E, {@link FeedSpecialMessage#SYNTAX_ERROR}
72 | *
73 | * @param csv the CSV
74 | *
75 | * @return true if the message represents a {@link FeedSpecialMessage#SYNTAX_ERROR} message
76 | */
77 | public boolean isRequestSyntaxError(String[] csv) {
78 | return valueEquals(csv, 2, FeedSpecialMessage.SYNTAX_ERROR.value());
79 | }
80 |
81 | /**
82 | * Gets a new Request ID. This method is thread safe.
83 | *
84 | * @return a new request ID
85 | */
86 | public String getNewRequestID() {
87 | int maxRequestID;
88 | synchronized (requestIDs) {
89 | maxRequestID = requestIDs.stream().max(Integer::compareTo).orElse(0) + 1;
90 | }
91 | requestIDs.add(maxRequestID);
92 | return String.valueOf(maxRequestID);
93 | }
94 |
95 | /**
96 | * Removes a Request ID. This method is thread safe.
97 | *
98 | * @param requestID the request ID
99 | */
100 | public void removeRequestID(String requestID) {
101 | requestIDs.remove(Integer.parseInt(requestID));
102 | }
103 |
104 | /**
105 | * Clears all Request IDs so that {@link #getNewRequestID()} starts at 0.
106 | */
107 | public void clearRequestIDs() {
108 | requestIDs.clear();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/map/NamedCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.map;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 | import java.util.function.BiConsumer;
10 | import java.util.function.Function;
11 | import java.util.function.Supplier;
12 |
13 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
14 |
15 | /**
16 | * {@link NamedCSVMapper} mappings are based off of named CSV indices.
17 | */
18 | public class NamedCSVMapper extends AbstractCSVMapper {
19 |
20 | private final HashMap> csvMappingsOfCSVIndexNames;
21 |
22 | /**
23 | * Instantiates a new {@link NamedCSVMapper}.
24 | *
25 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO
26 | */
27 | public NamedCSVMapper(Supplier pojoInstantiator) {
28 | super(pojoInstantiator);
29 |
30 | csvMappingsOfCSVIndexNames = new HashMap<>();
31 | }
32 |
33 | /**
34 | * Sets a CSV index name to POJO field mapping.
35 | *
36 | * @param the type of the POJO field
37 | * @param csvIndexName the CSV index name
38 | * @param fieldSetter see {@link CSVMapping} constructor doc
39 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
40 | */
41 | public
void setMapping(String csvIndexName, BiConsumer fieldSetter,
42 | Function stringToFieldConverter) {
43 | csvMappingsOfCSVIndexNames.put(csvIndexName, new CSVMapping<>(fieldSetter, stringToFieldConverter));
44 | }
45 |
46 | /**
47 | * Removes a CSV mapping, if it exists.
48 | *
49 | * @param csvIndexName the csv index name
50 | */
51 | public void removeMapping(String csvIndexName) {
52 | csvMappingsOfCSVIndexNames.remove(csvIndexName);
53 | }
54 |
55 | /**
56 | * Maps the given CSV to a POJO.
57 | *
58 | * @param csv the CSV
59 | * @param offset offset to add to CSV indices when applying {@link CSVMapping}
60 | * @param csvIndicesOfIndexNames a {@link Map} with they key being the csvIndexNames that were added
61 | * via {@link #setMapping(String, BiConsumer, Function)} and the values being which
62 | * CSV indices they correspond to in the given csv.
63 | *
64 | * @return a new POJO
65 | *
66 | * @throws CSVMappingException thrown for {@link CSVMappingException}s
67 | */
68 | public T map(String[] csv, int offset, Map csvIndicesOfIndexNames) {
69 | T instance = pojoInstantiator.get();
70 |
71 | // Loop through all added 'CSVMapping's and apply them with the given 'csvIndicesOfIndexNames' map
72 | for (Map.Entry csvIndexOfIndexName : csvIndicesOfIndexNames.entrySet()) {
73 | CSVMapping csvMapping = csvMappingsOfCSVIndexNames.get(csvIndexOfIndexName.getKey());
74 |
75 | if (csvMapping == null) {
76 | throw new IllegalArgumentException("The CSV index name " + csvIndexOfIndexName.getKey() +
77 | " does not have CSVMapping! Please report this!");
78 | }
79 |
80 | int csvNamedIndex = csvIndexOfIndexName.getValue();
81 | if (!valueNotWhitespace(csv, csvNamedIndex + offset)) { // Don't map empty CSV values
82 | continue;
83 | }
84 |
85 | try {
86 | csvMapping.apply(instance, csv[csvNamedIndex + offset]);
87 | } catch (Exception exception) {
88 | throw new CSVMappingException(
89 | String.format("Error mapping at index %d with offset %d with index name %s",
90 | csvNamedIndex, offset, csvIndexOfIndexName.getKey()), exception);
91 | }
92 | }
93 |
94 | return instance;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/schema_json/feed/lookup/market_summary/fundamental_snapshot.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "title": "POJO for FDS (Fundamental Summary) requests. See Market Summary via TCP/IP",
4 | "properties": {
5 | "Symbol": {
6 | "existingJavaType": "java.lang.String"
7 | },
8 | "Description": {
9 | "existingJavaType": "java.lang.String"
10 | },
11 | "PeRatio": {
12 | "existingJavaType": "java.lang.Double"
13 | },
14 | "AvgVolume": {
15 | "existingJavaType": "java.lang.Double"
16 | },
17 | "DivYield": {
18 | "existingJavaType": "java.lang.Double"
19 | },
20 | "DivAmount": {
21 | "existingJavaType": "java.lang.Double"
22 | },
23 | "DivRate": {
24 | "existingJavaType": "java.lang.Double"
25 | },
26 | "PayDate": {
27 | "existingJavaType": "java.time.LocalDate"
28 | },
29 | "ExDivDate": {
30 | "existingJavaType": "java.time.LocalDate"
31 | },
32 | "CurrentEps": {
33 | "existingJavaType": "java.lang.Double"
34 | },
35 | "EstEps": {
36 | "existingJavaType": "java.lang.Double"
37 | },
38 | "SIC": {
39 | "existingJavaType": "java.lang.Integer"
40 | },
41 | "Precision": {
42 | "existingJavaType": "java.lang.Integer"
43 | },
44 | "Display": {
45 | "existingJavaType": "java.lang.Double"
46 | },
47 | "GrowthPercent": {
48 | "existingJavaType": "java.lang.Double"
49 | },
50 | "FiscalYearEnd": {
51 | "existingJavaType": "java.time.LocalDate"
52 | },
53 | "Volatility": {
54 | "existingJavaType": "java.lang.Double"
55 | },
56 | "ListedMarket": {
57 | "existingJavaType": "java.lang.Integer"
58 | },
59 | "MaturityDate": {
60 | "existingJavaType": "java.time.LocalDate"
61 | },
62 | "OptionRoots": {
63 | "existingJavaType": "java.lang.String"
64 | },
65 | "CouponRate": {
66 | "existingJavaType": "java.lang.Double"
67 | },
68 | "InstitutionalPercent": {
69 | "existingJavaType": "java.lang.Double"
70 | },
71 | "YearEndClose": {
72 | "existingJavaType": "java.lang.Double"
73 | },
74 | "Beta": {
75 | "existingJavaType": "java.lang.Double"
76 | },
77 | "LEAPs": {
78 | "existingJavaType": "java.lang.String"
79 | },
80 | "WRAPs": {
81 | "existingJavaType": "java.lang.String"
82 | },
83 | "Assets": {
84 | "existingJavaType": "java.lang.Double"
85 | },
86 | "Liabilities": {
87 | "existingJavaType": "java.lang.Double"
88 | },
89 | "BalanceSheetDate": {
90 | "existingJavaType": "java.time.LocalDate"
91 | },
92 | "LongTermDebt": {
93 | "existingJavaType": "java.lang.Double"
94 | },
95 | "CommonSharesOutstanding": {
96 | "existingJavaType": "java.lang.Double"
97 | },
98 | "MarketCap": {
99 | "existingJavaType": "java.lang.Double"
100 | },
101 | "52WeekHigh": {
102 | "existingJavaType": "java.lang.Double"
103 | },
104 | "52WeekHighDate": {
105 | "existingJavaType": "java.time.LocalDate"
106 | },
107 | "52WeekLow": {
108 | "existingJavaType": "java.lang.Double"
109 | },
110 | "52WeekLowDate": {
111 | "existingJavaType": "java.time.LocalDate"
112 | },
113 | "CalHigh": {
114 | "existingJavaType": "java.lang.Double"
115 | },
116 | "CalHighDate": {
117 | "existingJavaType": "java.time.LocalDate"
118 | },
119 | "CalLow": {
120 | "existingJavaType": "java.lang.Double"
121 | },
122 | "CalLowDate": {
123 | "existingJavaType": "java.time.LocalDate"
124 | },
125 | "Expiration": {
126 | "existingJavaType": "java.lang.String"
127 | },
128 | "LastSplit": {
129 | "existingJavaType": "java.lang.Double"
130 | },
131 | "LastSplitDate": {
132 | "existingJavaType": "java.time.LocalDate"
133 | },
134 | "PrevSplit": {
135 | "existingJavaType": "java.lang.Double"
136 | },
137 | "PrevSplitDate": {
138 | "existingJavaType": "java.time.LocalDate"
139 | },
140 | "NAICS": {
141 | "existingJavaType": "java.lang.Integer"
142 | },
143 | "ShortInterest": {
144 | "existingJavaType": "java.lang.Double"
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/NestedListCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
5 |
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.function.BiConsumer;
9 | import java.util.function.Function;
10 | import java.util.function.Supplier;
11 |
12 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
13 |
14 | /**
15 | * {@link NestedListCSVMapper} mappings are based on a nested CSV lists inside a CSV list (e.g. a group of 3 CSV values
16 | * that repeated in a CSV list).
17 | */
18 | public class NestedListCSVMapper extends AbstractListCSVMapper {
19 |
20 | protected final Supplier extends List> listInstantiator;
21 | protected final HashMap> csvMappingsOfCSVIndices;
22 | protected final int nestedListLength;
23 |
24 | /**
25 | * Instantiates a new {@link NestedListCSVMapper}.
26 | *
27 | * @param listInstantiator a {@link Supplier} to instantiate a new {@link List}
28 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO
29 | * @param nestedListLength the nested list length
30 | */
31 | public NestedListCSVMapper(Supplier extends List> listInstantiator, Supplier pojoInstantiator,
32 | int nestedListLength) {
33 | super(pojoInstantiator);
34 |
35 | this.listInstantiator = listInstantiator;
36 | this.nestedListLength = nestedListLength;
37 |
38 | csvMappingsOfCSVIndices = new HashMap<>();
39 | }
40 |
41 | /**
42 | * Adds a CSV index to POJO field mapping as the CSV index being the largest
43 | * {@link #setMapping(int, BiConsumer, Function)} CSV index + 1.
44 | *
45 | * @param the type of the POJO field
46 | * @param fieldSetter see {@link CSVMapping} constructor doc
47 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
48 | */
49 | public
void addMapping(BiConsumer fieldSetter, Function stringToFieldConverter) {
50 | int nextCSVIndex = csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).orElse(-1) + 1;
51 | setMapping(nextCSVIndex, fieldSetter, stringToFieldConverter);
52 | }
53 |
54 | /**
55 | * Sets a CSV index to POJO field mapping.
56 | *
57 | * @param the type of the POJO field
58 | * @param csvIndex the CSV index
59 | * @param fieldSetter see {@link CSVMapping} constructor doc
60 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
61 | */
62 | public
void setMapping(int csvIndex, BiConsumer fieldSetter, Function stringToFieldConverter) {
63 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(fieldSetter, stringToFieldConverter));
64 | }
65 |
66 | /**
67 | * Removes a CSV mapping, if it exists.
68 | *
69 | * @param csvIndex the CSV index
70 | */
71 | public void removeMapping(int csvIndex) {
72 | csvMappingsOfCSVIndices.remove(csvIndex);
73 | }
74 |
75 | /**
76 | *
77 | * Note: this will map to a list of POJOs using the mappings added via
78 | * {@link #setMapping(int, BiConsumer, Function)}. This will iterate through the list at {@link #nestedListLength}
79 | * length.
80 | */
81 | @Override
82 | public List mapToList(String[] csv, int offset) {
83 | List mappedList = listInstantiator.get();
84 |
85 | for (int csvIndex = offset; csvIndex < csv.length; csvIndex += nestedListLength) {
86 | T instance = pojoInstantiator.get();
87 |
88 | // Loop through all added 'CSVMapping's and apply them
89 | boolean valueWasMapped = false;
90 | for (int mappedCSVIndex : csvMappingsOfCSVIndices.keySet()) {
91 | if (!valueNotWhitespace(csv, csvIndex + mappedCSVIndex)) { // Don't map empty CSV values
92 | continue;
93 | }
94 |
95 | // apply() could throw a variety of exceptions
96 | try {
97 | csvMappingsOfCSVIndices.get(mappedCSVIndex).apply(instance, csv[csvIndex + mappedCSVIndex]);
98 | valueWasMapped = true;
99 | } catch (Exception exception) {
100 | throw new CSVMappingException(
101 | String.format("Error mapping at index %d with offset %d at mapped CSV index %d",
102 | csvIndex - offset, offset, mappedCSVIndex), exception);
103 | }
104 | }
105 |
106 | if (valueWasMapped) {
107 | mappedList.add(instance);
108 | }
109 | }
110 |
111 | return mappedList;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/IndexCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
5 |
6 | import java.util.HashMap;
7 | import java.util.function.BiConsumer;
8 | import java.util.function.Function;
9 | import java.util.function.Supplier;
10 |
11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
12 |
13 | /**
14 | */
15 | public class IndexCSVMapper extends AbstractIndexCSVMapper {
16 |
17 | protected final HashMap> csvMappingsOfCSVIndices;
18 |
19 | /**
20 | * Instantiates a new {@link IndexCSVMapper}.
21 | *
22 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO
23 | */
24 | public IndexCSVMapper(Supplier pojoInstantiator) {
25 | super(pojoInstantiator);
26 |
27 | csvMappingsOfCSVIndices = new HashMap<>();
28 | }
29 |
30 | /**
31 | * Adds a CSV index to POJO field mapping as the CSV index being the largest mapped CSV index + 1.
32 | *
33 | * @param the type of the POJO field
34 | * @param fieldSetter see {@link CSVMapping} constructor doc
35 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
36 | */
37 | public
void addMapping(BiConsumer fieldSetter, Function stringToFieldConverter) {
38 | setMapping(getNextCSVIndex(), fieldSetter, stringToFieldConverter);
39 | }
40 |
41 | /**
42 | * Adds a CSV index to POJO field mapping as the CSV index being the largest mapped CSV index + 1.
43 | *
44 | * @param csvValueConsumer see {@link CSVMapping} constructor doc
45 | */
46 | public void addMapping(BiConsumer csvValueConsumer) {
47 | setMapping(getNextCSVIndex(), csvValueConsumer);
48 | }
49 |
50 | /**
51 | * Adds a CSV index to POJO field mapping as the CSV index being the largest mapped CSV index + 1.
52 | *
53 | * @param csvMapping the {@link CSVMapping}
54 | */
55 | public void addMapping(CSVMapping csvMapping) {
56 | setMapping(getNextCSVIndex(), csvMapping);
57 | }
58 |
59 | /**
60 | * Gets the largest CSV index in {@link #csvMappingsOfCSVIndices} + 1.
61 | *
62 | * @return an int
63 | */
64 | private int getNextCSVIndex() {
65 | return csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).orElse(-1) + 1;
66 | }
67 |
68 | /**
69 | * Sets a CSV index to POJO field mapping.
70 | *
71 | * @param the type of the POJO field
72 | * @param csvIndex the CSV index
73 | * @param fieldSetter see {@link CSVMapping} constructor doc
74 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
75 | */
76 | public
void setMapping(int csvIndex, BiConsumer fieldSetter, Function stringToFieldConverter) {
77 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(fieldSetter, stringToFieldConverter));
78 | }
79 |
80 | /**
81 | * Sets a CSV index to POJO field mapping.
82 | *
83 | * @param the type parameter
84 | * @param csvIndex the CSV index
85 | * @param csvValueConsumer see {@link CSVMapping} constructor doc
86 | */
87 | public
void setMapping(int csvIndex, BiConsumer csvValueConsumer) {
88 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(csvValueConsumer));
89 | }
90 |
91 | /**
92 | * Sets a CSV index to POJO field mapping.
93 | *
94 | * @param csvIndex the CSV index
95 | * @param csvMapping the {@link CSVMapping}
96 | */
97 | public void setMapping(int csvIndex, CSVMapping csvMapping) {
98 | csvMappingsOfCSVIndices.put(csvIndex, csvMapping);
99 | }
100 |
101 | /**
102 | * Removes a CSV mapping, if it exists.
103 | *
104 | * @param csvIndex the CSV index
105 | */
106 | public void removeMapping(int csvIndex) {
107 | csvMappingsOfCSVIndices.remove(csvIndex);
108 | }
109 |
110 | /**
111 | *
112 | * Note this will map with the mappings added via {@link #setMapping(int, BiConsumer, Function)}.
113 | */
114 | @Override
115 | public T map(String[] csv, int offset) {
116 | T instance = pojoInstantiator.get();
117 |
118 | // Loop through all added 'CSVMappings' and apply them
119 | for (int csvIndex : csvMappingsOfCSVIndices.keySet()) {
120 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values
121 | continue;
122 | }
123 |
124 | // apply() could throw a variety of exceptions
125 | try {
126 | csvMappingsOfCSVIndices.get(csvIndex).apply(instance, csv[csvIndex + offset]);
127 | } catch (Exception exception) {
128 | throw new CSVMappingException(csvIndex, offset, exception);
129 | }
130 | }
131 |
132 | return instance;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/properties/IQFeed4jProperties.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.properties;
2 |
3 | import net.jacobpeterson.iqfeed4j.IQFeed4j;
4 |
5 | import static net.jacobpeterson.iqfeed4j.util.properties.PropertyUtil.getProperty;
6 |
7 | /**
8 | * {@link IQFeed4jProperties} defines properties for {@link IQFeed4j}.
9 | */
10 | public class IQFeed4jProperties {
11 |
12 | /** The properties file name. */
13 | public static final String PROPERTIES_FILE = "iqfeed4j.properties";
14 | /** The default properties file name. */
15 | public static final String DEFAULT_PROPERTIES_FILE = "iqfeed4j.default.properties";
16 |
17 | private static final String IQCONNECT_COMMAND_KEY = "iqconnect_command";
18 | /** The value of {@link #IQCONNECT_COMMAND_KEY} in {@link #PROPERTIES_FILE}. */
19 | public static final String IQCONNECT_COMMAND = getProperty(
20 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
21 | IQCONNECT_COMMAND_KEY);
22 |
23 | private static final String PRODUCT_ID_KEY = "product_id";
24 | /** The value of {@link #PRODUCT_ID_KEY} in {@link #PROPERTIES_FILE}. */
25 | public static final String PRODUCT_ID = getProperty(
26 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
27 | PRODUCT_ID_KEY);
28 |
29 | private static final String APPLICATION_VERSION_KEY = "application_version";
30 | /** The value of {@link #APPLICATION_VERSION_KEY} in {@link #PROPERTIES_FILE}. */
31 | public static final String APPLICATION_VERSION = getProperty(
32 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
33 | APPLICATION_VERSION_KEY);
34 |
35 | private static final String LOGIN_KEY = "login";
36 | /** The value of {@link #LOGIN_KEY} in {@link #PROPERTIES_FILE}. */
37 | public static final String LOGIN = getProperty(
38 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
39 | LOGIN_KEY);
40 |
41 | private static final String PASSWORD_KEY = "password";
42 | /** The value of {@link #PASSWORD_KEY} in {@link #PROPERTIES_FILE}. */
43 | public static final String PASSWORD = getProperty(
44 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
45 | PASSWORD_KEY);
46 |
47 | private static final String AUTOCONNECT_KEY = "autoconnect";
48 | /** The value of {@link #AUTOCONNECT_KEY} in {@link #PROPERTIES_FILE}. */
49 | public static final Boolean AUTOCONNECT = Boolean.parseBoolean(getProperty(
50 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
51 | AUTOCONNECT_KEY));
52 |
53 | private static final String SAVE_LOGIN_INFO_KEY = "save_login_info";
54 | /** The value of {@link #SAVE_LOGIN_INFO_KEY} in {@link #PROPERTIES_FILE}. */
55 | public static final Boolean SAVE_LOGIN_INFO = Boolean.parseBoolean(getProperty(
56 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
57 | SAVE_LOGIN_INFO_KEY));
58 |
59 | private static final String FEED_NAME_KEY = "feed_name";
60 | /** The value of {@link #FEED_NAME_KEY} in {@link #PROPERTIES_FILE}. */
61 | public static final String FEED_NAME = getProperty(
62 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
63 | FEED_NAME_KEY);
64 |
65 | private static final String FEED_HOSTNAME_KEY = "feed_hostname";
66 | /** The value of {@link #FEED_HOSTNAME_KEY} in {@link #PROPERTIES_FILE}. */
67 | public static final String FEED_HOSTNAME = getProperty(
68 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
69 | FEED_HOSTNAME_KEY);
70 |
71 | private static final String LEVEL_1_FEED_PORT_KEY = "level_1_feed_port";
72 | /** The value of {@link #LEVEL_1_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */
73 | public static final Integer LEVEL_1_FEED_PORT = Integer.parseInt(getProperty(
74 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
75 | LEVEL_1_FEED_PORT_KEY));
76 |
77 | private static final String MARKET_DEPTH_FEED_PORT_KEY = "market_depth_feed_port";
78 | /** The value of {@link #MARKET_DEPTH_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */
79 | public static final Integer MARKET_DEPTH_FEED_PORT = Integer.parseInt(getProperty(
80 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
81 | MARKET_DEPTH_FEED_PORT_KEY));
82 |
83 | private static final String DERIVATIVE_FEED_PORT_KEY = "derivative_feed_port";
84 | /** The value of {@link #DERIVATIVE_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */
85 | public static final Integer DERIVATIVE_FEED_PORT = Integer.parseInt(getProperty(
86 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
87 | DERIVATIVE_FEED_PORT_KEY));
88 |
89 | private static final String ADMIN_FEED_PORT_KEY = "admin_feed_port";
90 | /** The value of {@link #ADMIN_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */
91 | public static final Integer ADMIN_FEED_PORT = Integer.parseInt(getProperty(
92 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
93 | ADMIN_FEED_PORT_KEY));
94 |
95 | private static final String LOOKUP_FEED_PORT_KEY = "lookup_feed_port";
96 | /** The value of {@link #LOOKUP_FEED_PORT_KEY} in {@link #PROPERTIES_FILE}. */
97 | public static final Integer LOOKUP_FEED_PORT = Integer.parseInt(getProperty(
98 | PROPERTIES_FILE, DEFAULT_PROPERTIES_FILE,
99 | LOOKUP_FEED_PORT_KEY));
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/AbstractLookupFeed.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.lookup;
2 |
3 | import com.google.common.base.Splitter;
4 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed;
5 | import net.jacobpeterson.iqfeed4j.feed.RequestIDFeedHelper;
6 | import net.jacobpeterson.iqfeed4j.feed.exception.IQFeedRuntimeException;
7 | import net.jacobpeterson.iqfeed4j.feed.exception.NoDataException;
8 | import net.jacobpeterson.iqfeed4j.feed.exception.SyntaxException;
9 | import net.jacobpeterson.iqfeed4j.feed.message.MultiMessageListener;
10 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType;
11 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedSpecialMessage;
12 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.index.AbstractIndexCSVMapper;
13 | import org.slf4j.Logger;
14 |
15 | import java.util.Arrays;
16 | import java.util.Map;
17 |
18 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueEquals;
19 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
20 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valuePresent;
21 |
22 | /**
23 | * {@link AbstractLookupFeed} represents a {@link AbstractFeed} for Lookup data.
24 | */
25 | public abstract class AbstractLookupFeed extends AbstractFeed {
26 |
27 | private static final String FEED_NAME_SUFFIX = " Lookup Feed";
28 |
29 | protected final RequestIDFeedHelper requestIDFeedHelper;
30 |
31 | /**
32 | * Instantiates a new {@link AbstractLookupFeed}.
33 | *
34 | * @param logger the {@link Logger}
35 | * @param lookupFeedName the lookup feed name
36 | * @param hostname the host name
37 | * @param port the port
38 | * @param csvSplitter the CSV {@link Splitter}
39 | */
40 | public AbstractLookupFeed(Logger logger, String lookupFeedName, String hostname, int port, Splitter csvSplitter) {
41 | super(logger, lookupFeedName + FEED_NAME_SUFFIX, hostname, port, csvSplitter, true, true);
42 |
43 | requestIDFeedHelper = new RequestIDFeedHelper();
44 | }
45 |
46 | /**
47 | * Handles a standard message for a {@link MultiMessageListener} by: checking for request error messages, handling
48 | * {@link FeedSpecialMessage#END_OF_MESSAGE} messages, and performing
49 | * {@link AbstractIndexCSVMapper#map(String[], int)} on the csv to call
50 | * {@link MultiMessageListener#onMessageReceived(Object)}.
51 | *
52 | * @param the type of {@link MultiMessageListener}
53 | * @param csv the CSV
54 | * @param requestID the Request ID
55 | * @param offset the offset to add to CSV indices
56 | * @param listenersOfRequestIDs the {@link Map} with the keys being the Request IDs and the values being the
57 | * corresponding {@link MultiMessageListener}s
58 | * @param indexCSVMapper the {@link AbstractIndexCSVMapper} for the message
59 | *
60 | * @return true if the requestID was a key inside listenersOfRequestIDs, false otherwise
61 | */
62 | protected boolean handleStandardMultiMessage(String[] csv, String requestID, int offset,
63 | Map> listenersOfRequestIDs, AbstractIndexCSVMapper indexCSVMapper) {
64 | MultiMessageListener listener = listenersOfRequestIDs.get(requestID);
65 |
66 | if (listener == null) {
67 | return false;
68 | }
69 |
70 | if (requestIDFeedHelper.isRequestErrorMessage(csv, requestID)) {
71 | if (requestIDFeedHelper.isRequestNoDataError(csv)) {
72 | listener.onMessageException(new NoDataException());
73 | } else if (requestIDFeedHelper.isRequestSyntaxError(csv)) {
74 | listener.onMessageException(new SyntaxException());
75 | } else {
76 | listener.onMessageException(new IQFeedRuntimeException(
77 | valuePresent(csv, 2) ?
78 | String.join(",", Arrays.copyOfRange(csv, 2, csv.length)) :
79 | "Error message not present."));
80 | }
81 | } else if (requestIDFeedHelper.isRequestEndOfMessage(csv, requestID)) {
82 | listenersOfRequestIDs.remove(requestID);
83 | requestIDFeedHelper.removeRequestID(requestID);
84 | listener.handleEndOfMultiMessage();
85 | } else {
86 | try {
87 | T message = indexCSVMapper.map(csv, offset);
88 | listener.onMessageReceived(message);
89 | } catch (Exception exception) {
90 | listener.onMessageException(exception);
91 | }
92 | }
93 |
94 | return true;
95 | }
96 |
97 | /**
98 | * Checks if a message is a {@link FeedMessageType#ERROR} message or that the first CSV value is whitespace to check
99 | * if a Request ID is present.
100 | *
101 | * @param csv the CSV
102 | *
103 | * @return true if the message is an error or is invalid
104 | */
105 | protected boolean isErrorOrInvalidMessage(String[] csv) {
106 | if (valueEquals(csv, 0, FeedMessageType.ERROR.value())) {
107 | logger.error("Received error message! {}", (Object) csv);
108 | return true;
109 | }
110 |
111 | // Messages sent on this feed have a numeric Request ID first
112 | if (!valueNotWhitespace(csv, 0)) {
113 | logger.error("Received unknown message format: {}", (Object) csv);
114 | return true;
115 | }
116 |
117 | return false;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/docs/Documentation2JSONSchema.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.docs;
2 |
3 | import org.w3c.dom.Document;
4 | import org.w3c.dom.NodeList;
5 | import org.xml.sax.SAXException;
6 |
7 | import javax.xml.parsers.DocumentBuilder;
8 | import javax.xml.parsers.DocumentBuilderFactory;
9 | import javax.xml.parsers.ParserConfigurationException;
10 | import javax.xml.xpath.XPath;
11 | import javax.xml.xpath.XPathConstants;
12 | import javax.xml.xpath.XPathExpressionException;
13 | import javax.xml.xpath.XPathFactory;
14 | import java.io.ByteArrayInputStream;
15 | import java.io.IOException;
16 |
17 | /**
18 | * {@link Documentation2JSONSchema} is not meant for regular runtime use. It's used to generate jsonschema2POJO schema
19 | * JSON from the HTML in the docs.
20 | */
21 | public final class Documentation2JSONSchema {
22 |
23 | /**
24 | * This is a standalone helper method that converts properties HTML copied from the IQFeed docs page into a JSON
25 | * schema property list that includes the type and the description. It's kinda hacky, but it helps in the generation
26 | * of JSON POJOs with Javadocs.
27 | *
28 | * @param docHTML the documentation HTML
29 | */
30 | public static void printIQFeedHTMLWebDocAsJSONSchema(String docHTML) throws Exception {
31 | // For position(): https://stackoverflow.com/questions/2407781/get-nth-child-of-a-node-using-xpath
32 | // It is 1-indexed.
33 |
34 | String nameXPath = "//tbody/tr/td[position()=2]";
35 | String typeXPath = "//tbody/tr/td[position()=3]";
36 | String descriptionXPath = "//tbody/tr/td[position()=4]";
37 |
38 | System.out.println(getSchemaFromDoc(docHTML, nameXPath, typeXPath, descriptionXPath));
39 | }
40 |
41 | /**
42 | * Converts a generic HTML doc with "name", "type", and "description/note" into schema JSON used for
43 | * jsonSchema2POJO.
44 | *
45 | * @param docHTML the doc html
46 | * @param nameXPath the name Xpath
47 | * @param typeXPath the type Xpath
48 | * @param descriptionXPath the description Xpath
49 | *
50 | * @return the schema from the doc
51 | *
52 | * @throws ParserConfigurationException thrown for {@link ParserConfigurationException}s
53 | * @throws XPathExpressionException thrown for {@link XPathExpressionException}s
54 | * @throws IOException thrown for {@link IOException}s
55 | * @throws SAXException thrown for {@link SAXException}s
56 | */
57 | private static String getSchemaFromDoc(String docHTML, String nameXPath, String typeXPath, String descriptionXPath)
58 | throws ParserConfigurationException, XPathExpressionException, IOException, SAXException {
59 | StringBuilder schemaJSON = new StringBuilder();
60 | DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
61 | Document document = builder.parse(new ByteArrayInputStream(docHTML.getBytes()));
62 | XPathFactory xPathFactory = XPathFactory.newInstance();
63 | XPath xPath = xPathFactory.newXPath();
64 |
65 | NodeList propertyNameNodes = (NodeList) xPath.compile(nameXPath).evaluate(document, XPathConstants.NODESET);
66 | NodeList propertyTypesNodes = (NodeList) xPath.compile(typeXPath).evaluate(document, XPathConstants.NODESET);
67 | NodeList propertyDescriptionsNodes = (NodeList) xPath.compile(descriptionXPath).evaluate(document,
68 | XPathConstants.NODESET);
69 |
70 | for (int propertyIndex = 0; propertyIndex < propertyNameNodes.getLength(); propertyIndex++) {
71 | String propertyName = propertyNameNodes.item(propertyIndex).getTextContent();
72 | String propertyType = propertyTypesNodes.item(propertyIndex).getTextContent();
73 | String propertyDescription = propertyDescriptionsNodes.item(propertyIndex).getTextContent();
74 |
75 | // Property description may be null
76 | if (propertyDescription == null || propertyDescription.isEmpty()) {
77 | propertyDescription = propertyName;
78 | }
79 |
80 | // Lower Camel Case
81 | propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
82 |
83 | schemaJSON.append("\"").append(propertyName).append("\": {\n");
84 | schemaJSON.append("\"existingJavaType\": \"")
85 | .append(parseDocTypeToSchemaJavaType(propertyType)).append("\",\n");
86 | schemaJSON.append("\"title\": \"").append(propertyDescription).append("\"\n");
87 | schemaJSON.append("},\n");
88 | }
89 |
90 | return schemaJSON.toString();
91 | }
92 |
93 | /**
94 | * @see jsonSchema2Pojo Reference
95 | */
96 | private static String parseDocTypeToSchemaJavaType(String docType) {
97 | docType = docType.toLowerCase();
98 |
99 | if (docType.contains("string") || docType.contains("timestamp")) {
100 | return "java.lang.String";
101 | } else if (docType.contains("boolean") || docType.contains("bool")) {
102 | return "java.lang.Boolean";
103 | } else if (docType.contains("int") || docType.contains("integer")) {
104 | return "java.lang.Integer";
105 | } else if (docType.contains("double") || docType.contains("float") || docType.contains("decimal")) {
106 | return "java.lang.Double";
107 | } else {
108 | return "UNKNOWN DOC DATA TYPE";
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/TrailingIndexCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
5 |
6 | import java.util.HashMap;
7 | import java.util.function.BiConsumer;
8 | import java.util.function.Function;
9 | import java.util.function.Supplier;
10 |
11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueExists;
12 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
13 |
14 | /**
15 | * {@link TrailingIndexCSVMapper} mappings are based off of predefined CSV indices, but with the ability to add a CSV
16 | * mapping that accumulates all CSV values after a certain index into one mapping.
17 | */
18 | public class TrailingIndexCSVMapper extends AbstractIndexCSVMapper {
19 |
20 | protected final HashMap> csvMappingsOfCSVIndices;
21 | protected int trailingCSVIndex;
22 | protected CSVMapping trailingCSVMapping;
23 |
24 | /**
25 | * Instantiates a new {@link TrailingIndexCSVMapper}.
26 | *
27 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO
28 | */
29 | public TrailingIndexCSVMapper(Supplier pojoInstantiator) {
30 | super(pojoInstantiator);
31 |
32 | csvMappingsOfCSVIndices = new HashMap<>();
33 | }
34 |
35 | /**
36 | * Adds a CSV index to POJO field mapping as the CSV index being the largest
37 | * {@link #setMapping(int, BiConsumer, Function)} CSV index + 1.
38 | *
39 | * @param the type of the POJO field
40 | * @param fieldSetter see {@link CSVMapping} constructor doc
41 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
42 | */
43 | public
void addMapping(BiConsumer fieldSetter, Function stringToFieldConverter) {
44 | int nextCSVIndex = csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).orElse(-1) + 1;
45 | setMapping(nextCSVIndex, fieldSetter, stringToFieldConverter);
46 | }
47 |
48 | /**
49 | * Sets a CSV index to POJO field mapping.
50 | *
51 | * @param the type of the POJO field
52 | * @param csvIndex the CSV index
53 | * @param fieldSetter see {@link CSVMapping} constructor doc
54 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
55 | */
56 | @SuppressWarnings("OptionalGetWithoutIsPresent") // Optional will always be present here
57 | public
void setMapping(int csvIndex, BiConsumer fieldSetter, Function stringToFieldConverter) {
58 | csvMappingsOfCSVIndices.put(csvIndex, new CSVMapping<>(fieldSetter, stringToFieldConverter));
59 | trailingCSVIndex = csvMappingsOfCSVIndices.keySet().stream().max(Integer::compareTo).get() + 1;
60 | }
61 |
62 | /**
63 | * Removes a CSV mapping, if it exists.
64 | *
65 | * @param csvIndex the CSV index
66 | */
67 | public void removeMapping(int csvIndex) {
68 | csvMappingsOfCSVIndices.remove(csvIndex);
69 | }
70 |
71 | /**
72 | * Sets the trailing CSV mapping that accumulates/maps all CSV values after the being the largest
73 | * {@link #setMapping(int, BiConsumer, Function)} CSV index + 1.
74 | *
75 | * @param the type of the POJO field
76 | * @param fieldSetter see {@link CSVMapping} constructor doc
77 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
78 | */
79 | public
void setTrailingMapping(BiConsumer fieldSetter, Function stringToFieldConverter) {
80 | this.trailingCSVMapping = new CSVMapping<>(fieldSetter, stringToFieldConverter);
81 | }
82 |
83 | /**
84 | *
85 | * Note this will map with the mappings added via {@link #setMapping(int, BiConsumer, Function)} and
86 | * {@link #setTrailingMapping(BiConsumer, Function)}.
87 | */
88 | @Override
89 | public T map(String[] csv, int offset) {
90 | T instance = pojoInstantiator.get();
91 |
92 | // Loop through all added 'CSVMapping's and apply them
93 | for (int csvIndex : csvMappingsOfCSVIndices.keySet()) {
94 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values
95 | continue;
96 | }
97 |
98 | // apply() could throw a variety of exceptions
99 | try {
100 | csvMappingsOfCSVIndices.get(csvIndex).apply(instance, csv[csvIndex + offset]);
101 | } catch (Exception exception) {
102 | throw new CSVMappingException(csvIndex, offset, exception);
103 | }
104 | }
105 |
106 | // Now apply 'trailingCSVMapping' on trailing CSV values
107 | if (valueExists(csv, trailingCSVIndex + offset)) {
108 | StringBuilder trailingCSVBuilder = new StringBuilder();
109 | for (int csvIndex = trailingCSVIndex + offset; csvIndex < csv.length; csvIndex++) {
110 | trailingCSVBuilder.append(csv[csvIndex]);
111 |
112 | if (csvIndex < csv.length - 1) { // Don't append comma on last CSV value
113 | trailingCSVBuilder.append(",");
114 | }
115 | }
116 |
117 | String trailingCSVString = trailingCSVBuilder.toString();
118 | // apply() could throw a variety of exceptions
119 | try {
120 | trailingCSVMapping.apply(instance, trailingCSVString);
121 | } catch (Exception exception) {
122 | throw new CSVMappingException(trailingCSVIndex, offset, exception);
123 | }
124 | }
125 |
126 | return instance;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/properties/PropertyUtil.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.properties;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.io.IOException;
7 | import java.io.InputStream;
8 | import java.util.Collections;
9 | import java.util.HashMap;
10 | import java.util.Map;
11 | import java.util.Properties;
12 |
13 | /**
14 | * {@link PropertyUtil} is a util class for all things {@link Properties}.
15 | */
16 | public class PropertyUtil {
17 |
18 | private static final Logger LOGGER = LoggerFactory.getLogger(PropertyUtil.class);
19 |
20 | public static final Map CACHED_PROPERTIES = Collections.synchronizedMap(new HashMap<>());
21 |
22 | /**
23 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not
24 | * cached already.
25 | *
26 | * @param propertyFile the property file
27 | * @param key the key
28 | *
29 | * @return the string
30 | */
31 | public static String getProperty(String propertyFile, String key) {
32 | return getProperty(propertyFile, null, key, null);
33 | }
34 |
35 | /**
36 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not
37 | * cached already. If the desired property is not found in the propertyFile, then the
38 | * defaultPropertyFile is searched.
39 | *
40 | * @param propertyFile the property file
41 | * @param defaultPropertyFile the default property file
42 | * @param key the key
43 | *
44 | * @return the string
45 | */
46 | public static String getProperty(String propertyFile, String defaultPropertyFile, String key) {
47 | return getProperty(propertyFile, defaultPropertyFile, key, null);
48 | }
49 |
50 | /**
51 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not
52 | * cached already. If the desired property is not found in the propertyFile, then the
53 | * defaultPropertyFile is searched, and if it's not there, then defaultValue is returned.
54 | *
55 | * @param propertyFile the property file
56 | * @param defaultPropertyFile the default property file
57 | * @param key the key
58 | * @param defaultValue the default value (if the desired property wasn't found, then this is returned)
59 | *
60 | * @return the string
61 | */
62 | public static String getProperty(String propertyFile, String defaultPropertyFile, String key, String defaultValue) {
63 | Properties properties;
64 |
65 | if (!CACHED_PROPERTIES.containsKey(propertyFile)) {
66 | properties = loadPropertyFile(propertyFile, defaultPropertyFile);
67 | CACHED_PROPERTIES.put(propertyFile, properties);
68 | } else {
69 | properties = CACHED_PROPERTIES.get(propertyFile);
70 | }
71 |
72 | return properties == null ? defaultValue : properties.getProperty(key, defaultValue);
73 | }
74 |
75 | /**
76 | * Loads property file {@link Properties}.
77 | *
78 | * @param propertyFile the property file name
79 | * @param defaultPropertyFile the default property file name
80 | *
81 | * @return the properties
82 | */
83 | public synchronized static Properties loadPropertyFile(String propertyFile, String defaultPropertyFile) {
84 | ClassLoader classLoader = PropertyUtil.class.getClassLoader();
85 | if (classLoader == null) {
86 | classLoader = ClassLoader.getSystemClassLoader();
87 | }
88 |
89 | // Load the default property file if exists
90 | Properties defaultProperties = null;
91 | InputStream defaultPropertyStream = classLoader.getResourceAsStream(defaultPropertyFile);
92 |
93 | if (defaultPropertyStream != null) {
94 | defaultProperties = new Properties();
95 |
96 | // Load the properties
97 | try {
98 | defaultProperties.load(defaultPropertyStream);
99 | LOGGER.debug("Loaded default properties file: {}", defaultPropertyFile);
100 | } catch (IOException exception) {
101 | LOGGER.error("Could not load default property file: {}\n{}", defaultPropertyFile, exception);
102 | }
103 |
104 | // Close the InputStream
105 | try {
106 | defaultPropertyStream.close();
107 | } catch (IOException exception) {
108 | LOGGER.error("Could not close default property file stream: {}\n{}", defaultPropertyFile, exception);
109 | }
110 |
111 | } else {
112 | LOGGER.warn("No default property file found for: {}", propertyFile);
113 | }
114 |
115 | // Load the property file
116 | Properties properties = null;
117 | InputStream propertyStream = classLoader.getResourceAsStream(propertyFile);
118 |
119 | if (propertyStream != null) {
120 | // Add default properties if they were found
121 | properties = defaultProperties == null ? new Properties() : new Properties(defaultProperties);
122 |
123 | // Load the properties
124 | try {
125 | properties.load(propertyStream);
126 | LOGGER.info("Loaded properties file: {}", propertyFile);
127 | } catch (IOException exception) {
128 | LOGGER.error("Could not load property file: {}\n{}", propertyFile, exception);
129 | }
130 |
131 | // Close the InputStream
132 | try {
133 | propertyStream.close();
134 | } catch (IOException exception) {
135 | LOGGER.error("Could not close property file stream: {}\n{}", propertyFile, exception);
136 | }
137 | } else {
138 | LOGGER.debug("Could not find property file: {}", propertyFile);
139 |
140 | if (defaultProperties != null) {
141 | LOGGER.info("Using default properties for: {}", propertyFile);
142 | properties = defaultProperties;
143 | }
144 | }
145 |
146 | return properties;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/AbstractCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper;
2 |
3 | import java.time.LocalDate;
4 | import java.time.LocalDateTime;
5 | import java.time.LocalTime;
6 | import java.time.format.DateTimeFormatter;
7 | import java.time.format.DateTimeFormatterBuilder;
8 | import java.time.temporal.ChronoField;
9 | import java.util.Locale;
10 | import java.util.function.Function;
11 | import java.util.function.Supplier;
12 |
13 | /**
14 | * {@link AbstractCSVMapper} maps CSV {@link String} values to various types/objects.
15 | *
16 | * @param the type of CSV mapping
17 | */
18 | public abstract class AbstractCSVMapper {
19 |
20 | /**
21 | * {@link PrimitiveConvertors} contains common {@link Function}s with the argument being the CSV {@link String}
22 | * value and the return value being the converted CSV primitive value.
23 | */
24 | public static class PrimitiveConvertors {
25 |
26 | public static final Function STRING = (value) -> value;
27 | public static final Function CHAR = (value) -> value.length() == 0 ? '\0' : value.charAt(0);
28 | public static final Function BOOLEAN = Boolean::valueOf;
29 | public static final Function BYTE = Byte::valueOf;
30 | public static final Function SHORT = Short::valueOf;
31 | public static final Function INTEGER = Integer::valueOf;
32 | public static final Function LONG = Long::valueOf;
33 | public static final Function FLOAT = Float::valueOf;
34 | public static final Function DOUBLE = Double::valueOf;
35 | }
36 |
37 | /**
38 | * {@link DateTimeFormatters} contains various {@link DateTimeFormatters} for formatting/converting from/to CSV.
39 | */
40 | public static class DateTimeFormatters {
41 |
42 | /** Format of: HHmmss */
43 | public static final DateTimeFormatter TIME = DateTimeFormatter.ofPattern("HHmmss");
44 |
45 | /** Format of: yyyyMMdd */
46 | public static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
47 |
48 | /** Format of: yyyyMMdd HHmmss */
49 | public static final DateTimeFormatter DATE_SPACE_TIME =
50 | DateTimeFormatter.ofPattern("yyyyMMdd HHmmss");
51 |
52 | /** Format of: MMM dd h:mma */
53 | public static final DateTimeFormatter MONTH3_DAY_TIME_AM_PM =
54 | new DateTimeFormatterBuilder()
55 | .appendPattern("MMM dd h:mma")
56 | .parseDefaulting(ChronoField.YEAR, LocalDate.now().getYear())
57 | .toFormatter(Locale.ENGLISH);
58 |
59 | /** Format of: yyyy-MM-dd HH:mm:ss.nnnnnn */
60 | public static final DateTimeFormatter DASHED_DATE_SPACE_TIME_FRACTIONAL =
61 | new DateTimeFormatterBuilder()
62 | .parseCaseInsensitive()
63 | .append(DateTimeFormatter.ISO_LOCAL_DATE)
64 | .appendLiteral(' ')
65 | .append(DateTimeFormatter.ISO_LOCAL_TIME) // Optionally includes micro/nanoseconds
66 | .toFormatter(Locale.ENGLISH);
67 |
68 | /** Format of: yyyy-MM-dd */
69 | public static final DateTimeFormatter DASHED_DATE = DateTimeFormatter.ISO_LOCAL_DATE;
70 |
71 | /** Format of: MM/dd/yyyy */
72 | public static final DateTimeFormatter SLASHED_DATE = DateTimeFormatter.ofPattern("MM/dd/yyyy");
73 |
74 | /** Format of: HH:mm:ss[.nnnnnn] */
75 | public static final DateTimeFormatter COLON_TIME = DateTimeFormatter.ISO_LOCAL_TIME;
76 |
77 | /** Format of: yyyyMMdd HH:mm:ss */
78 | public static final DateTimeFormatter DATE_SPACE_COLON_TIME = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
79 |
80 | /** Format of: [Hmmss][HHmmss] */
81 | public static final DateTimeFormatter OPTIONAL_1_OR_2_DIGIT_HOUR_TIME =
82 | DateTimeFormatter.ofPattern("[Hmmss][HHmmss]");
83 | }
84 |
85 | /**
86 | * {@link DateTimeConverters} contains common {@link Function}s with the argument being the CSV {@link String} value
87 | * and the return value being the converted CSV date/time value.
88 | */
89 | public static class DateTimeConverters {
90 |
91 | /** Convertor using {@link DateTimeFormatters#TIME} */
92 | public static final Function TIME =
93 | value -> LocalTime.parse(value, DateTimeFormatters.TIME);
94 |
95 | /** Convertor using {@link DateTimeFormatters#DATE} */
96 | public static final Function DATE =
97 | value -> LocalDate.parse(value, DateTimeFormatters.DATE);
98 |
99 | /** Convertor using {@link DateTimeFormatters#DATE_SPACE_TIME} */
100 | public static final Function DATE_SPACE_TIME =
101 | value -> LocalDateTime.parse(value, DateTimeFormatters.DATE_SPACE_TIME);
102 |
103 | /** Convertor using {@link DateTimeFormatters#MONTH3_DAY_TIME_AM_PM} */
104 | public static final Function MONTH3_DAY_TIME_AM_PM =
105 | value -> LocalDateTime.parse(value, DateTimeFormatters.MONTH3_DAY_TIME_AM_PM);
106 |
107 | /** Convertor using {@link DateTimeFormatters#DASHED_DATE_SPACE_TIME_FRACTIONAL} */
108 | public static final Function DASHED_DATE_SPACE_TIME_FRACTIONAL =
109 | value -> LocalDateTime.parse(value, DateTimeFormatters.DASHED_DATE_SPACE_TIME_FRACTIONAL);
110 |
111 | /** Convertor using {@link DateTimeFormatters#DASHED_DATE_SPACE_TIME_FRACTIONAL} */
112 | public static final Function DASHED_DATE_SPACE_TIME = DASHED_DATE_SPACE_TIME_FRACTIONAL;
113 |
114 | /** Convertor using {@link DateTimeFormatters#DASHED_DATE} */
115 | public static final Function DASHED_DATE =
116 | value -> LocalDate.parse(value, DateTimeFormatters.DASHED_DATE);
117 |
118 | /** Convertor using {@link DateTimeFormatters#SLASHED_DATE} */
119 | public static final Function SLASHED_DATE =
120 | value -> LocalDate.parse(value, DateTimeFormatters.SLASHED_DATE);
121 |
122 | /** Convertor using {@link DateTimeFormatters#COLON_TIME} */
123 | public static final Function COLON_TIME =
124 | value -> LocalTime.parse(value, DateTimeFormatters.COLON_TIME);
125 |
126 | /** Convertor using {@link DateTimeFormatters#DATE_SPACE_COLON_TIME} */
127 | public static final Function DATE_SPACE_COLON_TIME =
128 | value -> LocalDateTime.parse(value, DateTimeFormatters.DATE_SPACE_COLON_TIME);
129 |
130 | /** Convertor using {@link DateTimeFormatters#OPTIONAL_1_OR_2_DIGIT_HOUR_TIME} */
131 | public static final Function OPTIONAL_1_OR_2_DIGIT_HOUR_TIME =
132 | value -> LocalTime.parse(value, DateTimeFormatters.OPTIONAL_1_OR_2_DIGIT_HOUR_TIME);
133 |
134 | /** Calls {@link #OPTIONAL_1_OR_2_DIGIT_HOUR_TIME} but returns null on an {@link Exception}. */
135 | public static final Function OPTIONAL_1_OR_2_DIGIT_HOUR_TIME_NULLABLE = value -> {
136 | try {
137 | return OPTIONAL_1_OR_2_DIGIT_HOUR_TIME.apply(value);
138 | } catch (Exception exception) {
139 | return null;
140 | }
141 | };
142 | }
143 |
144 | protected final Supplier pojoInstantiator;
145 |
146 | /**
147 | * Instantiates a new {@link AbstractCSVMapper}.
148 | *
149 | * @param pojoInstantiator a {@link Supplier} to instantiate a new POJO
150 | */
151 | public AbstractCSVMapper(Supplier pojoInstantiator) {
152 | this.pojoInstantiator = pojoInstantiator;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/historical/pool/HistoricalFeedPool.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.lookup.historical.pool;
2 |
3 | import net.jacobpeterson.iqfeed4j.feed.lookup.historical.HistoricalFeed;
4 | import org.apache.commons.pool2.ObjectPool;
5 | import org.apache.commons.pool2.PooledObject;
6 | import org.apache.commons.pool2.PooledObjectFactory;
7 | import org.apache.commons.pool2.impl.DefaultEvictionPolicy;
8 | import org.apache.commons.pool2.impl.DefaultPooledObject;
9 | import org.apache.commons.pool2.impl.GenericObjectPool;
10 | import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
11 |
12 | import java.time.Duration;
13 | import java.util.concurrent.TimeUnit;
14 | import java.util.function.Consumer;
15 |
16 | import static com.google.common.base.Preconditions.checkArgument;
17 |
18 | /**
19 | * {@link HistoricalFeedPool} contains a thread-safe pool of {@link HistoricalFeed}s and has IQFeed's history request
20 | * rate limit built in. This class is designed to allow a user to make as many simultaneous {@link HistoricalFeed}
21 | * requests that IQFeed allows.
22 | */
23 | public class HistoricalFeedPool {
24 |
25 | /**
26 | * Defines the feed request time delay in milliseconds which is currently 20 ms (50 requests/second) plus 1 ms for
27 | * margin.
28 | */
29 | public static final int DEFAULT_FEED_REQUEST_TIME_DELAY_MILLIS = 20 + 1;
30 |
31 | private final ObjectPool pool;
32 | private final Object lastRequestMillisLock;
33 | private long lastRequestMillis;
34 |
35 | /**
36 | * Instantiates a new {@link HistoricalFeedPool} using {@link Factory} as the {@link HistoricalFeed}
37 | * {@link PooledObjectFactory} and a customized {@link GenericObjectPoolConfig}.
38 | *
39 | * @param historicalFeedName the {@link HistoricalFeed} name
40 | * @param hostname the hostname
41 | * @param port the port
42 | */
43 | public HistoricalFeedPool(String historicalFeedName, String hostname, int port) {
44 | final GenericObjectPoolConfig feedPoolConfig = new GenericObjectPoolConfig<>();
45 | // Configure max/min pool objects
46 | feedPoolConfig.setMaxTotal(100);
47 | feedPoolConfig.setMaxIdle(-1);
48 | feedPoolConfig.setMinIdle(0);
49 | feedPoolConfig.setBlockWhenExhausted(true);
50 | // Test the feed instance to validate it before use
51 | feedPoolConfig.setTestOnBorrow(true);
52 | feedPoolConfig.setTestOnCreate(true);
53 | feedPoolConfig.setTestOnReturn(true);
54 | // Configure eviction of idle feeds
55 | feedPoolConfig.setEvictionPolicy(new DefaultEvictionPolicy<>());
56 | feedPoolConfig.setMinEvictableIdleTime(Duration.ofSeconds(30));
57 | feedPoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(25));
58 | feedPoolConfig.setNumTestsPerEvictionRun(-3); // Evict 1/3rd of idle feeds
59 |
60 | pool = new GenericObjectPool<>(new Factory(historicalFeedName, hostname, port), feedPoolConfig);
61 | lastRequestMillisLock = new Object();
62 | }
63 |
64 | /**
65 | * Instantiates a new {@link HistoricalFeedPool} using {@link Factory} as the {@link HistoricalFeed}
66 | * {@link PooledObjectFactory} and a given {@link GenericObjectPoolConfig}.
67 | *
68 | * @param historicalFeedName the {@link HistoricalFeed} name
69 | * @param hostname the hostname
70 | * @param port the port
71 | * @param feedPoolConfig the {@link HistoricalFeed} {@link GenericObjectPoolConfig}
72 | */
73 | public HistoricalFeedPool(String historicalFeedName, String hostname, int port,
74 | GenericObjectPoolConfig feedPoolConfig) {
75 | pool = new GenericObjectPool<>(new Factory(historicalFeedName, hostname, port), feedPoolConfig);
76 | lastRequestMillisLock = new Object();
77 | }
78 |
79 | /**
80 | * Instantiates a new {@link HistoricalFeedPool}.
81 | *
82 | * @param objectPool the {@link ObjectPool} of {@link HistoricalFeed}s to use
83 | */
84 | public HistoricalFeedPool(ObjectPool objectPool) {
85 | checkArgument(objectPool.getNumActive() <= 0);
86 | this.pool = objectPool;
87 | lastRequestMillisLock = new Object();
88 | }
89 |
90 | /**
91 | * Stops this instance of {@link HistoricalFeedPool} (stops/closes all {@link HistoricalFeed}s in the feed pool).
92 | */
93 | public void stop() {
94 | pool.close();
95 | }
96 |
97 | /**
98 | * Synchronously makes a request to a {@link HistoricalFeed} in this {@link HistoricalFeedPool} given a request
99 | * {@link Consumer}. This method may block for up to {@link Factory#getFeedRequestTimeDelayMillis()} milliseconds.
100 | *
101 | * @param historicalFeedConsumer the {@link HistoricalFeed} {@link Consumer}
102 | *
103 | * @throws Exception thrown for a variety of {@link Exception}s
104 | */
105 | public void request(Consumer historicalFeedConsumer) throws Exception {
106 | blockIfRateLimited();
107 |
108 | HistoricalFeed borrowedHistoricalFeed = pool.borrowObject();
109 | try {
110 | historicalFeedConsumer.accept(borrowedHistoricalFeed);
111 | } finally {
112 | pool.returnObject(borrowedHistoricalFeed);
113 | }
114 | }
115 |
116 | /**
117 | * Blocks until time since {@link #lastRequestMillis} is greater than {@link #getFeedRequestTimeDelayMillis()}. Also
118 | * sets {@link #lastRequestMillis} to the current time.
119 | *
120 | * @throws InterruptedException thrown for {@link InterruptedException}s
121 | */
122 | private void blockIfRateLimited() throws InterruptedException {
123 | synchronized (lastRequestMillisLock) {
124 | long elapsedMillis = System.currentTimeMillis() - lastRequestMillis;
125 | if (elapsedMillis < getFeedRequestTimeDelayMillis()) {
126 | Thread.sleep(getFeedRequestTimeDelayMillis() - elapsedMillis);
127 | }
128 |
129 | lastRequestMillis = System.currentTimeMillis();
130 | }
131 | }
132 |
133 | /**
134 | * Gets the time delay in milliseconds for feed request rate limiting.
135 | *
136 | * @return a positive number of milliseconds
137 | */
138 | protected long getFeedRequestTimeDelayMillis() {
139 | return DEFAULT_FEED_REQUEST_TIME_DELAY_MILLIS;
140 | }
141 |
142 | /**
143 | * {@link Factory} is a {@link PooledObjectFactory} for {@link HistoricalFeed}s.
144 | */
145 | public static class Factory implements PooledObjectFactory {
146 |
147 | private final String historicalFeedName;
148 | private final String hostname;
149 | private final int port;
150 |
151 | /**
152 | * Instantiates a new {@link Factory}.
153 | *
154 | * @param historicalFeedName the {@link HistoricalFeed} name
155 | * @param hostname the hostname
156 | * @param port the port
157 | */
158 | public Factory(String historicalFeedName, String hostname, int port) {
159 | this.historicalFeedName = historicalFeedName;
160 | this.hostname = hostname;
161 | this.port = port;
162 | }
163 |
164 | @Override
165 | public PooledObject makeObject() throws Exception {
166 | HistoricalFeed historicalFeed = new HistoricalFeed(historicalFeedName, hostname, port);
167 | historicalFeed.start();
168 | historicalFeed.waitForProtocolVersionValidation(15, TimeUnit.SECONDS);
169 |
170 | return new DefaultPooledObject<>(historicalFeed);
171 | }
172 |
173 | @Override
174 | public void activateObject(PooledObject pooledObject) {}
175 |
176 | @Override
177 | public void passivateObject(PooledObject pooledObject) {}
178 |
179 | @Override
180 | public void destroyObject(PooledObject pooledObject) throws Exception {
181 | pooledObject.getObject().stop();
182 | }
183 |
184 | @Override
185 | public boolean validateObject(PooledObject pooledObject) {
186 | return pooledObject.getObject().isValid();
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------