-
list of stocks
-
retrieve latest prices from Yahoo Finance
This is the architecture of the application: image::src/docs/asciidoc/portfolio1.png[Architecture]
Let us start with an empty project by specifying the extensions on https://code.quarkus.io and download the resulting zip.
-
RESTeasy JAX-R
-
RESTeasy JSON-B
To make sure the development environment works, you start the development mode with Maven:
mvn compile quarkus:dev
In your browser, enter http://localhost:8080/hello and you will see "hello" as a response.
The retrieve the latest stock price, I add this dependency:
<dependency>
<groupId>com.yahoofinance-api</groupId>
<artifactId>YahooFinanceAPI</artifactId>
<version>3.15.0</version>
</dependency>
To get the stock information, including price:
Stock stock = YahooFinance.get(symbol);
It will be necessary get the latest stock price through a REST service, so we add the StockResource:
@Path("/{symbol}/latestPrice")
@GET
public Response stock(@PathParam("symbol") String symbol) throws IOException {
Stock stock = YahooFinance.get(symbol);
Response response = null;
if (stock == null) {
response = Response.status(Response.Status.NOT_FOUND).build();
} else {
StockLatestPriceResponse stockResponse = new StockLatestPriceResponse(stock.getSymbol(), stock.getQuote().getPrice());
response = Response.ok(stockResponse).build();
}
return response;
}
We can test this by accessing this URL: http://localhost:8080/stocks/AAPL/latestPrice
To bootstrap the Angular application, run this in the src/main directory:
ng new portfolio --skipGit --routing=true --style=scss
This creates a new directory "portfolio" with the Angular code. I rename that to "angular" to make it obvious that it contains Angular front-end code.
To run the front-end, change directory to the src/main/angular directory and run ng serve. When you enter "http://localhost:4200" in
your browser, you will see the example page with "portfolio app is running".
I add the PrimeNG package - this contains nice user interface components.
npm install --save primeng npm install --save primeicons npm install --save @angular/cdk npm install --save chart.js npm install --save @fullcalendar/core
Next I create a Portfolio page to display the list of stocks, and the service to retrieve the latest price.
ng generate component Portfolio ng generate service Stock
You can see how I implemented the page on my Github repository.
To retrieve the latest stock price, the StockService calls the REST endpoint implemented in Quarkus.
getStockLatestPrice(symbol: string): Observable<StockLatestPriceResponse> {
return this.http.get<StockLatestPriceResponse>(`/stocks/${symbol}/latestPrice`);
}
To check if it works, you can run ng serve again.
You will see errors in the browser console:
GET http://localhost:4200/stocks/AAPL/latestPrice 404 (Not Found) GET http://localhost:4200/stocks/GOOG/latestPrice 404 (Not Found)
The Angular service expects that the Quarkus service is available at the same URL prefix, and this is http://localhost:4200. The Quarkus service actually lives at http://localhost:8080, so will will need a proxy.
{
"/stocks": {
"target": "http://localhost:8080",
"secure": false
}
}
If you have started Quarkus with mvn compile quarkus:dev, then you can start the Angular app with ng serve --proxy-config proxy.conf.json.
So far, the Quarkus service and Angular application are separated. The Angular production build with ng build --prod produces static files
that can be served by Quarkus.
By default, ng build --prod puts all produced files in the dist directory. We want those files in the src/main/resources/META-INF/resources
directory. You can change that in the angular.json file:
"configurations": {
"production": {
"outputPath": "../resources/META-INF/resources",
After running ng build --prod, you can start Quarkus with mvn compile quarkus:dev and load the Angular app with http://localhost:8080/index.html
Now we have an application that we can deploy and run. Quarkus gives you the ability to run the application as a native executable.
When you build the application with mvn package -Pnative -Dquarkus.native.container-build=true -Dmaven.test.skip, it will build a
runner executable that contains everything it needs. This executable starts up very quickly and is great for running in a Docker container.
After building the executable, you can build a Docker image and run it:
docker build -f src/main/docker/Dockerfile.native -t quarkus/portfolio . docker run -i --rm -p 8080:80 quarkus/portfolio
After that, you can access the application at http://localhost:8080/index.html
See the Github repository at: https://github.com/koert/portfolio1
In my previous article I have setup an Angular application with a Quarkus backend and produced a Docker image. You can deploy this image directly with Docker, or run on a Kubernetes cluster. To evaluate how easy it is to deploy this image at AWS, I started looking at Amazon Elastic Container Service (AWS ECS).
After registering and installing command line tools.
aws iam --region eu-west-1 create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://config/task-execution-assume-role.json aws iam --region eu-west-1 attach-role-policy --role-name ecsTaskExecutionRole --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
ecs-cli configure --cluster portfolio --default-launch-type FARGATE --config-name portfolio --region eu-west-1
You have to setup an Administrator user in IAM and create an access key. Easiest way is to use the IAM console. .Configure profile
ecs-cli configure profile --access-key <ACCESS_KEY> --secret-key <SECRET_KEY> --profile-name portfolio-profile
ecs-cli up --cluster-config portfolio --ecs-profile portfolio-profile
#INFO[0000] Created cluster cluster=portfolio region=eu-west-1 #INFO[0000] Waiting for your cluster resources to be created... #INFO[0000] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS #INFO[0061] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS #VPC created: vpc-01234567890 #Subnet created: subnet-01231231231223123 #Subnet created: subnet-02342342342342344 #Cluster creation succeeded.
aws ec2 describe-security-groups --filters Name=vpc-id,Values=vpc-01234567890 --region eu-west-1
# "OwnerId": "091823891238", # "GroupId": "sg-01231231231231233",
aws ec2 authorize-security-group-ingress --group-id sg-01231231231231233 --protocol tcp --port 80 --cidr 0.0.0.0/0 --region eu-west-1 aws ec2 authorize-security-group-ingress --group-id sg-01231231231231233 --protocol tcp --port 8080 --cidr 0.0.0.0/0 --region eu-west-1
ecs-cli compose --project-name portfolio service up --create-log-groups --cluster-config portfolio --ecs-profile portfolio-profile
#INFO[0000] Using ECS task definition TaskDefinition="portfolio:3" #WARN[0000] Failed to create log group portfolio in eu-west-1: The specified log group already exists #INFO[0000] Created an ECS service service=portfolio taskDefinition="portfolio:3" #INFO[0001] Updated ECS service successfully desiredCount=1 force-deployment=false service=portfolio #INFO[0016] (service portfolio) has started 1 tasks: (task b0161234-bde5-44c1-1234-3d66caab1233). timestamp="2020-02-06 14:07:16 +0000 UTC" #INFO[0046] Service status desiredCount=1 runningCount=1 serviceName=portfolio #INFO[0046] ECS Service has reached a stable state desiredCount=1 runningCount=1 serviceName=portfolio
ecs-cli compose --project-name portfolio service ps --cluster-config portfolio --ecs-profile portfolio-profile
#Name State Ports TaskDefinition Health #b0161234-bde5-44c1-1234-3d66caab1233/web RUNNING 163.135.225.218:8080->8080/tcp portfolio:3 UNKNOWN
Now the application is running and you can access it at the listen IP address and port.
ecs-cli logs --task-id b0161234-bde5-44c1-1234-3d66caab1233 --follow --cluster-config portfolio --ecs-profile portfolio-profile
It runs on just one container - you can scale it up with a simple command.
ecs-cli compose --project-name portfolio service scale 2 --cluster-config portfolio --ecs-profile portfolio-profile
ecs-cli compose --project-name portfolio service ps --cluster-config portfolio --ecs-profile portfolio-profile
You will now see 2 IP addresses and you can access both instances. Normally you would setup a load balancer that sends traffic to both instances. This is beyond the scope of this article.
Let’s say that you made some improvements and want to deploy a new version. I could not find the option to do this with ecs-cli, but it is pretty straight forward with the "aws ecs update-service" command command.
aws ecs update-service --service portfolio --cluster portfolio --force-new-deployment
This will first deploy the new version, keep both version running for a short time and then removes the old instance.
The clean up your experimental deployment, you first stop the instance and then delete the cluster.
ecs-cli compose --project-name portfolio service down --cluster-config portfolio --ecs-profile portfolio-profile
#INFO[0000] Updated ECS service successfully desiredCount=0 force-deployment=false service=portfolio #INFO[0000] Service status desiredCount=0 runningCount=1 serviceName=portfolio #INFO[0015] Service status desiredCount=0 runningCount=0 serviceName=portfolio #INFO[0015] (service portfolio) has stopped 1 running tasks: (task b0161234-bde5-44c1-1234-3d66caab1233). timestamp="2020-02-06 10:56:53 +0000 UTC" #INFO[0015] ECS Service has reached a stable state desiredCount=0 runningCount=0 serviceName=portfolio #INFO[0015] Deleted ECS service service=portfolio #INFO[0015] ECS Service has reached a stable state desiredCount=0 runningCount=0 serviceName=portfolio
ecs-cli down --force --cluster-config portfolio --ecs-profile portfolio-profile