Tuesday, October 29, 2024

A Real-World Guide to Cross-Account SQS Publisher-Consumer with AWS - Part 2

This is the second part of the blog. For context on the problem and additional details, please refer to Part 1.

Following the successful results of the Proof of Concept (POC), Alex demonstrated the concept to his team. However, he soon discovered that a similar initiative had previously been attempted by another developer. Unfortunately, that effort was halted midway due to a KMS error encountered when a Lambda function in the "Sales Account" attempted to send or read messages in the Data Processing Account. The error message was as follows:


"An error occurred (KMS.AccessDeniedException) when calling the SendMessage operation: User: arn:aws:iam::xxxxx:user/xxxx is not authorized to perform: kms:GenerateDataKey on resource: arn:aws:kms:us-east-1:xxxx:key/xxxx with an explicit deny."

Upon further investigation, Alex learned that, according to the company’s security policy, all messages in transit and at rest must be encrypted. To enforce this requirement, the company's security team had provisioned a Customer Master Key (CMK) in KMS. The previous developer had utilized this existing CMK for SQS to comply with the company’s encryption policy.

Determined to overcome this challenge, Alex decided to extend his POC to leverage the existing KMS key within the company. Below is the first version of the templates for the extended POC:

CloudFormation stack prepared for deployment in the Data Processing Account


AWSTemplateFormatVersion: '2010-09-09'
Description: SQS Queues for Sales and Processing with Cross-Account Access and KMS Key

Parameters:
  SalesAccountId:
    Type: String
    Description: The AWS account ID of the Sales Department

Resources:
  # SQS Queue for requests from Sales to Processing
  SalesToProcessingRequestQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: salesToProcessingRequestQueue
      KmsMasterKeyId: !ImportValue KMSKeyId  # Import the KMS Key ID from another stack

  # SQS Queue for responses from Processing to Sales
  ProcessingToSalesResponseQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: processingToSalesResponseQueue
      KmsMasterKeyId: !ImportValue KMSKeyId  # Import the KMS Key ID from another stack

  # Policy to allow cross-account access for the Sales to Processing request queue
  SalesToProcessingRequestQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref SalesToProcessingRequestQueue
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "sqs:SendMessage"
            Principal:
              AWS: !Sub "arn:aws:iam::${SalesAccountId}:root"
            Resource: !GetAtt SalesToProcessingRequestQueue.Arn

  # Policy to allow cross-account access for the Processing to Sales response queue
  ProcessingToSalesResponseQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref ProcessingToSalesResponseQueue
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${SalesAccountId}:root"
            Action:
              - "sqs:ReceiveMessage"
              - "sqs:DeleteMessage"
              - "sqs:GetQueueAttributes"
            Resource: !GetAtt ProcessingToSalesResponseQueue.Arn




CloudFormation stack prepared for deployment in the Sales Account


Parameters:
  DataProcessingAccountId:
    Type: String
    Description: Account ID of the Data Processing Department

  DataProcessingAccountRegion:
    Type: String
    Default: eu-west-1
    Description: Region of the Data Processing Department where the SQS queue is located
    
  DataProcessingAccountKmsKeyId:
    Type: String
    Description: KMS Key ID to be used by Lambda for encrypting & decrypting data

