diff --git a/.gitignore b/.gitignore
index eb5a316..a0ed885 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,31 @@
target
+### Java template
+*.class
+
+*.iml
+
+.idea
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/
+
+/src/main/resources/application-local.properties
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+### Maven template
+target/
diff --git a/README.md b/README.md
index 9252965..7153ae5 100644
--- a/README.md
+++ b/README.md
@@ -1,46 +1,58 @@
-java-api-streaming
+FX-OANDA-Streaming
==================
-A simple demo app in Java for getting streaming rates using OANDA
-API. This example uses [apache httpcomponents](http://hc.apache.org/httpcomponents-client-ga/) for https
-connections and [json-simple](https://code.google.com/p/json-simple/) for json decoding.
+A simple demo app in Java 8 using Spring Boot for getting streaming rates using OANDA
+API. This example uses [Spring RestTemplate](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) for https
+connections.
### Setup
Clone this repo to the location of your choice.
Modify the following variables in
-[JavaApiStreaming.java](src/main/java/JavaApiStreaming.java):
+[application.properites](src/main/resources/application.properties):
domain
access_token
account_id
- instruments
+ instruments
+
+Make sure you separate the instruments by comma (e.g: EUR_USD,USD_JPY,EUR_JPY).
+
Maven is used for building. Install from http://maven.apache.org/download.cgi.
-On Ubuntu, you can run `sudo apt-get install maven`.
+On Ubuntu, you can run `sudo apt-get install maven`. On Mac using brew `brew install maven`
To create and execute the jar file, run
- mvn package
- java -jar target/streaming-example-java-1.jar
+ mvn clean package
+ java -jar target/oanda.streaming-0.0.1-SNAPSHOT.jar
### Sample Output
- EUR_USD
- 2014-03-21T17:56:09.932922Z
- 1.37912
- 1.37923
- -------
- USD_CAD
- 2014-03-21T17:56:20.776248Z
- 1.12011
- 1.12029
- -------
- USD_JPY
- 2014-03-21T17:56:13.668154Z
- 102.262
- 102.275
+ 2016-04-10 14:28:57.068 INFO 84369 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
+ 2016-04-10 14:28:57.121 INFO 84369 --- [ restartedMain] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
+ 2016-04-10 14:28:57.132 INFO 84369 --- [ restartedMain] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
+ 2016-04-10 14:28:57.305 INFO 84369 --- [ restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
+ 2016-04-10 14:28:58.809 INFO 84369 --- [ restartedMain] i.c.fx.oanda.streaming.FXOANDAStreaming : {
+ "instrument" : "EUR_USD",
+ "bid" : 1.13929,
+ "ask" : 1.14029,
+ "time" : "2016-04-08T21:08:58.499"
+ }
+ 2016-04-10 14:28:58.813 INFO 84369 --- [ restartedMain] i.c.fx.oanda.streaming.FXOANDAStreaming : {
+ "instrument" : "USD_JPY",
+ "bid" : 108.028,
+ "ask" : 108.128,
+ "time" : "2016-04-08T21:09:00.391"
+ }
+ 2016-04-10 14:28:58.819 INFO 84369 --- [ restartedMain] i.c.fx.oanda.streaming.FXOANDAStreaming : {
+ "instrument" : "EUR_JPY",
+ "bid" : 123.133,
+ "ask" : 123.233,
+ "time" : "2016-04-08T21:08:59.84"
+ }
+
### More Information
diff --git a/pom.xml b/pom.xml
index 8298b6a..cb9f2bb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,69 +1,57 @@
-
4.0.0
- Oanda
- streaming-example-java
- 1
+ io.cax.fx
+ oanda.streaming
+ 0.0.1-SNAPSHOT
+ jar
+
+ oanda.streaming
+ Demo FX OANDA Streaming with Spring Boot
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.3.3.RELEASE
+
+
+
+
+ UTF-8
+ 1.8
+
- org.apache.httpcomponents
- httpclient
- 4.3
- compile
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
- com.googlecode.json-simple
- json-simple
- 1.1
- compile
+ org.springframework.boot
+ spring-boot-starter-test
+ test
- org.apache.maven.plugins
- maven-compiler-plugin
- 2.3.2
+ org.springframework.boot
+ spring-boot-maven-plugin
-
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- true
- dependency-jars/
- JavaApiStreaming
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- 2.5.1
-
-
- copy-dependencies
- package
-
- copy-dependencies
-
-
- ${project.build.directory}/dependency-jars/
-
-
-
-
-
+
diff --git a/src/main/java/JavaApiStreaming.java b/src/main/java/JavaApiStreaming.java
deleted file mode 100644
index 3bf5e57..0000000
--- a/src/main/java/JavaApiStreaming.java
+++ /dev/null
@@ -1,80 +0,0 @@
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.util.Date;
-
-import org.apache.http.*;
-import org.apache.http.client.methods.*;
-import org.apache.http.impl.client.BasicResponseHandler;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.client.HttpClient;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.util.EntityUtils;
-
-import org.json.simple.JSONObject;
-import org.json.simple.JSONValue;
-
-public class JavaApiStreaming {
- public static void main (String[]args) throws IOException {
-
- HttpClient httpClient = HttpClientBuilder.create().build();
-
- try {
-
- // Set these variables to whatever personal ones are preferred
- String domain = "https://stream-fxpractice.oanda.com";
- String access_token = "ACCESS-TOKEN";
- String account_id = "1234567";
- String instruments = "EUR_USD,USD_JPY,EUR_JPY";
-
-
- HttpUriRequest httpGet = new HttpGet(domain + "/v1/prices?accountId=" + account_id + "&instruments=" + instruments);
- httpGet.setHeader(new BasicHeader("Authorization", "Bearer " + access_token));
-
- System.out.println("Executing request: " + httpGet.getRequestLine());
-
- HttpResponse resp = httpClient.execute(httpGet);
- HttpEntity entity = resp.getEntity();
-
- if (resp.getStatusLine().getStatusCode() == 200 && entity != null) {
- InputStream stream = entity.getContent();
- String line;
- BufferedReader br = new BufferedReader(new InputStreamReader(stream));
-
- while ((line = br.readLine()) != null) {
-
- Object obj = JSONValue.parse(line);
- JSONObject tick = (JSONObject) obj;
-
- // unwrap if necessary
- if (tick.containsKey("tick")) {
- tick = (JSONObject) tick.get("tick");
- }
-
- // ignore heartbeats
- if (tick.containsKey("instrument")) {
- System.out.println("-------");
-
- String instrument = tick.get("instrument").toString();
- String time = tick.get("time").toString();
- double bid = Double.parseDouble(tick.get("bid").toString());
- double ask = Double.parseDouble(tick.get("ask").toString());
-
- System.out.println(instrument);
- System.out.println(time);
- System.out.println(bid);
- System.out.println(ask);
- }
- }
- } else {
- // print error message
- String responseString = EntityUtils.toString(entity, "UTF-8");
- System.out.println(responseString);
- }
-
- } finally {
- httpClient.getConnectionManager().shutdown();
- }
- }
-}
diff --git a/src/main/java/io/cax/fx/oanda/streaming/FXOandaStreaming.java b/src/main/java/io/cax/fx/oanda/streaming/FXOandaStreaming.java
new file mode 100644
index 0000000..c90c066
--- /dev/null
+++ b/src/main/java/io/cax/fx/oanda/streaming/FXOandaStreaming.java
@@ -0,0 +1,85 @@
+package io.cax.fx.oanda.streaming;
+
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.cax.fx.oanda.streaming.domain.Tick;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponents;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.io.IOException;
+import java.util.Scanner;
+
+
+@SpringBootApplication
+public class FXOANDAStreaming implements CommandLineRunner{
+
+ Logger logger = LoggerFactory.getLogger(FXOANDAStreaming.class);
+
+ @Value("${oanda.access_token}")
+ private String accessToken;
+
+ @Value("${oanda.account_id}")
+ private String accountId;
+
+
+ @Value("${oanda.domain}")
+ private String domain;
+
+ @Value("${oanda.instruments}")
+ private String instruments;
+
+ private RestOperations restTemplate;
+
+ public static void main (String[]args) throws IOException {
+ SpringApplication.run(FXOANDAStreaming.class, args);
+ }
+
+ @Override
+ public void run(String... strings) throws Exception {
+
+ restTemplate = new RestTemplate();
+
+
+ UriComponents uriComponents = UriComponentsBuilder.fromUriString(domain).path("/v1/prices").queryParam("accountId",accountId).queryParam("instruments",instruments).build();
+
+ restTemplate.execute(uriComponents.toUriString(),
+ HttpMethod.GET,
+ clientHttpRequest -> clientHttpRequest.getHeaders().add("Authorization","Bearer " + accessToken),
+ clientHttpResponse -> {
+ try(Scanner scanner = new Scanner(clientHttpResponse.getBody(),"utf-8")){
+
+ while(scanner.hasNext()) formatPrintContent(scanner.nextLine());
+ }
+ return new ResponseEntity<>(HttpStatus.OK);
+ });
+ }
+
+ private void formatPrintContent(String content){
+
+ ObjectMapper mapper = new ObjectMapper();
+
+ try {
+
+ mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
+ Tick tick = mapper.readValue(content,Tick.class);
+ logger.info(mapper.writer(new DefaultPrettyPrinter()).writeValueAsString(tick));
+
+ } catch (IOException e) {
+ logger.debug("heartbeat not processed: " + e.getMessage());
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/cax/fx/oanda/streaming/domain/Tick.java b/src/main/java/io/cax/fx/oanda/streaming/domain/Tick.java
new file mode 100644
index 0000000..dbd17d8
--- /dev/null
+++ b/src/main/java/io/cax/fx/oanda/streaming/domain/Tick.java
@@ -0,0 +1,68 @@
+package io.cax.fx.oanda.streaming.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonRootName;
+
+import java.util.Date;
+
+/**
+ * Created by cq on 10/4/16.
+ */
+@JsonRootName(value = "tick")
+public class Tick {
+
+ //{"tick":{"instrument":"EUR_USD","time":"2016-04-08T20:59:58.540499Z","bid":1.13929,"ask":1.14029}}
+ private String instrument;
+
+ @JsonProperty("time")
+ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.S", locale = "ENGLISH")
+ private Date dateTime;
+
+ private double bid;
+
+ private double ask;
+
+ public String getInstrument() {
+ return instrument;
+ }
+
+ public void setInstrument(String instrument) {
+ this.instrument = instrument;
+ }
+
+ public Date getDateTime() {
+ return dateTime;
+ }
+
+ public void setDateTime(Date dateTime) {
+ this.dateTime = dateTime;
+ }
+
+ public double getBid() {
+ return bid;
+ }
+
+ public void setBid(double bid) {
+ this.bid = bid;
+ }
+
+ public double getAsk() {
+ return ask;
+ }
+
+ public void setAsk(double ask) {
+ this.ask = ask;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Tick{");
+ sb.append("instrument='").append(instrument).append('\'');
+ sb.append(", dateTime=").append(dateTime);
+ sb.append(", bid=").append(bid);
+ sb.append(", ask=").append(ask);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
new file mode 100644
index 0000000..2254c61
--- /dev/null
+++ b/src/main/resources/application.properties
@@ -0,0 +1,4 @@
+oanda.domain=
+oanda.access_token=
+oanda.account_id=
+oanda.instruments=
\ No newline at end of file