Skip to content

koert/portfolio1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Portfolio1

The features of the first version:
  • list of stocks

  • retrieve latest prices from Yahoo Finance

This is the architecture of the application: image::src/docs/asciidoc/portfolio1.png[Architecture]

Project setup

Let us start with an empty project by specifying the extensions on https://code.quarkus.io and download the resulting zip.

The necessary extensions:
  • 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.

Add Yahoo finance API dependency

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);

Stock REST resource

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

Angular front-end

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

Portfolio page

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.

Stock price service

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.

proxy.conf.json
{
  "/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.

Combine Quarkus and Angular

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:

angular.json
  "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

Running the application

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

Deployment on Amazon Web Services

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.

Setting up security policy
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
Configure a cluster
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
Create the cluster
ecs-cli up --cluster-config portfolio --ecs-profile portfolio-profile
Output
#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.
Find group ID
aws ec2 describe-security-groups --filters Name=vpc-id,Values=vpc-01234567890 --region eu-west-1
Output
#  "OwnerId": "091823891238",
#  "GroupId": "sg-01231231231231233",
Authorize ports
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
Bring the cluster up
ecs-cli compose --project-name portfolio service up --create-log-groups --cluster-config portfolio --ecs-profile portfolio-profile
Output
#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
Find out IP address
ecs-cli compose --project-name portfolio service ps --cluster-config portfolio --ecs-profile portfolio-profile
Output
#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.

Examine the logs
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.

Scaling - use 2 containers
ecs-cli compose --project-name portfolio service scale 2 --cluster-config portfolio --ecs-profile portfolio-profile
Find out scaled up containers and IP addresses
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.

Update new deployment

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.

Update image
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.

Clean up

The clean up your experimental deployment, you first stop the instance and then delete the cluster.

Stop the instance
ecs-cli compose --project-name portfolio service down --cluster-config portfolio --ecs-profile portfolio-profile
Output
#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
Delete cluster
ecs-cli down --force --cluster-config portfolio --ecs-profile portfolio-profile

Conclusion

I am not an AWS wizard, but I found it reasonably easy to setup a cluster and deploy the application. To make the application ready for real world use, there is much more to do, like user registration/login, load balancing, data persistance to a database, etc.

About

Demo application with stock portfolio - Angular front-end, Quarkus backend

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors