Maven JAR Plugin: Excluding Package(s) from JAR

Let’s assume we want to exclude com.choonchernlim.epicapp.scratch package when we create the JAR file.

Add the following in pom.xml:-

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <executions>
        <execution>
            <id>default-jar</id>
            <phase>package</phase>
            <goals>
                <goal>jar</goal>
            </goals>
            <configuration>
                <excludes>
                    <exclude>**/scratch/**</exclude>
                </excludes>
            </configuration>
        </execution>
    </executions>
</plugin>

The key here is to specify the execution ID called default-jar.

Spring Security: Handling 403 Error Page

If you are already using Spring, then you might want to use Spring Security to secure your web resources.

To do that, we specify the URI to be secured with <security:intercept-url/> tag:-

<beans ...>
    <!-- Error pages don't need to be secured -->
    <security:http pattern="/error/**" security="none"/>

    <security:http auto-config="true">
        <security:form-login ... />
        <security:logout ... />
        <security:intercept-url pattern="/top-secrets/**" access="ROLE_TOPSECRET"/>
    </security:http>
		
		...
</beans>

When users without role ROLE_TOPSECRET access /top-secrets/kfc-secret, they will see this default error page:-

This proves that Spring Security is doing its job. However, the default error page looks rather F.U.G.L.Y. Further, the error page may reveal too much information about the application server. The above error page shows the application runs on Jetty. If I’m a motherhacker, I would research all the possible vulnerabilities on this particular application server in attempt to hack it.

A better solution is to provide a friendly error page when the user access is denied. This can be done by specifying <security:access-denied-handler/> tag:-

<beans ...>
    <!-- Error pages don't need to be secured -->
    <security:http pattern="/error/**" security="none"/>

    <security:http auto-config="true">
        <security:form-login ... />
        <security:logout ... />
        <security:access-denied-handler error-page="/error/access-denied"/>
        <security:intercept-url pattern="/top-secrets/**" access="ROLE_TOPSECRET"/>
    </security:http>
		
		...
</beans>

Then, we create a simple error controller that returns the error page:-

@Controller
@RequestMapping(value = "/error")
public class ErrorController {
    @RequestMapping(value = "/access-denied", method = RequestMethod.GET)
    public String accessDenied() {
        return "error-access-denied";
    }
}

Now, the user will see this custom error page:-

This solution is better than the previous one. However, SiteMesh doesn’t have the opportunity to decorate this error page before it gets rendered.

To fix this, we can create a simple redirect to allow the request to make a full-round trip to the server so that SiteMesh can decorate the error page:-

@Controller
@RequestMapping(value = "/error")
public class ErrorController {
    @RequestMapping(value = "/router", method = RequestMethod.GET)
    public String errorRouter(@RequestParam("q") String resource) {
        return "redirect:/error/" + resource;
    }

    @RequestMapping(value = "/access-denied", method = RequestMethod.GET)
    public String accessDenied() {
        return "error-access-denied";
    }
}

Then, we tweak the Spring Security to use the error router URI:-

<beans ...>
    <!-- Error pages don't need to be secured -->
    <security:http pattern="/error/**" security="none"/>

    <security:http auto-config="true">
        <security:form-login ... />
        <security:logout ... />
        <security:access-denied-handler error-page="/error/router?q=access-denied"/>
        <security:intercept-url pattern="/top-secrets/**" access="ROLE_TOPSECRET"/>
    </security:http>
		
		...
</beans>

Now, the user will see this nice beautiful error page:-

Maven: Enabling Snapshot Downloads

By default, Maven does not allow us to download snapshots. To enable this feature so that we can download snapshots from Nexus, add the following configuration to [USER.HOME]/.m2/settings.xml:-

<?xml version="1.0"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
	<profiles>
		<profile>
			<id>allow-snapshots</id>
			<activation>
				<activeByDefault>true</activeByDefault>
			</activation>
			<repositories>
				<repository>
					<id>snapshots</id>
					<url>http://snapshots</url>
					<releases>
						<enabled>false</enabled>
					</releases>
					<snapshots>
						<enabled>true</enabled>
					</snapshots>
				</repository>
			</repositories>
		</profile>
	</profiles>
</settings>

Remember to replace http://snapshots with the actual snapshot link.

Spring: Set Active Profile Using Existing JNDI

If we are using @Profile, Spring allows us select the active profile by using spring.profiles.active key. Although I can hardcode the active profile in web.xml, I want my active profile to be determined based on a JNDI string defined in the application server.

PROBLEM

My application servers (DEV, QA and PROD) have an existing JNDI string (let’s assume it is called cell/persistent/env) that allows the deployed application to check what environment it is currently in.

I’m very reluctant to create yet another JNDI called spring.profiles.active that does the exact same thing as this existing JNDI, but it allows Spring to seamlessly activate the right profile.

There are two solutions to this problem…

SOLUTION 1: Creating Application-specific JNDI

One way to do this is to create an application-specific JNDI called spring.profiles.active. Then, we map this JNDI to the existing JNDI called cell/persistent/env.

The problem with this approach is the JNDI mapping configuration is very specific to the application server(s). For example, Websphere Application Server uses ibm-web-bnd.xml, Jetty uses jetty-web.xml, JBoss uses jboss-web.xml, etc… and the configuration is slightly different in every file. The last thing I want to maintain a set of server-specific configuration files.

SOLUTION 2: Creating a Custom ApplicationContextInitializer

Spring provides ApplicationContextInitializer that allows us to programatically initialize the application context. This way, we can perform a JNDI lookup to activate the right profile before refreshing the application context.

First, we need to define a context parameter called contextInitializerClasses in web.xml:-

<web-app ...>
    ...
		
    <context-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>com.choonchernlim.epicapp.config.CustomApplicationContextInitializer</param-value>
    </context-param>
</web-app>

Then, we go ahead and implement that custom context initializer class:-

public class CustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    private final JndiLocatorDelegate jndi = JndiLocatorDelegate.createDefaultResourceRefLocator();

    @Override
    public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
        try {
            String profile = jndi.lookup("cell/persistent/env", String.class);
            configurableApplicationContext.getEnvironment().addActiveProfile(profile);
        }
        catch (NamingException e) {
            throw new RuntimeException("JNDI lookup failed", e);
        }
    }
}

Let’s assume the active profile value can either be “DEV”, “QA” or “PROD”.

With that, we can now create environment-specific configurations, and only one configuration will be enabled depending on the JNDI value:-

@Configuration
public class WebServiceURLConfiguration {

    @Profile("DEV")
    @Bean(name = "webServiceURL")
    public String devWebServiceURL() {
        return "http://dev.server:7777/webservice";
    }

    @Profile("QA")
    @Bean(name = "webServiceURL")
    public String qaWebServiceURL() {
        return "http://qa.server:8888/webservice";
    }

    @Profile("PROD")
    @Bean(name = "webServiceURL")
    public String prodWebServiceURL() {
        return "http://prod.server:9999/webservice";
    }
}

Spring Web Services: Client Integration Testing with MockWebServiceServer

Spring Web Services provides a great way to perform web service client integration tests. However, the example in the documentation requires the client class to extend org.springframework.ws.client.core.support.WebServiceGatewaySupport, which is rather ugly. Instead, I prefer to have WebServiceTemplate injected into my client class.

So, I made a slight tweak to my integration test to work this work…

Production Code To Be Tested

First, we need the following Spring Web Services dependency:

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-core</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>

Let’s assume the client class looks like this:-

@Service
public class WSClientServiceImpl implements WSClientService {

    private WebServiceTemplate webServiceTemplate;

    @Autowired
    public WSClientServiceImpl(WebServiceTemplate webServiceTemplate) {
        this.webServiceTemplate = webServiceTemplate;
    }

    public String login(String username, String password) {
        Login login = new ObjectFactory().createLogin();
        login.setUsername(username);
        login.setPassword(password);

        SoapActionCallback callback = new SoapActionCallback("http://webservice/login");
        Object object = webServiceTemplate.marshalSendAndReceive(login, callback);

        JAXBElement<String> jaxbElement = (JAXBElement<String>) object;
        String sessionId = jaxbElement.getValue();

        return sessionId;
    }
}

The WebServiceTemplate bean is configured like this:-

<beans ...>
    <context:component-scan base-package="com.choonchernlim.epicapp"/>

    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPaths">
            <list>
                <value>...</value>
            </list>
        </property>
    </bean>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <property name="marshaller" ref="marshaller"/>
        <property name="unmarshaller" ref="marshaller"/>
        <property name="defaultUri" value="http://webservice"/>
    </bean>
</beans>

Constructing Integration Test

Make sure we have the Spring Web Services Test framework dependency:-

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-test</artifactId>
    <version>2.1.4.RELEASE</version>
    <scope>test</scope>
</dependency>

The integration test looks similar to the example in the Spring documentation, however, I injected WebServiceTemplate instead:-

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:applicationContext.xml"})
public class WSClientServiceImplTest {

    @Autowired
    private WSClientService wsClientService;

    @Autowired
    private WebServiceTemplate webServiceTemplate;

    private MockWebServiceServer mockServer;

    @Before
    public void createServer() throws Exception {
        mockServer = MockWebServiceServer.createServer(webServiceTemplate);
    }

    @Test
    public void testLogin() {
        Source requestPayload = new StringSource(
                "<ns2:Login xmlns:ns2='http://webservice/login'>" +
                "  <ns2:Username>user</ns2:Username>" +
                "  <ns2:Password>password</ns2:Password>" +
                "</ns2:Login>"
        );

        Source responsePayload = new StringSource(
                "<SessionID xmlns='http://webservice/login'>12345</SessionID>"
        );

        mockServer.expect(payload(requestPayload)).andRespond(withPayload(responsePayload));

        String sessionId = wsClientService.login("user", "password");

        assertThat(sessionId, is("12345"));

        mockServer.verify();
    }
}

SonarQube: Building Specific Module in Multi-Module Project

Let’s assume we have a multi-module project that looks something like this:-

app
├── pom.xml
├── app-jar
│   └── pom.xml
├── app-ear
│   └── pom.xml
└── app-war
    └── pom.xml

To build certain module(s) within the project, we can use the sonar.skippedModules option to skip the modules we don’t need. For example, the following configuration in Jenkins will only build app-jar module in SonarQube:-

Maven: Building Specific Module in Multi-Module Project

Let’s assume we have a multi-module project that looks something like this:-

app
├── pom.xml
├── app-jar
│   └── pom.xml
├── app-ear
│   └── pom.xml
└── app-war
    └── pom.xml

To build certain module(s) within the project, we can use the -pl option. For example, the following mvn command will only build app-jar module:-

clean -pl app-jar verify site