Introduction to Amazon Code Pipeline with Java part 12: the job agent entry point in code

Introduction

In the previous post we started looking into the Code Pipeline job agent application. The job agent can be any application that is capable of executing long-running threads that periodically communicates with a Code Pipeline endpoint through the AWS SDK. The model application we’re going to look at is a Java Maven web app. It can be deployed directly on a Tomcat server or in AWS Elastic Beanstalk – which is actually a wrapper around an EC2 server with Tomcat installed.

In this post we’ll start looking into some actual Java code within the job agent. Note that I won’t provide every single detail in this series as most of the code is related to our business and is therefore irrelevant to the current discussion. You should also have at least some experience with Maven and things like the web.xml file. I will also omit infrastructure related code such as reading the properties or logging with log4j as much as possible. I’ll only show that type of code where it would otherwise be difficult to follow where a certain variable is coming from. We’ll actually see some of that in this post. This is in order to minimise the “noise”. You are welcome to ask specific questions in the comments section.

The entry point

We’ll start with the entry point of the application and see how the long running thread is started. Our own implementation of the job agent starts with a servlet initiator which implements the javax.servlet.ServletContextListener interface. You may be familiar with this interface. If you have a class which implements this interface then you can wire it up as a listener in web.xml. It will then be called once when the web application starts:

<listener>
        <listener-class>com.apica.awscodepipelinebuildrunner.ServletInitiator</listener-class>
</listener> 

…where the full name of the listener class implements the ServletContextListener interface:

public class ServletInitiator implements ServletContextListener
{
    @Override
    public void contextInitialized(ServletContextEvent sce)
    {
    }
}

We are free to execute any code within the overridden contextInitialized method. We use it not just to start the long running thread – a daemon thread – but also to read the application properties, set up the object dependencies and construct all the objects that are required by the job agent. So this function serves like a “main” function where a lot of plumbing occurs so that the rest of the application can carry out its work.

Since the job monitoring thread will run in the background we need some way to start it. We’ll implement the Runnable interface for that.

Here’s the implementation of the contextInitialized function:

public void contextInitialized(ServletContextEvent sce)
{
        final class ConsumerRunner implements Runnable
        {

            @Override
            public void run()
            {                
                String settingsFileName = "aws-code-pipeline-job-agent-app-settings-dev.txt";
                
                AppSettingsPropertiesReader appSettingsPropertiesReader = new S3AppSettingsPropertiesReader(settingsFileName);
                PropertiesReadResult readPropertiesFile = appSettingsPropertiesReader.readPropertiesFile();
                if (readPropertiesFile.isSuccess())
                {
                    Properties properties = readPropertiesFile.getProperties();                    
                    ISslHostnameVerifier hostnameVerifier = new LtpWebApiHostnameVerifier();
                    ISslTrustManager sslTrustManager = new LtpWebApiTrustManager();
                    ICodePipelineService codePipelineService = new LtpWebApiCodePipelineService(hostnameVerifier, 
                            sslTrustManager, properties);
                    LtpApiLoadtestJobExecutorService ltpApiLoadtestJobExecutorService = new LtpApiLoadtestJobExecutorService(hostnameVerifier, sslTrustManager, properties);
                    JobProcessor jobProcessor = new ApicaLoadtestJobProcessor(ltpApiLoadtestJobExecutorService, 
                            new CodePipelineLoadtestThresholdParser());
                    Daemon jobAgentDaemon = new JobWorkerDaemon(Executors.newScheduledThreadPool(1),
                            properties, new CentralLogger(properties), 
                            new LtpApiClientTokenProvider(codePipelineService), jobProcessor);
                    DaemonContext jobAgentDaemonContext = new DaemonLoader.Context();

                    try
                    {
                        jobAgentDaemon.init(jobAgentDaemonContext);
                        jobAgentDaemon.start();
                    } catch (Exception ex)
                    {                        
                        try
                        {
                            jobAgentDaemon.stop();
                            jobAgentDaemon.destroy();
                        } catch (Exception daemonShutdownExcepion)
                        {
                            //log the exception
                        }
                    }
                } else
                {
                    //log the exception
                }
            }
        }
        try
        {
            Thread t = new Thread(new ConsumerRunner());
            t.start();
        } catch (Exception ex)
        {  //ignore code

        }
}

That’s a lot of objects to go through. Here are a couple of points to note without any significant detail:

  • We keep the application properties in Amazon S3 and we read those in the beginning of the job agent lifetime. The implementation details are irrelevant and you are free to store and read the app properties the way you want to, I won’t show any code related to that
  • Then come two interfaces – ISslHostnameVerifier and ISslTrustManager – that are relevant to making HTTPS calls. These can actually be interesting to look at, we’ll do that in the next post
  • We then build the executor service and the job processor objects
  • Then comes the construction of the daemon thread which is initialised and started in a try-catch block
  • The last bit of the implementation starts a thread with a new ConsumerRunner object

Don’t worry if you can’t see through the jungle of objects and classes right now. We’ll go through the details in the rest of the series. Read the next part here.

View all posts related to Amazon Web Services and Big Data here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: