Tag Archives: tomcat

docker-compose wait for dependencies

Sometimes, when you have an over-optimized system leveraging asynchronization, you have to jump through hoops to synchronize things again to make them work in practice. This is the case with docker-compose currently.

First, let me mention that this is with the docker-compose file format 3.x. Things are different with other versions.

Here’s the issue. Say you’ve got 2 microservices you want to dockerize, a tomcat container and a mysql container. docker-compose will start the two containers in arbitrary order, but you and I know that the tomcat webapp probably needs the database to be available. How do you do it?

The docker compose file allows for an argument called depends_on.
depends_on argument orders the services but it doesn’t actually wait for a service to be “ready”. I believe it just waits for the container’s service port to be exposed. The database may not have started up yet even if the ports are exposed.

Stackoverflow has this solution
Unfortunately, the depends_on’s condition argument that was introduced in compose file version 2.x is deprecated in 3.x.

I believe Docker’s official philosophy is that your application is to be created in a robust manner. That is, if the database is not available (either it’s not started yet or it has gone down temporarily or permanently since starting), the application should handle it gracefully. It’s also very difficult for them to know what it means for a service to be started. e.g. What does it mean for mysql to be started, vs postgres, vs rabbitmq, vs tomcat, vs nodejs, vs some random service you wrote?

Nevertheless, I believe there ought to be a standard way that allows users to order container startup based on readiness.

So let’s talk about the solution that my coworker and I put together, built on other open source utilities.

The first thing I’d like you to take a look at is ufoscout’s docker-compose-wait project. His wait.sh script will wait for a service to be ready before returning. It uses the netcat util to ping the service’s port until it’s available.

In your docker-compose.yml file, you just need to specify the container names and port of the other services that you depend on. See the WAIT_HOSTS environment variable. For example

version: '3'
services:

  mysql:
    image: "mysql:5.7"
    container_name: mysql
    ports:
      - "3306:3306"

  my_app:
    image: "mySuperApp:latest"
    environment:
      WAIT_HOSTS: mysql:3306

The wait.sh file is the key, but there are some caveats to using it. For instance, your container needs the nc (netcat) util. So unless your image is ubuntu, you’re not likely to have that in the standard tomcat image from Docker Hub. You’ll also have to make sure you call the wait.sh script before calling the service startup script. e.g. “CMD /wait.sh && /MySuperApp.sh”

Here’s what I did for the tomcat container. First, I located the the tomcat image’s Dockerfile. It’s referenced in the README for me so that’s nice.

Then I made several modifications to it

ADD https://raw.githubusercontent.com/ufoscout/docker-compose-wait/1.0.0/wait.sh /$CATALINA_HOME/wait.sh
RUN chmod +x /$CATALINA_HOME/wait.sh
RUN apt-get update && apt-get install -y netcat 
# CMD ["catalina.sh", "run"]
CMD ./wait.sh && catalina.sh run

Now I build the image.

docker build -t kanesee/tomcat-wait:9.0 .

The entire set of files and modifications can be found in this github repo.

So now we have a version of tomcat that will wait for other services to be ready before it starts. We just need to add the WAIT_HOSTS argument to docker-compose.yml to specify the name of the database container and port and then it’s ready to go.
If you need longer than the default 30 seconds, you can set WAIT_HOSTS_TIMEOUT to a longer period.

For your benefit, I pushed my tomcat-wait image to Docker Hub so that you don’t need to create your own image if you’re looking for a tomcat v9.0 that does this.

Otherwise, please follow this recipe and create your own “waiting” services. If you create one, I’d love to know about it. Please leave a link to your image in the comments section.

Advertisements
Tagged , , ,

Tomcat ContextPath and Angularjs

An interesting obstacle arose when I tried to use Angularjs (NG) in Tomcat, specifically in regards to the path.

Typically, I’ve been using NG assuming everything starts at the root path. So my index.html is at http://domain.com/index.html.

However, in Tomcat, each webapp has its own starting path. So you may have a weather webapp that is at http://domain.com/weather/ and a clock webapp at http://domain.com/clock/.

How do you configure NG within tomcat?

You could hardcode your starting path. But imagine if you wanted to have two clock webapps in tomcat, renaming one to http://domain.com/clock1/ and the other http://domain.com/clock2.

Now you would have to change all the references of “clock” to “clock1” and “clock2” respectively. And there are many references…

In the index.html, you have to change all your <link …css> as well as all <script …js>. You might think that you can outsmart yourself by changing the index.html to a index.jsp and using <%=request.getContextPath()%>.