Resources:
  # IAM Role for Lambda to send messages to SalesToProcessingRequestQueue
  LambdaSendRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: LambdaSendPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "sqs:SendMessage"
                Resource: !Sub "arn:aws:sqs:${DataProcessingAccountRegion}:${DataProcessingAccountId}:salesToProcessingRequestQueue"
              - Effect: Allow
                Action:
                  - "kms:GenerateDataKey"
                Resource: !Sub "arn:aws:kms:${DataProcessingAccountRegion}:${DataProcessingAccountId}:key/${DataProcessingAccountKmsKeyId}"

  # Python Lambda Function to Send Message to SalesToProcessingRequestQueue
  SendMessageLambda:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaSendRole.Arn
      Runtime: python3.8
      Code:
        ZipFile: |
          import boto3
          import os

          def lambda_handler(event, context):
              sqs = boto3.client('sqs', region_name=os.environ['DATA_PROCESSING_ACCOUNT_REGION'])
              queue_url = f"https://sqs.{os.environ['DATA_PROCESSING_ACCOUNT_REGION']}.amazonaws.com/{os.environ['DATA_PROCESSING_ACCOUNT_ID']}/salesToProcessingRequestQueue"
              response = sqs.send_message(
                  QueueUrl=queue_url,
                  MessageBody="Sample message"
              )
              print(f"Message sent with ID: {response['MessageId']}")

      Environment:
        Variables:
          DATA_PROCESSING_ACCOUNT_ID: !Ref DataProcessingAccountId
          DATA_PROCESSING_ACCOUNT_REGION: !Ref DataProcessingAccountRegion           
                
  # IAM Role for Lambda with permissions to access logs and SQS
  LambdaRoleForSQSAccess:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: LambdaSQSPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "arn:aws:logs:*:*:*"
              - Effect: Allow
                Action:
                  - "sqs:ReceiveMessage"
                  - "sqs:DeleteMessage"
                  - "sqs:GetQueueAttributes"
                Resource: !Sub "arn:aws:sqs:${DataProcessingAccountRegion}:${DataProcessingAccountId}:processingToSalesResponseQueue"
              - Effect: Allow
                Action:
                  - "kms:Decrypt"
                Resource: !Sub "arn:aws:kms:${DataProcessingAccountRegion}:${DataProcessingAccountId}:key/${DataProcessingAccountKmsKeyId}"

  # Lambda Function to Process Messages from ProcessingToSalesResponseQueue
  ProcessMessageLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: "ProcessMessageLambda"
      Handler: index.lambda_handler
      Role: !GetAtt LambdaRoleForSQSAccess.Arn
      Runtime: python3.8
      Code:
        ZipFile: |
          import json

          def lambda_handler(event, context):
              for record in event['Records']:
                  message_body = record['body']
                  print("Received message:", message_body)
                  # Process message logic here

  # SQS Trigger for Lambda
  LambdaSQSTrigger:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 10
      EventSourceArn: !Sub "arn:aws:sqs:${DataProcessingAccountRegion}:${DataProcessingAccountId}:processingToSalesResponseQueue"
      FunctionName: !Ref ProcessMessageLambda

Please note that Alex used the existing KMSKeyId in various places within the updated templates and provided the necessary permissions for the Lambda functions to send and receive messages from the cross-account SQS.

Alex then tested the Lambda function in the AWS console to generate sample send messages but ran into the same error. Even though he didn't know the exact reason for the issue, he was quite pleased to have been able to reproduce it in his local stack!However soon he discovered that the existing security stack wasn’t granting the necessary permissions for cross-account calls. He prepared a sample KMS key policy and successfully convinced the security team to update their KMS key policy to accommodate the cross-account use case. Below is a sample template prepared by Alex to demonstrate how the template looks (note that the actual key policy is more restricted; this is just for demonstration purposes).


Example CloudFormation stack prepared for KMS Key provisioning in the Data Processing Account


AWSTemplateFormatVersion: '2010-09-09'
Description: Create a new customer-managed KMS key with cross-account access

Parameters:
  SalesAccountId:
    Type: String
    Description: The account ID of the SalesAccountId

Resources:
  MyKMSKey:
    Type: 'AWS::KMS::Key'
    Properties:
      Description: 'Example KMS key for encrypting/decripting data'
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
          # Full access for the data processing account
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'

          # Cross-account access for the sales account
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${SalesAccountId}:root"
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:GenerateDataKey'
            Resource: '*'
      KeyUsage: ENCRYPT_DECRYPT
      Origin: AWS_KMS

Outputs:
  KMSKeyId:
    Description: 'The ID of the created KMS key'
    Value: !Ref MyKMSKey
    Export:
      Name: KMSKeyId


