Overflowing BigDecimals

If you do a lot of numerical calculations in Java where control over precision and rounding is important (especially currency-related calculations), the BigDecimal class should be your first port of call. They encapsulate an integer unscaled value, and a 32-bit precision scaling factor, which provides a large range of values.

If we define a variable called dividendTax as a BigInteger like so:

private BigDecimal dividendTax;

with a scaling factor of 2 (for currency) and a suitable rounding mode:

static final Integer SCALE = 2;
public static final RoundingMode ROUND_MODE = RoundingMode.HALF_UP;

We can round and scale an intermediate BigInteger-based calculation to a Double value like so:


public Double getDividendTax() {
return dividendTax.setScale(Constants.SCALE, Constants.ROUND_MODE).doubleValue();
}

Operations like divide() are methods on the BigDecimal instances themselves:

public Double getMonthlyTaxableAmount() {
return annualDividendTax.divide(new BigDecimal(12.0).setScale(Constants.SCALE, Constants.ROUND_MODE).doubleValue();
}

The above code snippet is possibly dangerous, in that the divide() operation can throw an ArithmeticException if the result from divide() is an infinite expansion (e.g. 1 divided by 3). This happened to me a couple of times until I figured what was missing.

The key is to pass a MathContext instance which specifies the correct precision to use. By default, the BigDecimal arithmetical operators use unlimited precision, which will obviously fail if a division operation results in an infinite series. So you need to truncate the results using one of the predefined precision ranges:

public Double getMonthlyTaxableAmount() {
return annualDividendTax.divide(new BigDecimal(12.0), MathContext.DECIMAL32).setScale(Constants.SCALE, Constants.ROUND_MODE).doubleValue();
}

Keep this in mind if you are using division operations that may potentially give infinite-precision results.

Tomcat and NTLM Authentication

Here is an example of how to use JCIFS to give you transparent user authentication via a built-in servlet filter that it comes bundled with. You will need Tomcat (I am using 5.5.9) or another servlet container, and the JCIFS library in WEB-INF/lib. Here is the contents of web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="TestNTLM" 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">
<display-name>Servlet 2.4 application</display-name>

<filter>
<filter-name>NtlmHttpFilter</filter-name>
<filter-class>jcifs.http.NtlmHttpFilter</filter-class>

<init-param>
<param-name>jcifs.smb.client.domain</param-name>
<param-value>mydomain</param-value>
</init-param>
<init-param>
<param-name>jcifs.netbios.wins</param-name>
<param-value>192.168.10.24,192.168.10.25</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>NtlmHttpFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>

I have minimally specified a domain name and a couple of WINS servers to use for name resolution. In the index page, I have just put in the following code in a JSP code block:

jcifs.smb.NtlmPasswordAuthentication auth = (jcifs.smb.NtlmPasswordAuthentication)request.getSession().getAttribute("NtlmHttpAuth");
out.println("User: = " + auth.getUsername());
out.println("Domain: = " + auth.getDomain());

This is just to illustrate the kind of information you can retrieve from the session context if JCIFS successfully authenticates the user. The NtlmHttpAuth object is an instance of NtlmPasswordAuthentication that is automatically placed in session scope by the servlet filter.

NT Authentication using JCIFS and SHAJ

Here is another set of examples on how to do native Windows-based user authentication from Java, one using JCIFS and the other using SHAJ. JCIFS has the advantage of not having to install a DLL into the system search path, whereas SHAJ has the advantage of not having to specify a domain controller explicitly in the code. (Incidentally , there is a utility called netdom.exe in the Windows 2000 Resource Kit Tools download that you can use to find your currently active domain controller – just execute netdom /query for the details).

Here is the code:


package uk.co.researchkitchen.auth.nt;

import java.net.UnknownHostException;

import jcifs.UniAddress;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbAuthException;
import jcifs.smb.SmbException;
import jcifs.smb.SmbSession;

import com.cenqua.shaj.Shaj;
import com.cenqua.shaj.log.Log;
import com.cenqua.shaj.log.PrintStreamLog;

public class TestAuth {

public void doAuthJCifs() {
UniAddress mydomaincontroller = null;
try {
mydomaincontroller = UniAddress.getByName( "192.168.0.10" );
} catch (UnknownHostException e) {

e.printStackTrace();
}
NtlmPasswordAuthentication mycreds = new NtlmPasswordAuthentication( "domain", "username", "password" );
try {
SmbSession.logon(mydomaincontroller, mycreds );
} catch( SmbAuthException sae ) {
sae.printStackTrace();
} catch( SmbException se ) {
se.printStackTrace();
}

}

public void doAuthShaj() {
Log.Factory.setInstance(new PrintStreamLog(System.out, true));

if(!Shaj.init()) {
System.err.println("Failed to initialize");
return;
}

System.out.println(Shaj.checkPassword("domain", "username", "password"));

}
}

NT Authentication using JAAS

On a similar vein to the entries I posted a couple of months back, here is an example of how to use JAAS to get the current user credentials. It uses the NTLoginModule default implementation, with the following parameters in a config file called ntlogin.config:

NTLMTest {
com.sun.security.auth.module.NTLoginModule Required debug=true;
};

And here’s the code. Run it with the parameter -Djava.security.auth.login.config=ntlogin.config to test.


package uk.co.researchkitchen.auth.jaas;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import com.sun.security.auth.callback.TextCallbackHandler;

public class JaasNTCredentialsTest {

LoginContext loginContext = null;

public static void main(String[] args) {
JaasNTCredentialsTest jnt = new JaasNTCredentialsTest();
jnt.doAuth();
}

public void doAuth() {
try {
loginContext = new LoginContext("NTLMTest", new TextCallbackHandler());
loginContext.login();
} catch (LoginException e) {
e.printStackTrace();
}

Subject subject = loginContext.getSubject();

}

}

Incidentally, if you just want to get the user name, you can use something like this:

Set principals = subject.getPrincipals();

for (Principal p : principals) {
if (p instanceof NTUserPrincipal) {
System.out.println("User name: " + p.getName());
}
}