Monday 12 December 2016

JUnit based on Spring MVC Test framework fails with AuthenticationCredentialsNotFoundException


After adding the second servlet and a servlet mapping to the web.xml configuration of a Spring-based web application, a JUnit test that relied on the Spring MVC Test framework started to fail.
The unit test was used to verify proper functioning of controller security layer that is based on Spring Security framework (v3.2.9 at the time).

The JUnit code (fragments):

@ContextConfiguration(loader = WebContextLoader.class, locations = {
"classpath:spring/application-context.xml",
"classpath:spring/servlet-context.xml",
"classpath:spring/application-security.xml"})
public class AuthenticationIntegrationTest extends AbstractTransactionalJUnit4SpringContextTests {
    @Autowired
    private WebApplicationContext restApplicationContext;

    @Autowired
    private FilterChainProxy springSecurityFilterChain;    

    private MockMvc mockMvc;

...

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(restApplicationContext)
                .addFilter(springSecurityFilterChain, "/*")
                .build();
    }

    @Test
    public void testCorrectUsernamePassword() throws Exception {
        String username = "vitali@vtesc.ca";
        String password = "password";
       
        ResultActions actions = mockMvc.perform(post("/user/register").header("Authorization", createBasicAuthenticationCredentials(username, password)));
    }
}

The test started to fail with the AuthenticationCredentialsNotFoundException as the root cause.
The change that caused the failure was introduced in order to split request security filtering into 2 distinct filter chains. The existing configuration for securing RESTful calls with Basic authentication needed to be amended to add a separate handling of requests supporting the Web user interface of the application.
That necessitated adding a second <security:http> configuration to the application-security.xml context:

<!-- REST -->
<http pattern="/rest/**" entry-point-ref="basicAuthEntryPoint" authentication-manager-ref="restAuthManager">
...
</http>

<!-- Web UI -->
<http pattern="/web/**" entry-point-ref="preAuthEntryPoint" authentication-manager-ref="webAuthManager">
        <custom-filter position="PRE_AUTH_FILTER" ref="preAuthFilter" />
        <!-- Must be disabled in order for the webAccessDeniedHandler be invoked by Spring Security -->
        <anonymous enabled="false"/>
        <access-denied-handler ref="webAccessDeniedHandler"/>
</http>

The pattern="/rest/**"  attribute was also introduced at the same time to the original <http> configuration element.

That is what ultimately caused the test to fail since the JUnit was not using Servlet path.
It is important to note that Spring MVC Test Framework runs outside of a web container and has neither dependency nor is using the web.xml.
When testing with MockMvc, it is not required to specify the context path or Servlet path when submitting requests to the controllers under test.
For example, when testing this controller:

    @RequestMapping(value = "/user/register", method = RequestMethod.POST, headers = "accept=application/json,text/*", produces = "application/json")
    @PreAuthorize("hasPermission(null, 'ROLE_USER')")
    @ResponseBody
    public RegistrationResponse register(@RequestBody(required=false) UserDeviceLog userDeviceLog) {
...
it would be sufficient to send request only specifying the mapping:
mockMvc.perform(post("/user/register").header("Authorization", createBasicAuthenticationCredentials(username, password)))

However, when access to the controllers is protected by Spring Security and the pattern is specified in the <security:http> configuration, the Spring MVC Test Framework will fully respect the processing flow failing requests that do not provide a correct Servlet path.

Resolution:
1. Specify a correct Servlet path in the request URL and also add the mapping by passing the path to the servletPath(String) method of the MockHttpServletRequestBuilder class:

mockMvc.perform(post("/rest/user/register").servletPath("/rest").header("Authorization", createBasicAuthenticationCredentials(username, password)));

2. Configure the MockMvc instance with the security filter mapping that matches the pattern specified in the application-security.xml configuration:
    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(restApplicationContext)
                .addFilter(springSecurityFilterChain, "/rest/*")
                .build();
    }

<end>

Wednesday 13 April 2016

Various tips on Oracle Spatial

When creating a spatial index on a table with SDO_GEOMETRY, one of the required parameters is LAYER_GTYPE.

How to find GTYPE of SDO_GEOMETRY objects in a table:

select sdo_geometry.get_gtype(geom), count(*) from map_data.zip_geom group by sdo_geometry.get_gtype(geom)
/

Monday 11 April 2016

Configuring log4j to create a new log file at each Java program run.