After being informed by the security team that they had successfully updated the KMS key policy, Alex retried the Lambda function and received successful invocations. He logged into the data processing account and verified that the invocation was working as expected.

Sunday, October 27, 2024

A Real-World Guide to Cross-Account SQS Publisher-Consumer with AWS - Part 1

1. Scenario

Imagine a rapidly growing company called DataCorp. As the company expands, it currently operates with two main departments: Sales and Data Processing. Each department has its own unique role and responsibilities, contributing to the company’s success as it continues to grow. Each department operates within its own AWS account and utilizes EC2 instances to host its applications.

1.1 Existing Setup

  • Sales Department: This team collects and processes customer data on its EC2 instances. Once they prepare the data, they manually transfer it—either physically or through secure shared drives and emails—to the Data Processing Department for further enrichment.

  • Data Processing Department: This team also operates on EC2 instances, where they enrich, validate, and finalize the customer data. After processing, they manually send the enriched data back to Sales, creating further delays in customer engagement and record-keeping.







While both departments excel in their specific roles, the absence of a direct connection between their applications has resulted in data-sharing bottlenecks. This situation leads to inefficiencies and delays that impact the overall performance of DataCorp, hindering timely decision-making and customer responsiveness.

2. The Solution: Automating with AWS Lambda And SQS

Alex, a junior AWS developer at DataCorp, saw an opportunity to streamline the workflow between the Sales and Data Processing Departments. He discovers that the Sales Department currently uses AWS S3 to store both their generated and enriched files. With this setup in mind, Alex envisions an automation strategy using AWS Lambda and Amazon SQS to create seamless communication between the two departments, allowing data to flow more efficiently and reducing the need for manual data handoffs.

Here’s what Alex proposes:




To kick things off, Alex started with a proof of concept (POC) to demonstrate cross-account data access capabilities. He developed two initial CloudFormation templates—one for each AWS account—to test the SQS producer-consumer functionality across accounts, focusing first on setting up the necessary permissions.

2.1 Scope For the 1st POC




CloudFormation stack prepared for deployment in the Data Processing Account

Parameters:
  SalesAccountId:
    Type: String
    Description: The AWS account ID of the Sales Department 

Resources:
  SalesToProcessingRequestQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: salesToProcessingRequestQueue

  ProcessingToSalesResponseQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: processingToSalesResponseQueue

  SalesToProcessingRequestQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref SalesToProcessingRequestQueue
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "sqs:SendMessage"
            Principal:
              AWS: !Sub "arn:aws:iam::${SalesAccountId}:root"
            Resource: !GetAtt SalesToProcessingRequestQueue.Arn

  ProcessingToSalesResponseQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref ProcessingToSalesResponseQueue
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub "arn:aws:iam::${SalesAccountId}:root"
            Action:
              - "sqs:ReceiveMessage"
              - "sqs:DeleteMessage"
              - "sqs:GetQueueAttributes"
            Resource: !GetAtt ProcessingToSalesResponseQueue.Arn


CloudFormation stack prepared for deployment in the Sales Account


Parameters:
  DataProcessingAccountId:
    Type: String
    Description: Account ID of the Data Processing Department

  DataProcessingAccountRegion:
    Type: String
    Default: eu-west-1
    Description: Region of the Data Processing Department where the SQS queue is located