But then you still have to change references of the hardcoded path in your .js files, such as your app.js that defines your module and routes.

And don’t even get me started on all your templates that reference .js and .css.

Well, let me jump straight to the short answer.

In your index.html, simply add this within your <head> tag

<head>
    <base href="/clock/" />
</head>

That’s it. Now all your relative paths will have that base href prefixed to them automatically.

So <link href=”mystyle.css”> will automatically fetch from http://domain.com/clock/mystyle.css

<script src=”myscripts.js>” will automatically fetch from http://domain.com/clock/myscripts.js

Even app.js will leverage this base when using relative url’s.
e.g. $routeProvider.when(‘/’, {templateurl: ‘home.html’})
will automatically reference http://domain.com/clock/home.html

This html standard can even be used on jsp pages. Anything that is html will take advantage of it

Tagged , , ,

Setting up Tomcat for debugging

Short PSA. If you want to set up Tomcat for debugging, set up JPDA.

To do so, simply open the startup.sh file, which is somewhere in TOMCAT/bin folder.

Add these two lines near the top

export JPDA_ADDRESS=7000
export JPDA_TRANSPORT=dt_socket

Then modify the exec line (usually the last line in the file) from this:

exec "$PRGDIR"/"$EXECUTABLE" start "$@"

to this

exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@"
Tagged ,

Upgrading to Jersey 2.x in Tomcat8

Jersey 1.x is still supported. As of this writing, version 1.19.3 was just released on Oct 24th, 2016.

But recently I discovered that Tomcat8 doesn’t play well with Jersey 1.x so we simply have to upgrade to Jersey 2.x. Easy right? (If it were, I wouldn’t be writing this post.)

Jersey 1.x
Let’s review the Jersey 1.x configurations first

As far as maven dependencies, here’s what I used in my pom.xml

<dependency>
  <groupId>com.cedarsoft.rest</groupId>
  <artifactId>jersey</artifactId>
  <version>1.0.0</version>
</dependency>

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-json</artifactId>
  <version>1.5</version>
</dependency>

com.cedarsoft.rest:jersey is a bundle that includes these dependencies.

Unfortunately, I could find no such bundle for Jersey 2.x so I had to mix and match until trial-and-error led me to a workable solution. (I save you the trouble.)

Here’s my old web.xml

<web-app version="2.4"
 xmlns="http://java.sun.com/xml/ns/j2ee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <servlet>
    <servlet-name>JerseyREST</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>INSERT_MY_PACKAGES_AND_CLASSES_HERE</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JerseyREST</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Jersey 2.x
Now here are the changes for Jersey 2.

   <dependency>
     <groupId>javax.servlet</groupId>
     <artifactId>javax.servlet-api</artifactId>
     <version>3.1.0</version>
     <scope>provided</scope>
   </dependency>
   <dependency>
     <groupId>org.glassfish.jersey.containers</groupId>
     <artifactId>jersey-container-servlet-core</artifactId>
     <version>2.13</version>
   </dependency>
   <dependency>
     <groupId>org.glassfish.jersey.containers</groupId>
     <artifactId>jersey-container-servlet</artifactId>
     <version>2.13</version>
   </dependency>
   <dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
     <version>2.8.5</version>
   </dependency>
   <dependency>
     <groupId>org.glassfish.jersey.media</groupId>
     <artifactId>jersey-media-json-jackson</artifactId>
     <version>2.13</version>
   </dependency>
   <!-- jersey file upload dependencies -->
   <dependency>
     <groupId>org.glassfish.jersey.media</groupId>
     <artifactId>jersey-media-multipart</artifactId>
     <version>2.13</version>
   </dependency>

org.glassfish.jersey.media:jersey-media-multipart is only required for file upload capability.

Here’s the new web.xml

<web-app version="3.1"
         metadata-complete="false"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
  <servlet>
    <servlet-name>JerseyREST</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
    </init-param>        
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>
        INSERT_MY_PACKAGES_AND_CLASSES_HERE
      </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
      <servlet-name>JerseyREST</servlet-name>
      <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Notice, the new servlet class.
It’s also important to load the org.glassfish.jersey.media.multipart.MultiPartFeature to support file upload capability.

You might also have to change some of your code.
For example, all your @JsonIgnore annotation classes will have to change from
org.codehaus.jackson.annotate.JsonIgnore
to
import com.fasterxml.jackson.annotation.JsonIgnore

Tagged ,

Quick Ubuntu 16 Setup with java8, mysql 5.7, tomcat7