For running standalone Java programs, such as jobs, that use Apache log4j logging, it is often useful to have a separate log file per each program execution.
The post covers a simple approach that can be used with a FileAppender by using a timestamp as part of the log file name injected from a system property.

Two different samples are provided. One can be used when the Java program is launched directly from a shell (manually or via a scheduler) and the other when launching it as an Ant task.

The log4j configuration is the same for both scenarios and is shown right below:

# A sample Log4j configuration demonstrating how to create a new log file 
# at each program start.
# Created:  Apr 6, 2016 by Vitali Tchalov

log4j.rootLogger=info, stdout, logfile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %5p [%t] (%d) %c - %m%n

log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=logs/job_${log.timestamp}.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d [%t] %5p %c - %m%n

The configuration uses a custom system property log.timestamp to append a unique (with a second precision) suffix to the log file name.

The way the property is set depends on how the Java program is launched.

Scenario 1 - when starting a plain regular Java program by directly invoking the java executable

1. Add a system property in a static block of the main class (i.e. the launching class with the main(String[]) method) prior to referencing any Logger.

static {
    System.setProperty("log.timestamp", 
        new  SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()));
}

Below is a complete class source code:

package com.forms2docx;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.log4j.Logger;

/**
 * A sample class to demonstrate a technique to configure Log4j to create a new log file at each program run.
 * 
 * To compile the sample program, specify the absolute path to a log4j.jar file, for example:
 * javac -d bin -cp ".;./lib/log4j-1.2.17.jar;" ./com/forms2docx/*.java
 *
 * To run with the static block that programmatically adds the log.timestamp property:
 * java -cp ".;./bin;./lib/log4j-1.2.17.jar;" com.forms2docx.Log4jNewFile
 *
 * To run with the log.timestamp property passed from the command line:
 * java -cp ".;./bin;./lib/log4j-1.2.17.jar;" -Dlog.timestamp=$(date +"%Y%m%d_%H%M%S") com.forms2docx.Log4jNewFile
 *
 * @author Vitali Tchalov
 */
public class Log4jNewFile {
    static {
       System.setProperty("log.timestamp", 
           new  SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()));
    }

    private static final Logger logger = Logger.getLogger(Log4jNewFile.class);

    public static void main(String[] args) {

        logger.info(String.format("Job has started at %s.", 
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));

        logger.info("The sample demonstrates how to configure Log4j to create a new file on every program run.");
 
    } 
}


To execute this program, compile and run from a shell:

java -cp ".;./bin;./lib/log4j-1.2.17.jar;" com.forms2docx.Log4jNewFile

Of course, a log4j jar file must reside on the classpath.

If modifying the source is not possible or desirable for whatever reason, it is also possible to supply the system property on the command line, like this:

java -cp ".;./bin;./lib/log4j-1.2.17.jar;" -Dlog.timestamp=$(date +"%Y%m%d_%H%M%S") com.forms2docx.Log4jNewFile

The command line above is for a UNIX system (e.g. Linux, Mac). It might be possible to adapt it for Windows too but formatting a date and time to a short format would be very cumbersome in Windows.

Scenario 2 - starting a Java program (job) as an Ant task.

These steps are required for launching a Java program as an Ant task:

1. Include <tstamp /> to the Ant build file
2. Add the following to the java task:
<sysproperty key="log.timestamp" value="${DSTAMP}_${TSTAMP}" />

Note, the DSTAMP and TSTAMP are standard variables defined by Ant.

An example of an Ant build file to launch a Java program as an Ant task: (requires a log4j.jar file on the classpath as well as Ant in the PATH):
<project name="Launch Java Ant task sample" basedir="." default="info">
    <echo message="Launching Java Ant task sample..." />

    <tstamp/>

    <target name="info">
     <echo message="The runJob Java task demonstrates creating a new log file at each run."/>
    </target>

    <target name="runJob" description="Demonstrates a new log file per each run.">
        <java 
                classname="com.forms2docx.Log4jNewFile"
                fork="true"
                failonerror="true">
            <jvmarg value='-Dlog4j.configuration=file:"${basedir}/log4j.properties"' />
            <jvmarg value='-server' />
            <sysproperty key="log.timestamp" value="${DSTAMP}_${TSTAMP}" />

            <classpath>
                <pathelement location="${basedir}/bin"/>
                <fileset dir="${basedir}/lib">
                    <include name="*.jar" />
                </fileset>
            </classpath>
        </java>

        <echo message="Task completed."/>          
    </target>