Resources:
  # IAM Role for Lambda to send messages to SalesToProcessingRequestQueue
  LambdaSendRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: LambdaSendPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "sqs:SendMessage"
                Resource: !Sub "arn:aws:sqs:${DataProcessingAccountRegion}:${DataProcessingAccountId}:salesToProcessingRequestQueue"

  # Python Lambda Function to Send Message to SalesToProcessingRequestQueue
  SendMessageLambda:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.lambda_handler
      Role: !GetAtt LambdaSendRole.Arn
      Runtime: python3.8
      Code:
        ZipFile: |
          import boto3
          import os

          def lambda_handler(event, context):
              sqs = boto3.client('sqs', region_name=os.environ['DATA_PROCESSING_ACCOUNT_REGION'])
              queue_url = f"https://sqs.{os.environ['DATA_PROCESSING_ACCOUNT_REGION']}.amazonaws.com/{os.environ['DATA_PROCESSING_ACCOUNT_ID']}/salesToProcessingRequestQueue"
              response = sqs.send_message(
                  QueueUrl=queue_url,
                  MessageBody="Sample message"
              )
              print(f"Message sent with ID: {response['MessageId']}")

      Environment:
        Variables:
          DATA_PROCESSING_ACCOUNT_ID: !Ref DataProcessingAccountId
          DATA_PROCESSING_ACCOUNT_REGION: !Ref DataProcessingAccountRegion           
                
  # IAM Role for Lambda with permissions to access logs and SQS
  LambdaRoleForSQSAccess:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: LambdaSQSPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "arn:aws:logs:*:*:*"
              - Effect: Allow
                Action:
                  - "sqs:ReceiveMessage"
                  - "sqs:DeleteMessage"
                  - "sqs:GetQueueAttributes"
                Resource: !Sub "arn:aws:sqs:${DataProcessingAccountRegion}:${DataProcessingAccountId}:processingToSalesResponseQueue"

  # Lambda Function to Process Messages from ProcessingToSalesResponseQueue
  ProcessMessageLambda:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: "ProcessMessageLambda"
      Handler: index.lambda_handler
      Role: !GetAtt LambdaRoleForSQSAccess.Arn
      Runtime: python3.8
      Code:
        ZipFile: |
          import json

          def lambda_handler(event, context):
              for record in event['Records']:
                  message_body = record['body']
                  print("Received message:", message_body)
                  # Process message logic here

  # SQS Trigger for Lambda
  LambdaSQSTrigger:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 10
      EventSourceArn: !Sub "arn:aws:sqs:${DataProcessingAccountRegion}:${DataProcessingAccountId}:processingToSalesResponseQueue"
      FunctionName: !Ref ProcessMessageLambda

While Alex's proof of concept (POC) successfully demonstrated the basic functionality of cross-account communication between the Sales and Data Processing departments using AWS Lambda and Amazon SQS, several factors need to be considered for real implementation to ensure the system works effectively in a production environment.

  • What is the maximum message size allowed in SQS?
  • How will you handle sensitive data in messages?
  • How will you ensure compatibility with existing systems?
  • How do you plan for future growth in message volume and processing needs?
  • Etc


Saturday, May 11, 2019

What do I really need? Vm ,Vagrant or Docker



If we are working on windows and our production environment is in Linux machine, then sometimes our code may give problems in the production environment. So, we need to set the development environment as same as production.

In order to do that we can install Virtual box (or any other VMware) in our host PC and install relevant Linux OS and install required software inside the VM and run our application exactly same as the production.

BUT...WE HERE STORIES LIKE...


  • IT works on my machine. But it is not working in other developers’ machine.
  • We have to spend a lot more time to setup the development environment.
  • Sometimes we use LAMP in development machine and production servers use individual installment, may cause issues.
  • Some software version issues or OS update may give issues. Ex: We both using windows 10. When I try to install MYSQL server, it causes errors but other developers machine not giving such bad experience(actually good experience 😊).
  • Since I am a newcomer to this company/team, I need more time to set up production similar environment.

So in order to remove those types of issues, we can use "Vagrant" which is a virtual machine manager. It allows you to script the virtual machine configuration as well as the provisioning.

In the following figure, VirtualBox is used as a platform to create VMs however Vagrant is used to configuring the VM by setting OS version, Network address, Memory allocation, number of CPUs assigned etc.

https://www.softqubes.com/blog/introduction-of-vagrant-development/

Still Not Clear ..How Vagrant apply in our day to day development?

Let's imagine a new developer come to our project. Then he/she needs to set up the development environment similar to our production environment which uses Redhat OS.
But that developer really familiar with Windows environment. So he needs VirtualBox to simulate the production environment while test the code. 

But we have to spend a considerable amount of time to correctly configure the production environment locally. 