sudo apt-get update

# get latest java, which is java8 at time of writing
sudo apt-get install default-jdk

# get latest mysql, which is mysql5.7 at time of writing
sudo apt-get install mysql-server

# get tomcat7
sudo apt-get install tomcat7
Tagged , , ,

A (not so) simple web architecture. Part IIIb. Proxying requests

In the previous posts, I showed you how to use jsonp to bypass browser restrictions that prevent a webpage on one server (apache) from accessing resources on another (tomcat). jsonp works will but it has restrictions of its own, such as, it only allows GET. So you can’t use the full language of REST (POST, PUT, DELETE). And really, is it still REST then?

There’s a hack to get around that, but compounding hacks on top hacks is rarely a good idea unless you have a lot of old coffee grind you need to use up.

Another solution is to use the webpage’s server as a proxy server. Apache lets you do that with two modules: mod_jd and mod_proxy.

I won’t get too much into them, but here’s a short comparison I found:
mod_proxy
Pros:

  • No need for a separate module compilation and maintenance. mod_proxy, mod_proxy_http, mod_proxy_ajp and mod_proxy_balancer comes as part of standard Apache 2.2+ distribution
  • Ability to use http https or AJP protocols, even within the same balancer.

Cons:

  • mod_proxy_ajp does not support large 8K+ packet sizes.
  • Basic load balancer
  • Does not support Domain model clustering

mod_jk
Pros:

  • Advanced load balancer
  • Advanced node failure detection
  • Support for large AJP packet sizes

Cons:

  • Need to build and maintain a separate module

I decided to use mod_proxy because the module comes built into apache, which means less configuration and less possibility of error.

Here’s how simple it was to set up.
I added one line to the httpd.conf file under the main directive:

ProxyPass /pass/ http://localhost:8080/

That tells apache (served at http://localhost) that whenever a client makes a request for http://localhost/pass/ to redirect that to http://localhost:8080/.

The only slight change is the url of the call in javascript from

var matchUri = "http://localhost:8080/kodingnotes/rest/foo/read/jsonp";

to

var matchUri = "http://localhost/pass/kodingnotes/rest/foo/read";

Now, I can use the original (non-jsonp) jQuery call and apache will forward the request to the non-JXONWithPadding wrapper java method.

Tagged , , , ,

I’m not going to go over all the reasons why you should have a RESTful service with javascript front-end (e.g. clean separation of front- and back-end, reusable web service components, etc). Here I’ll just show how you can quickly set up a RESTful web service in Java and a javascript/jQuery front-end to access it.

The server part requires just 4 things:
1) Add Jersey dependency
2) Write a java bean and annotate it
3) Write a Service class
4) Deploy the servlet in web.xml

1) Add Jersey dependency

If you’re using Maven, then add the following Jersey (JAX-RS implementation) dependency to your pom.xml

<dependency>
    <groupId>com.cedarsoft.rest</groupId>
    <artifactId>jersey</artifactId>
    <version>1.0.0</version>
    <scope>compile</scope>
</dependency>


2) Write a java bean and annotate it

Create a java bean with the following:
1) annotate the class with @XmlRootElement
2) make sure there’s a null constructor (no arguments)
3) add public setters/getters

import javax.xml.bind.annotation.XmlRootElement
@XmlRootElement
public class Foo {
    private String name;
    public Foo() { }
    public String getName() { return name; }
    public void setName(String s) { name = s; }
}

3) Write a Service class

package com.kodingnotes.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path ("/foo")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class FooService {

    @GET
    @Path("/read")
    public Foo getFoo(@QueryParam("input") String input) {
        Foo foo = new Foo();
        foo.setName(input);
        return foo;
    }
}

4) Deploy the servlet in web.xml

In your web.xml, add the following servlet


<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <!-- ##### REFER TO THE PACKAGE WHERE YOU WANT JERSEY TO PICK UP THE RESOURCE CLASSES ###### -->
    <!-- ##### Use semi-colon to specify multiple packages ###### -->
    <param-value>com.kodingnotes.services</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
    <param-value>com.sun.jersey.api.container.filter.GZIPContentEncodingFilter</param-value>
  </init-param>
  <init-param>
    <param-name>com.sun.jersey.config.feature.logging.DisableEntitylogging</param-name>
    <param-value>false</param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>JerseyREST</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

 Edit the <param-value> of the <param-name>com.sun.jersey.config.property.packages<param-name>

A simple web architecture. Part I: The Server

Tagged , , , ,