Category Archives: Jetty

Maven + Jetty: Enabling SSL on Jetty Maven Plugin

PROBLEM

In order to run our web application using HTTPS, we need to enable SSL first on Jetty.

SOLUTION #1: Generating Certificate on the Fly

One clean solution by @PascalThivent is to recreate the certificate whenever Jetty starts. Not to steal any credits from him, but here’s a slightly modified configuration using more recent plugin versions:-

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>keytool-maven-plugin</artifactId>
    <version>1.5</version>
    <executions>
        <execution>
            <phase>generate-resources</phase>
            <id>clean</id>
            <goals>
                <goal>clean</goal>
            </goals>
        </execution>
        <execution>
            <phase>generate-resources</phase>
            <id>generateKeyPair</id>
            <goals>
                <goal>generateKeyPair</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
        <dname>cn=localhost</dname>
        <!--Both `keypass` and `storepass` must be at least 6 characters long. -->
        <keypass>jetty8</keypass>
        <storepass>jetty8</storepass>
        <alias>jetty8</alias>
        <keyalg>RSA</keyalg>
    </configuration>
</plugin>

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>8.1.14.v20131031</version>
    ...
    <configuration>
        <connectors>
            <connector implementation="org.eclipse.jetty.server.bio.SocketConnector">
                <port>7777</port>
            </connector>
            <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
                <port>7443</port>
                <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
                <keyPassword>jetty8</keyPassword>
                <password>jetty8</password>
            </connector>
        </connectors>
    </configuration>
</plugin>

In this case, the generated certificate is stored under the project’s build directory (“target” directory) every time we run mvn clean jetty:run. This is a great solution because it allows me to easily pass my project to my peers without the need to worry about the generated certificate.

However, a downside to this approach is because the certificate is regenerated again and again, my browser keeps prompting me to add an exception whenever I refresh the web link.

… and after adding the exception for like 5 times, it begins to get very annoying.

SOLUTION #2: Generating Certificate Once and Reuse it

Instead of regenerating the certificate again and again, I decided to generate the certificate that will last for 99999 days and store it under src/main/config directory.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>keytool-maven-plugin</artifactId>
    <version>1.5</version>
    <configuration>
        <keystore>${project.basedir}/src/main/config/jetty-ssl.keystore</keystore>
        <dname>cn=localhost</dname>
        <!--Both `keypass` and `storepass` must be at least 6 characters long. -->
        <keypass>jetty8</keypass>
        <storepass>jetty8</storepass>
        <alias>jetty8</alias>
        <keyalg>RSA</keyalg>
        <validity>99999</validity>
    </configuration>
</plugin>

<plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>8.1.14.v20131031</version>
    ...
    <configuration>
        <connectors>
            <connector implementation="org.eclipse.jetty.server.bio.SocketConnector">
                <port>7777</port>
            </connector>
            <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
                <port>7443</port>
                <keystore>${project.basedir}/src/main/config/jetty-ssl.keystore</keystore>
                <keyPassword>jetty8</keyPassword>
                <password>jetty8</password>
            </connector>
        </connectors>
    </configuration>
</plugin>

To generate (or regenerate) the certificate, run mvn keytool:clean keytool:generateKeyPair.

With this approach, I just need to add an exception ONCE in my browser regardless of the number of times I restart Jetty… or when I’m still actively working on this project after 99999 days.

Advertisements

Managing Log4j Configuration for Both Development and Production Environments

PROBLEM

Most of the time, we set the Log4j’s log levels to something lower (debug or info) during our local development. Once it is ready for production, we normally set the Log4j’s log levels to something higher (warn or even error) to prevent meaningless information from flooding the server log.

One way to do this is to manually adjust the log level(s) in log4.xml during our local development, for example:-

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration>
    <!-- This appender logs to the console -->
    <appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%-5p] [%c{1}] [%M:%L] - %m%n"/>
        </layout>
    </appender>

	<!-- Set "debug" log level for project code  -->
    <logger name="com.choonchernlim.myproject">
        <level value="debug"/>
    </logger>

	<!-- Set "info" log level for Spring framework  -->
    <logger name="org.springframework">
        <level value="info"/>
    </logger>

	<!-- Set "debug" log level for Hibernate framework  -->
    <logger name="org.hibernate">
        <level value="debug"/>
    </logger>

    <root>
    	<!-- The default log level is "warn" -->
        <priority value="warn"/>
        <appender-ref ref="consoleAppender"/>
    </root>
</log4j:configuration>

Once we are ready for production, we will manually change the package specific log levels back to warn or comment them out, for example:-

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration>
    <!-- this appender logs to the console -->
    <appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%-5p] [%c{1}] [%M:%L] - %m%n"/>
        </layout>
    </appender>

	<!-- Comment out package specific log levels
    <logger name="com.choonchernlim.myproject">
        <level value="debug"/>
    </logger>

    <logger name="org.springframework">
        <level value="info"/>
    </logger>

    <logger name="org.hibernate">
        <level value="debug"/>
    </logger>
	-->
	
    <root>
		<!-- The default log level is "warn" -->
        <priority value="warn"/>
        <appender-ref ref="consoleAppender"/>
    </root>
</log4j:configuration>

The problem with this approach is I always forget to make necessary changes in log4j.xml when I’m ready to package my project to be deployed in production.

SOLUTION

The solution that I come up with is rather simple. We will have two Log4j XML files under src/main/resources:-

  • log4j.xml
  • log4j-dev.xml

By default, assuming there’s no further Log4j configuration, log4j.xml will always get picked up by Log4j due to the default filename and its location. log4j-dev.xml will always be ignored by Log4j.

To ensure our local development picks up the configuration from log4j-dev.xml, we will need to make a minor tweak to the Jetty configuration in pom.xml:-

<project ...>
	...
    <build>
        <plugins>
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>8.1.8.v20121106</version>
                <configuration>
                    <systemProperties>
                        <!--
                        When Jetty runs, "log4j-dev.xml" will be used instead of 
						"log4j.xml" because the latter is reserved for production 
						usage.
                        -->
                        <systemProperty>
                            <name>log4j.configuration</name>
                            <value>log4j-dev.xml</value>
                        </systemProperty>
                    </systemProperties>
					...
                </configuration>
                <dependencies>
					...
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

This way, when we run our local development on Jetty, log4j-dev.xml will be used. When we deploy the project in production, log4j.xml will be used instead.