WHAT IF FIVE NEW DEVELOPERS HIRED FOR URGENT PROJECT RESOURCE REQUIREMENT THEN WHAT DO YOU THINK ABOUT THIS TIME? 

Each and every developer need to do the same thing in their own machines. Isn't it?

Solution: Keep one scripting file which has same configuration details as production. When new developers hired then they need to execute provisioning script to set up the local development environment.

Practical Usage Of Vagrant
Step 01: Download the provisioning script and execute.
Step 02: Download project, build and test.

Problem: BUT...VIRTUALIZATION  COMES WITH  A COST

Ex: Each VM needs a separate OS.

Solution: Containerization

So, we can use Docker instead of  VM (it depends on situation. Sometimes vagrant is the best for your situation).
In docker, we configure everything to run the code and we create a docker image using those code and data. Then we deploy that container. So, we do not need individual OSs. Docker uses OS kernel so no more cost than VM.

Practical Usage Of Containerization
Step 01: Download project code with docker.
Step 02: Build docker image file
Step 03: Deploy it anywhere where docker container platform installed

How Doker Link With Cloud?

Step 01: Build docker image
Step 02: Upload image to Docker Hub
Step 03: Create containers based on application scalability needs

If you have any question, feel free to comment o it.


Tuesday, March 19, 2019

Why We Need Observer Design Pattern



Observer Design Pattern



This is a one of the famous behavioral design patterns in the object-oriented world.
  • Why we need observer design pattern? 
  • What problem it solves?

      In order to answer those questions, we need to analysis the software design & development practical scenarios.So lets closely looks in to below scenario:
  •       Develop a software by WITHOUT applying observer pattern 
  •       Develop a software by applying observer pattern
     After the analysis, you can answer to above questions by yourself as well as you could get an idea where to apply this pattern in day to day software development projects.

    Let’s continue the Weather Station Story......

      As per the agreement John successfully completed the data feeding application using RMI. But still Volta (weather forecasting company) use paper based reports to presented their analysis reports and those reports print per hourly basis. When weather data interested party (government, media, etc.) wants to know the current weather condition or predictions, they need to contact Volta's telephone operator through telephone. Then telephone operator read data from the printed reports and respond to caller. 
(Image source: Iron Age Tattoo)

      Volta management wanted to optimize this process. So, they decided to mount 2 big displays in the front office to display current weather conditions and foretasted results. Management expected to automatically update displays as soon as automated analysis completed. Then telephone operator can easily present with up to date forecasting results.

      Due to successful of previous project, Volta management decided to give this project to One Soft Solutions. But this time One Soft Solutions senior engineer (John) was getting his annual leave. After a One Soft Solutions management discussion through telephone call John agreed to give some guidelines to his junior developers to continue the project until he come. As promised John, he ask to do this job for one of his junior team member (Devid).

     John prepared a task sheet and share with his Devid to fallow.
  1.       Assign the two front end UI design & development to two separate teams (Team A, Team B)
  2.       Create a new method called “updateDisplay” in “WeatherStation.java class” and write code to update the 2 remote displays which will develop by two UI teams.
  3.       Call updateDisplay method at the end of data analysis code then it will update the displays accordingly.
     Devid discuss with rest of the team and draw a diagram to illustrate what they are going to do.

Figure 1.1 communication workflow 

    Team A – Developed the “CurrentConditionDisplay”

      Team A used RMI to expose their front end object to 'WeatherStation'.Then WeatherStation can remotely invoke the UI object to update the display board (More details of RMI :RMI Post).
      
     They had used "Display" interface.

Figure 1.3 Remote Interface For Current Condition Display


      

      Team B – Developed the “ForecastConditionDisplay”

    They also followed the same process (Team A's).
Figure 1.4 Remote Interface For Forecast Condition Display


You can download the full source code:

     Meanwhile Devid and team had developed the WeatherStation back-end code base.They have retrieved the remote display objects and called the "exposeDataToDisplays" method to update remote objects status.

Figure 1.5 Remote Object Update
 You can download the full source code:

      Seems like everything is ok. All the client expected functions are in there.

     After two weeks....

    John   : Hi Devid ..I reviewed our weather station program
    Devid : Aha..We did that before expected date
    John   : Yes..You guys did it well..But currently display clients are tightly couple to our code.Right....
    Devid : Yeah..
    John   : What is the possibility to add new Display type with minimum changes to existing code.
    Devid : I understood the problem..But I don't have a clear idea how to do that.
    John   : 
  •        You could create common interface ("Display.java") 
  •        Ask all the UI teams to use that interface as their remote interface.
  •       Then you could create list of  display clients.
  •       Add all the remote display client's object to that array list.
  •       Then you can update all the display objects in one-click.
    Devid: What a cool thing..You decided it very well..
    John  : I proposed this solution by keeping "Observer Design Pattern" in mind.You also better to go through that design pattern and try to reduce coupling.I mean 

            "Check weather there is a way to expose weather data as API and clients could subscribe the weather service by themselves





Sunday, November 18, 2018

Circular Dependencies In Spring


Circular Dependencies In Spring


In order to understand the circular dependency scenario, we need to first understand "How Spring Inject Dependencies" in normal situation.

How Spring Inject Dependencies - Normal Situation

Let's take an example:
In our application we have beans A,B & C and they are depend on one another like below.


This is how spring resolve dependency injection process for our example scenario:


Above process illustrates by below code snippet. Here is our Main application :
Bean Files:

But...How It Works In Circular Dependency Situation ?

For the simplicity ,Lets assume A and B two beans are depend like below:



What Happen Next??

when having a circular dependency, Spring cannot decide which of the beans should be created first, since one bean depend on one another. In those type of situations , Spring will complain my throwing "BeanCurrentlyInCreationException" while loading the application context. 

But "I am not getting such exception?? Here is my code "




Yes..In this case you are using field level dependency injection.So spring is intelligent to identify such kind of scenario and it can fix the issue by itself.

  • Create Bean A & B first
  • Inject dependencies to each other 
This is how spring documentation states the problem:

 
References:

Wednesday, November 7, 2018

RMI IN ACTION


RMI (Remote Method Invocation)

RMI was most widely used technology(protocol) in old days to JAVA to JAVA distributed application communication since JDK 1.1. Although now a day’s most of web-based applications use modern technologies like REST based web-services, still we SHOULD NOT FORGET to learn those type of old technologies because they will help you to understand the modern solutions.  And by understanding those type of old technologies, you can easily understand, why design patterns, new architectural styles comes in to play.

Scenario: 

Volta is a weather forecasting company which have several data collection centers to collect weather data to predict accurate weather forecasting to its country citizens. Initially Volta’s head office collects its sub stations data using manual documentations. Due to this manual system, their predictions are not very up to date. So company management decided to automate their manual process. They have signed an agreement with One Soft Solutions to implement automated system.

Figure 1.1 Weather Station




John works for One Soft Solutions as a senior engineer. One Soft Solutions asked John to design an approach to address the above issue. In this situation, John have to face following problems:
  • Almost all the One Soft Solutions software engineers are busy with new projects except few java engineers.
  • Those non-busy engineers are not familiar with newer technologies like REST.
  • Client asked for demo within couple of weeks.
  • Solution should be expandable and should have ability to migrate to newer technologies after other developers are come in to play.
First of all John decided to explain RMI communication process in high level and then he created a documentation by including high level steps to to fallow to complete the project.

 Figure 1.2 RMI Communication Workflow

Step 01 :Designing and implementing the components of your distributed application.


    • Create Remote Interface (WeatherStation.java)
    • Implement the Remote Interface (WeatherStationImpl.java)
    • Implement the Client

Step 02 :Compiling sources

Step 03 :Making classes network accessible

Step 04 :Start the rmiregistry

Step 05 :Starting the server and client applications




Step 01 :Designing and implementing the components of your distributed application

Create Remote Interface

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface WeatherStation extends Remote {
                public String updateTodayConditions(
                                                                                      String subStation,
                                                                                       float temp,
                                                                                       float humidity,
                                                                                        float pressure
                                                                                  ) throws RemoteException;
}

Implement the Remote Interface

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class WeatherStationImpl extends UnicastRemoteObject implements WeatherStation  {
                protected WeatherStationImpl() throws RemoteException {
                                super();
                }
                public String updateTodayConditions(
                                                                                      String subStation,
                                                                                      float temp,
                                                                                      float humidity,
                                                                                      float pressure
                                                                                   ) {
                                System.out.println(
                                                                     "Sub Station :" + subStation +
                                                                     " Temparature :" + temp +
                                                                     "  Humidity :" + humidity+
                                                                     "  Pressure :" + pressure
                                                                  );
                                return "Success";
                }
}

WeatherStation Main Class

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;

public class Server {
                public static void main(String []args) throws RemoteException, MalformedURLException{
                                WeatherStation data = new WeatherStationImpl();
                                Naming.rebind("rmi://localhost:5000/WEATHERDATA",data);
                                System.out.println("Server Started..!!");
                                               
                }
}

Client Main Class

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class Client {

                public static void main(String []args) throws RemoteException, MalformedURLException, NotBoundException{
                      WeatherStation weatherData=(WeatherStation);
                       Naming.lookup("rmi://localhost:5000/WEATHERDATA");
                       String response=weatherData.updateTodayConditions("Station A",29,81, 54.1f);
                        System.out.println(response);

                }

}
















Step 02 :Compiling sources

Note:Before compile Client.java,you need to put remote interface (WeatherStation.class) to client folder. Client will invoke it as remote proxy.

Step 03 :Making classes network accessible

You need to allow client to access Server Stub's at run-time.Other wise server will throw remote exceptions.

Step 04 :Start the rmiregistry

Go to root folder of class folder and start rmiregisty by giving the port number.
 "start rmiregistry 5000"

Step 05 :Starting the server and client applications

Start Server program 1st and then start the client program.



You have done.

Next:I will explain one of the famous design pattern in my next blog post.Then you will understand why we learned this old jargon.

References:

Friday, August 31, 2018

Program to Nearer Future




As loving programming, we code software systems daily. But did you think about the future validity of your program .Sorry… "Near Future".


Sometimes our code may give big trouble when the systems scale up. Scaling your system may be from 2 users to 100 or 100000..00 users. You could say "we could not think about the Far Future because of project timing constraints". You are right. But remember to think for Near Future.


This is an example for illustrate the practice:

Lets say we have list of persons.


“Now  write a code to print person last names which start with the letter “Y””

If you are working with legacy system (before java8) your solution may look like below(java 8 solution will present at the end of the topic).  


But we know, when we going to apply that in real industrial environment we need to move those logics in to separate methods (in order to reduce code complexity).So we can write the same code as below:



But If we need to print persons which name start with H? Are you going to create separate method for that?(printLastNameStartWithH() etc.).You may say


...Noop……That’s not a good solution.First I change the method name and use generalized name and I can use additional argument to externalize filtering input. Then passing the filtering string, we can achieve your requirement.



But in near future if want to filter by lastNameStartWithM & lastNameStartWithH while keeping lastNameStartWithY ?
hmm..I need to create a separate method for that. Otherwise getting trouble now…

No still you need not to implement separate method for that.You can fallow this way;

Step 01:Create interface with test method


Step 02: Use anonymous inner class to implement filtering logic



In here we can change filtering logic as what we want. If we want to filter by lastNameStartWithM & lastNameStartWithR ,no need to change conditionalFilter methods’ signature. You can change the implementation of Condition interface as below.


 Now your code becomes:

If you familiar with java 8 ,no need to create separate interface for the above requirement. In java 8 , we have Predicate functional interface and lamda for that. Below is the java 8 solution.

So always try to program “NEAR FUTURE”. Sometimes his journey may not be simple to beginner. You have to learn and follow several things , specially including Design Patterns and Design Principles.