</project>
 
Note that by default, the TSTAMP is in "HHmm" format. When this precision is not sufficient, then a custom property with a required format can be added.
For example:
    <tstamp>
        <format property="tstamp-sec" pattern="HHmmss"/>
    </tstamp>


Then the sysproperty in the java task would look like this:

<sysproperty key="log.timestamp" value="${DSTAMP}_${tstamp-sec}" />
 
/* --- end --- */

Sunday 31 January 2016

How to enable iOS app for iCloud Documents

1. a) New app: create a new App ID in Member Center on Apple Developer website (https://developer.apple.com/). The account must have Agent or Admin role.

- open Certificates, Identifiers & Profiles, select Identifiers


- click the + sign to create a new App ID.
- App ID Description: enter a Name, for example - iCloudDriveExplorer
- App ID Prefix: it defaults to the Team ID and is not editable
- App ID Suffix: select the Explicit App ID option - it is a must for using iCloud. Example: net.samples.iCloudDriverExplorer
- App Services: check the iCloud option and select either Compatible with Xcode 5 or Include CloudKit support (requires Xcode 6), whichever suits the needs. Note: the status initially will be set to Configurable with a yellow indicator - that is OK.


- click Continue and complete the App ID creation process.

1. b) Existing app: Edit the App ID
- check the iCloud box option and select either Compatible with Xcode 5 or Include CloudKit support (requires Xcode 6), whichever suits the needs. Note: the status initially will be set to Configurable with a yellow indicator - that is OK.

2. In Xcode - create a new project or configure an existing project to enable iCloud Document entitlement.
- select the project's target and open the Capabilities tab.



- expand the iCloud row and switch the iCloud ON. Xcode will create the project entitlement plist file, in this example named: iCloudDriveExplorer.entitlements

The contents of the project entitlements file will look similar to this:

<plist version="1.0">
<dict>
    <key>com.apple.developer.icloud-container-identifiers</key>
    <array/>
    <key>com.apple.developer.ubiquity-kvstore-identifier</key>
    <string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
</plist>

- check required iCloud services: Key-value storage, iCloud Documents and CloudKit, whatever is needed.



When enabling iCloud Documents, Xcode will offer to use either the default container or custom containers. Configuring custom containers is a subject for another post.
For the default container Xcode will add a container entitlement to the project entitlments file and will update the Provisioning Profile. After this step, the status indicator for iCloud in the Member Center will become green:



After Xcode adds containers to the project entitlement file, it will be similar to this:

<plist version="1.0">
<dict>
    <key>com.apple.developer.icloud-container-identifiers</key>
    <array>
        <string>iCloud.$(CFBundleIdentifier)</string>
    </array>
    <key>com.apple.developer.icloud-services</key>
    <array>
        <string>CloudDocuments</string>
    </array>
    <key>com.apple.developer.ubiquity-container-identifiers</key>
    <array>
        <string>iCloud.$(CFBundleIdentifier)</string>
    </array>
    <key>com.apple.developer.ubiquity-kvstore-identifier</key>
    <string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
</dict>
</plist>

Important:
Enabling iCloud for an app requires an Xcode Developer Account with Agent or Admin role.
Even though Xcode allows to have multiple Developer Accounts (Xcode > Preferences > Accounts) and prompts to choose the account with which to enable iCloud, it may fail to create a container entitlement:
Add the "iCloud containers" entitlement to your App ID.

In this case Xcode will offer the Fix it option. However, running the Fix will not prompt for the Developer Account and may fail if the account Xcode choses to run with does not have Agent or Admin role.
One workaround is to remove, temporarily, other accounts from Xcode and only leave the Admin (or Agent) account. The other accounts can be exported into a file (Xcode > Preferences > Accounts > select Apple ID then click the Setting icon on the bottom left  > Export Developer Accounts).
When iCloud configuration complete, these accounts can be easily imported back.

Update:
In later versions of Xcode, for example 7.2, it is also possible to create and enable iCloud entitlements entirely from within Xcode. As long as the Developer Account has Agent or Admin role, Xcode will create App ID automatically. It will also modify the Provisioning Profile to enable iCloud service when the iCloud capability is switched on. And it will create entitlements for the default iCloud container. Manually creating App ID and enabling it for iCloud via Member Center is no longer the only option.