• No results found

boolean result = false;

In document And Son More Java Pitfalls pdf (Page 62-66)

Non-Blocking Server IO

Item 4: When Information Hiding Hides Too Much When you are developing a framework that will be used by a large project, it is some-

41: boolean result = false;

42: String dn = “uid=” + uname + “,ou=People,dc=pitfalls.org”; 43: try 44: { 45: createConnection(); 46: m_ld.authenticate( m_ldapversion, dn, pw ); 47: result = true; 48: } 49: catch ( LDAPException e ) 50: {

51: //here, result is originally set to false, so do nothing 52: }

53: return (result); 54: }

55: }

Listing 4.1 (continued)

In lines 39 through 54 of Listing 4.1, there exists a method called authenticate()

that returns a boolean value, denoting a successful login to the access control mecha- nism. In line 42, the username is turned into a LDAP distinguished name, and in lines 45 and 46, the method creates a connection and attempts to authenticate the user. If an

LDAPExceptionis thrown, the method simply returns false.

This is a good example of how ignoring exceptions for the purpose of hiding detail can cause hours and hours of pain for developers using this code. Of course, this class compiles fine. If the infrastructure is in place for this example (network connec- tivity, a directory server, the correct username and password), the method will return a boolean true value, and everything will work correctly. However, if there is another problem, such as a directory server problem or network connectivity problems, it will return false. How does the implementer using this API handle the problem or know what the problem is? The original API used by this class throws an LDAPEx- ception, but the authenticate method in listing 4.1 simply ignores it and returns false.

What is the API developer of this class to do? First of all, a simple design change to use an interface that has the authenticate()method could be used along with a creational design pattern. This way, multiple implementation classes could be written (LDAPAccessControl, DatabaseAccessControl, etc.) that can realize this inter- face, depending on which mechanism we are using. The developer using the API would still not need to know the internals but would have to handle an exception thrown by that method, as shown in the code segment below.

public interface iAccessControl {

public boolean authenticate(String user, String passwd) throws AccessException;

}

The inclusion of a new exception brings up another possible pitfall, however. We have created a new AccessExceptionclass because we do not want the API user to have to handle exceptions such as LDAPException that are dependent on our hidden imple- mentation. In the past, developers have handled this in a way shown in Listing 4.2.

01: public boolean authenticate(String uname, String pw) throws AccessException

02: {

03: boolean result = false;

04: String dn = “uid=” + uname + “,ou=People,dc=pitfalls.org”; 05: try 06: { 07: createConnection(); 08: m_ld.authenticate( m_ldapversion, dn, pw ); 09: result = true; 10: } 11: catch ( LDAPException e ) 12: {

13: throw new AccessException(e.getMessage()); 14: }

15: return (result); 16: }

17: }

Listing 4.2 Losing information with a new exception

On line 13 of Listing 4.2, we throw a new AccessException class to hide the LDAP-specific exception from the API user. Unfortunately, sometimes this complicates debugging because we lose a lot of information about the original cause of the excep- tion, which was contained in our original LDAPException. For example, perhaps there was an actual bug in the LDAP API we are using. We lose a vast majority of debugging information by discarding the “causative exception,” which was LDAPEx- ceptionin our example.

Prior to JDK 1.4, situations like these presented quite a few problems for debugging. Thankfully, JDK 1.4 released much-needed enhancements to allow “chained excep- tions.” Changes to the java.lang.Throwableclass can be seen in Table 4.1, and the implementation of Throwable.printStackTrace()was also changed to show the entire “causal” chain for debugging purposes. As you can see by looking at Table 4.1,

Table 4.1 New Chained Exception Capabilities Added to Throwable in JDK 1.4

METHOD DESCRIPTION

public Throwable getCause() Returns the cause of this throwable

or null if the cause is nonexistent or unknown. (The cause is the throwable that caused this throwable to get thrown.)

public Throwable Initializes the cause of this

initCause(Throwable c) throwable to the specified value.

(The cause is the throwable that caused this throwable to get thrown.)

public Throwable(Throwable cause) Constructs a new throwable with

the specified cause.

public Throwable(String message, Constructs a new throwable with the

Throwable cause) specified detail message and cause.

Of course, java.lang.Exception and java.lang.Error are subclasses of

Throwable, so now we can make minor adjustments to our code, passing in the cause of the exception to our new AccessExceptionclass. This is seen in Listing 4.3.

01: public boolean authenticate(String uname, String pw) throws AccessException

02: {

03: boolean result = false;

04: String dn = “uid=” + uname + “,ou=People,dc=pitfalls.org”; 05: try 06: { 07: createConnection(); 08: m_ld.authenticate( m_ldapversion, dn, pw ); 09: result = true; 10: } 11: catch ( LDAPException e ) 12: {

13: throw new AccessException(e);

14: }

15: return (result); 16: }

17: }

Listing 4.3 shows a simple way to handle our exception without losing information. Finally, Listing 4.4 shows the resulting class that replaces the listing in 4.1. As you can see in line 3 of Listing 4.4, we create a class that implements our iAccessControl

interface, and we have modified our authenticate() method to throw an

AccessException, passing the causal exception to the constructor in lines 39 to 55.

01: package org.javapitfals.item4; 02: import netscape.ldap.*;

03: public class LDAPAccessControl implements iAccessControl

04: {

05: private String m_host = null; 06: private int m_port = 389; 07: private int m_ldapversion = 3; 08: private LDAPConnection m_ld = null; 09:

10:

11: public LDAPAccessControl(String hostname) 12: {

13: this(hostname, 389, 3); 14: }

15:

16: public LDAPAccessControl(String hostname, int portnumber) 17: {

18: this(hostname, portnumber, 3); 19: }

20: public LDAPAccessControl(String hostname, int portnumber, int ldapversion) 21: { 22: m_host = hostname; 23: m_port = portnumber; 24: m_ldapversion = ldapversion; 25: }

26: private void createConnection() throws LDAPException 27: {

28: m_ld = new LDAPConnection(); 29: m_ld.connect(m_host, m_port); 30: }

31: /**

32: * The purpose of this function is to authenticate to 33: * the Directory Server with credentials.

34: *

35: * @param uname the user name 36: * @param pw the password

37: * @return successful authentication

38: */

39: public boolean authenticate(String uname, String pw)

In document And Son More Java Pitfalls pdf (Page 62-66)