Understanding the Cloud Foundry Java Buildpack Code with Tomcat Example
Join the DZone community and get the full member experience.
Join For FreeCloudfoundry's java buildpack is supporting some popular jvm based applications. This article is oriented to the audiences already with experience of cloudfoundry/heroku buildpack who want to have more understanding of how buildpack and cloudfoundry works internally.
cf push app -p app.war -b build-pack-url
The above command demonstrates the usage of pushing a war file to
cloudfoundry by using a custom buildpack (E.g.
https://github.com/cloudfoundry/java-buildpack). However, what exactly
happens inside, or how cloudfoundry bootstrap the war file with tomcat?
There are three contracts phase that bridge communication between
buildpack and cloudfoundry. The three phases are detect, compile and
release, which are three ruby shell scripts:
Java buildpack has multiple sub components, while each of them has all
of these three phases (E.g. tomcat is one of the sub components, while
it contained another layer of sub components).
Detect Phase:
detect
phase is to check whether a particular buildpack/component applies to
the deployed application. Take the war file example, tomcat applies only
when https://github.com/cloudfoundry/java-buildpack/blob/master/lib/java_buildpack/container/tomcat.rb is true:
def supports? web_inf? && !JavaBuildpack::Util::JavaMainUtils.main_class(@application) end
The above code means, the tomcat applies when the application has a WEB-INF folder andthisisnot a main class bootstrapped application.
Compile Phase:
Compile phase would be the major/comprehensive work for a customized
buildpack, while it is trying to build a file system on a lxc container.
Take the example of our war application and tomcat example.
In https://github.com/cloudfoundry/java-buildpack/blob/master/lib/java_buildpack/container/tomcat/tomcat_instance.rb
def compile download(@version, @uri) { |file| expand file } link_to(@application.root.children, root) @droplet.additional_libraries << tomcat_datasource_jar if tomcat_datasource_jar.exist? @droplet.additional_libraries.link_to web_inf_lib end def expand(file) with_timing "Expanding Tomcat to #{@droplet.sandbox.relative_path_from(@droplet.root)}" do FileUtils.mkdir_p @droplet.sandbox shell "tar xzf #{file.path} -C #{@droplet.sandbox} --strip 1 --exclude webapps 2>&1" @droplet.copy_resources end
The above code is all about preparing the tomcat and link the application files, so the application files will be available for the tomcat classpath. Before going to the code, we have to understand the working directory when the above code executes:
. => working directory .app => @application, contains the extracted war archive .buildpack/tomcat => @droplet.sandbox .buildpack/jdk .buildpack/other needed components
Inside compile method:
- download method will download tomcat binary file (specified here: https://github.com/cloudfoundry/java-buildpack/blob/master/config/tomcat.yml), and then extract the archive file to @droplet.sandbox directory. Then copy the resources folder's files to https://github.com/cloudfoundry/java-buildpack/tree/master/resources/tomcat/conf to @droplet.sandbox/conf
- Symlink the @droplet.sandbox/webapps/ROOT to .app/
- Symlink additional libraries (comes from other component rather than application) to the WEB-INF/lib
def command @droplet.java_opts.add_system_property 'http.port', '$PORT' [ @droplet.java_home.as_env_var, @droplet.java_opts.as_env_var, "$PWD/#{(@droplet.sandbox + 'bin/catalina.sh').relative_path_from(@droplet.root)}", 'run' ].flatten.compact.join(' ') end
The above code does:
- Add java system properties http.port (referenced in tomcat server.xml) with environment properties ($PORT), this is the port on the DEA bridging to the lxc container already setup when the container was provisioned.
- instruction of how to run the tomcat Eg. "./bin/catalina.sh run"
Published at DZone with permission of Shaozhen Ding, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments