Agile FAQs
  About   Slides   Home  

 
Managed Chaos
Naresh Jain's Random Thoughts on Software Development and Adventure Sports
     
`
 
RSS Feed

Recent Thoughts
Tags
Recent Comments

Refactoring Teaser II: Solution: Take 1

Following is the first take at refactoring the II Teaser.

Started off by refactoring the tests:

@Test
public void mineSenderFromEdgeServerRecordInMailHeaders() {
    header(
            mail_from("67.205.47.130", "agileindia.org").receivedBy("75.119.213.4", "mails.agileindia.org"),
            mail_from(null, "mail-vw0-f172.google.com").receivedBy("67.205.47.130", "agileindia.org"));
    senderIP = extractSenderIPFrom(headers).forRecipientDomain(GMAIL);
    assertThat(senderIP).is("209.85.212.172");
    assertDistanceOfMatchingHeaderFromTopIs(2);
}
@Test
public void useSenderIPForInvalidSenderEdgeServerDomainName() {
    header(
            mail_from("67.205.47.130", "agileindia.org").receivedBy("75.119.213.4", "mails.agileindia.org"),
            mail_from("209.85.212.172", "cannot-exist.agilefaqs.com").receivedBy("67.205.47.130", "agileindia.org"));
    senderIP = extractSenderIPFrom(headers).forRecipientDomain(GMAIL);
    assertThat(senderIP).is("209.85.212.172");
    assertDistanceOfMatchingHeaderFromTopIs(2);
}
@Test
public void senderNameCanBeIPAddress() {
    header(mail_from(null, "209.85.212.172").receivedBy("67.205.47.130", "agileindia.org"));
    senderIP = extractSenderIPFrom(headers).forRecipientDomain(GMAIL);
    assertThat(senderIP).is("209.85.212.172");
    assertDistanceOfMatchingHeaderFromTopIs(1);
}
@Test
public void matchMXRecordIPWithReciever() {
    header(mail_from("", "mail-vw0-f172.google.com").receivedBy("67.205.47.130", "apache2-echo.robin.dreamhost.com"));
    senderIP = extractSenderIPFrom(headers).forRecipientDomain(GMAIL);
    assertThat(senderIP).is("209.85.212.172");
    assertDistanceOfMatchingHeaderFromTopIs(1);
}
@Test
public void skipHeaderRecordsThatDontCrossEdgeServers() {
    header(
            mail_from("192.168.1.47", "smtp.gmail.com").receivedBy("75.119.213.4", "mail.gmail.com"),
            mail_from("192.168.1.3", "192.168.6.242").receivedBy("192.168.1.47", "smtp.gmail.com"));
    senderIP = extractSenderIPFrom(headers).forRecipientDomain(GMAIL);
    assertFalse(senderIP.isValid());
    assertDistanceOfMatchingHeaderFromTopIs(0);
}

Following is the crux of the Sender Edge Server IP Extraction Algo:

public IPAddressExtractor(final List receivedHeaders, final Domain recepientDomain) {
    Domain recepientMXRecord = recepientDomain.retrieveFirstMXRecord();
    for (MatchingCriterion critierion : asList(MATCHING_DOMAIN, MATCHING_IP, MATCHING_SECOND_LEVEL_DOMAIN)) {
        Match result = foreach(receivedHeaders).and(recepientMXRecord).match(critierion);
        if (result.success()) {
            storeSenderIPWithDistance(result);
            break;
        }
    }
}

To do the whole Fluent interfaces on line number 21, I had to create a private method:

private CriterionMatcher foreach(final List mailHeaders) {
    return new CriterionMatcher(mailHeaders);
}

and a package protected CriterionMatcher class

class CriterionMatcher {
    private final List mailHeaders;
    private int counter;
    private Domain mxRecord;
 
    CriterionMatcher(final List mailHeaders) {
        this.mailHeaders = mailHeaders;
    }
 
    CriterionMatcher and(final Domain mxRecord) {
        this.mxRecord = mxRecord;
        return this;
    }
 
    Match match(final MatchingCriterion criterion) {
        for (EmailHeader header : mailHeaders) {
            counter++;
 
            if (criterion.isSatisfiedBy(mxRecord, header)) {
                return new Match(header.fromDomain, header.fromIp, counter);
            }
        }
        return Match.NULL;
    }
}

Other than the switch statement smell and conditional complexity, the original code was obsessed with Primitive Obsession code smell. To fix this issue, the first thing I had to do was great first class citizens (Objects). So I ended up creating

public class IPAddress {
    private static final String IP_ADDRESS_REGEX = "\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b";
    private static final Pattern VALID_IP = Pattern.compile(IP_ADDRESS_REGEX);
    private static final String LOCAL_HOST = "127.0.0.1";
    public static final IPAddress NULL = new IPAddress("");
 
    private final String ip;
 
    private IPAddress(final String address) {
        ip = address;
    }
 
    public static IPAddress parse(final String address) {
        if (address == null) {
            return NULL;
        }
        Matcher matcher = VALID_IP.matcher(address);
 
        if (!matcher.find()) {
            return NULL;
        }
        return new IPAddress(matcher.group(0));
    }
 
    @Override
    public String toString() {
        return ip;
    }
 
    public boolean isLocalhost() {
        return LOCAL_HOST.equals(ip);
    }
 
    public boolean isValid() {
        return this != NULL;
    }
}

and

public class Domain {
    private static final Pattern SLD = Pattern.compile("(.*\\.)?(.*\\..*)");
    public static final Domain NULL = new Domain("", Network.NULL);
 
    private final String name;
    private final Network network;
 
    protected Domain(final String domainName, final Network network) {
        name = domainName.toLowerCase();
        this.network = network;
    }
 
    public IPAddress resolveIP() {
        try {
            String ipAddress = network.ipAddressOf(name);
            return IPAddress.parse(ipAddress);
        } catch (UnknownHostException e) {
            return IPAddress.NULL;
        }
    }
 
    public Domain secondLevelDomain() {
        Matcher mxRecordMatch = SLD.matcher(name);
        if (mxRecordMatch.find()) {
            return new Domain(mxRecordMatch.group(2), network);
        }
        return this;
    }
 
    public Domain retrieveFirstMXRecord() {
        List mxRecords = network.firstMXRecordFor(name);
        if (mxRecords.size() > 0) {
            return new Domain(mxRecords.get(0), network);
        }
        return NULL;
    }
 
    public boolean isValid() {
        return this != NULL;
    }
}

To create a Domain, we use a static Factory called DomainFactory

public final class DomainFactory {
    private static final String DOMAIN_NAME_REGEX = "[\\w.-]+\\.[A-Za-z]{2,6}";
    private static final int MAX_DOMAIN_NAME_LENGTH = 255;
 
    public static Domain build(final String domainName, final Network network) {
        if (isValidDomain(domainName)) {
            return new Domain(domainName, network);
        }
        IPAddress ip = IPAddress.parse(domainName);
        if (ip.isValid()) {
            return retrieveDomainName(ip, network);
        }
        return Domain.NULL;
    }
 
    private static Domain retrieveDomainName(final IPAddress ip, final Network network) {
        try {
            String hostName = network.hostNameFor(ip.toString());
            if (ip.toString().equals(hostName)) {
                return Domain.NULL;
            }
            return new Domain(hostName, network);
        } catch (UnknownHostException e) {
            return Domain.NULL;
        }
    }
}

Finally we’ve the 3 Criterion for checking if a header contains the edge server

public abstract class MatchingCriterion {
    public static final MatchingCriterion MATCHING_DOMAIN = new MatchingDomainCriterion();
    public static final MatchingCriterion MATCHING_IP = new MatchingIPCriterion();
    public static final MatchingCriterion MATCHING_SECOND_LEVEL_DOMAIN = new MatchingSecondLevelDomainCriterion();
 
    public boolean isSatisfiedBy(final Domain mxRecord, final EmailHeader header) {
        return header.fromDomain.isValid() && satisfies(mxRecord, header);
    }
 
    protected abstract boolean satisfies(Domain mxRecord, EmailHeader header);
}
private static class MatchingDomainCriterion extends MatchingCriterion {
    @Override
    protected boolean satisfies(final Domain mxRecord, final EmailHeader header) {
        return !(header.byIp.equals(header.fromIp) || header.fromIp.isLocalhost() || !header.byDomain.equals(mxRecord));
    }
}
private static class MatchingIPCriterion extends MatchingCriterion {
    @Override
    protected boolean satisfies(final Domain mxRecord, final EmailHeader header) {
        return header.byIp.equals(mxRecord.resolveIP());
    }
}
private static class MatchingSecondLevelDomainCriterion extends MatchingCriterion {
    @Override
    protected boolean satisfies(final Domain mxRecord, final EmailHeader header) {
        Domain secondLevelDomain = mxRecord.secondLevelDomain();
        return secondLevelDomain.equals(header.byDomain.secondLevelDomain());
    }
}

Also notice that for testing purpose we don’t want to hit the network, so I created a FakeNetwork class which stubs out all Network calls. Network is injected into all Domain classes through the DomainFactory. (I’m not very happy with this design, it feels like a bit of a hack to inject Network this way.)

public class FakeNetwork extends Network {
    private static final Map domain2ip = new HashMap() {
        {
            put("mail-vw0-f172.google.com", "209.85.212.172");
            put("209.85.212.172", "mail-vw0-f172.google.com");
            put("mails.agileindia.org", "72.14.203.121");
            put("agileindia.org", "67.205.47.130");
        }
    };
 
    @Override
    public String ipAddressOf(final String domainName) throws UnknownHostException {
        return lookup(domainName);
    }
 
    @Override
    public String hostNameFor(final String ipAddress) throws UnknownHostException {
        return lookup(ipAddress);
    }
 
    @Override
    public List firstMXRecordFor(final String name) {
        return asList("agileindia.org");
    }
 
    private String lookup(final String value) throws UnknownHostException {
        String data = domain2ip.get(value);
        if (data == null) {
            throw new UnknownHostException();
        }
        return data;
    }
}

Feel free to download the whole project.

  • http://finalprefix.com/ Joel Rosario

    Here’s my take in clojure:

    ;; refactoring_teaser_2/tests.clj
    (ns refactoring-teaser-2.tests
        (:use refactoring-teaser-2.edge-header))
     
    (defn same-recipient-mx? [recipient header]
      (let [{[recipient-mx & others] :mx-records} recipient]
           (if-not (#{(:from-ip header) "127.0.0.1"} (:by-ip header))
    	       (= recipient-mx (:by header)))))
     
    (defn same-recipient-mx-ip? [recipient header]
      (let [{[recipient-mx-ip & others] :mx-ips} recipient]
           (= recipient-mx-ip (:by-ip header))))
     
    (defn same-second-level-domain? [recipient header]
      (let [{[recipient-mx & others] :mx-records} recipient]
           (= (sld recipient-mx) (sld (:by header)))))
     
    (defn ip-address-from-headers [received-headers recipient]
      (if-let [header (header-at-the-edge received-headers
    				      (ways-to-identify-edge-header
    				       recipient
    				       same-recipient-mx? same-recipient-mx-ip? same-second-level-domain?))]
    	  (sender-from header)))
     
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Specs
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     
    (use 'spec)
    (use 'refactoring-teaser-4.network)
    (use 'refactoring-teaser-4.header-info)
     
    (tests
     (spec
      "When a header matches, it should return it"
      (given
       :headers [(struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.test.com" "")])
      (fn [headers] (header-at-the-edge headers (fn [header] (= (sld "mail.test.com") (sld (:by header))))))
      (returns (struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.test.com" "")))
     
     (spec
      "When no header matches, it should find none"
      (given
       :headers [(struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.stuff.com" "")])
      (fn [headers] (header-at-the-edge headers (fn [header] (= (sld "mail.test.com") (sld (:by header))))))
      (returns nil))
     
     (spec
      "Get the sender when the receiving server has the same name as the recipient's mx."
      (given
       :headers [(struct received-header "mail.test.com" "1.2.3.4" "internal.mail.test.com" "")
    	     (struct received-header "mail.gmail.com" "2.2.2.2" "mail.test.com" "")
    	     (struct received-header "internal.mail.gmail.com" "8.8.8.8" "mail.test.com" "")]
       :recipient (struct recipient "test.com" ["mail.test.com"]))
      (fn [headers recipient] (ip-address-from-headers headers (with-mx-ips recipient)))
      (returns ["mail.gmail.com" "2.2.2.2"]))
     
     (spec
      "Get the sender when the receiving server has the same ip as the recipient's mx's ip."
      (given
       :headers [(struct received-header "1.mail.stuff.com" "5.6.7.8" "localhost" "127.0.0.1")
    	     (struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.stuff.com" "1.2.3.4")]
       :recipient (struct recipient "test.com" ["mail.test.com"]))
      (fn [headers recipient] (ip-address-from-headers headers (with-mx-ips recipient)))
      (returns ["mail.gmail.com" "2.2.2.2"]))
     
     (spec
      "Get the sender when the receiving server's sld is the same as the recipient's mx's sld."
      (given
       :headers [(struct received-header "1.mail.stuff.com" "5.6.7.8" "localhost" "127.0.0.1")
    	     (struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.test.com" "")]
       :recipient (struct recipient "test.com" ["mail.test.com"]))
      (fn [headers recipient] (ip-address-from-headers headers (with-mx-ips recipient)))
      (returns ["mail.gmail.com" "2.2.2.2"]))
     )
    ;; refactoring_teaser_2/edge-header.clj
     
    (ns refactoring-teaser-2.edge-header
        (:use refactoring-teaser-2.network)
        (:use refactoring-teaser-2.header-info))
     
    (defn sld [domain] (last (re-matches #"(.*.)?(.*..*)" domain)))
    (defn ip-in [str] (last (re-matches #".+([0-9]+.[0-9]+.[0-9]+.[0-9]+).+" str)))
     
    (defn is-ip [str] (re-matches #"[0-9]+.[0-9]+.[0-9]+.[0-9]+" str))
    (defn is-not-ip [str] (not (is-ip str)))
    (defn is-domain [str] (and (re-matches #"([w-]+.)+(w+)+" str)
    			   (is-not-ip str)))
     
    (defn only-if [test? something]
      (when (test? something) something))
     
    (defmulti header-at-the-edge (fn [_ recipient-match] (when (ifn? recipient-match) :matching-strategy)))
     
    (defmethod header-at-the-edge :default [headers matching-conditions]
      (try
       (some #(header-at-the-edge headers %) matching-conditions)
       (catch java.lang.RuntimeException e nil)))
     
    (defmethod header-at-the-edge :matching-strategy [headers matching?]
      (some #(only-if matching? %) headers))
     
    (defn with-mx-ips [recipient]
      (conj recipient [:mx-ips (map #(ip-of %) (:mx-records recipient))]))
     
    (defn ways-to-identify-edge-header [recipient & strategies]
      (map (fn [strategy] (fn [header] (strategy recipient header)))
           strategies))
     
    (defn sender-from [{from :from from-ip :from-ip :as header}]
      (cond
       (is-domain from) [from (try (ip-of from) (catch java.lang.Exception e from-ip))]
       (is-ip from) (try [(host-of from) from-ip] (catch java.lang.Exception e nil))))
    ;; refactoring_teaser_2/header-info.clj
     
    (ns refactoring-teaser-2.header-info)
     
    (defstruct received-header :from :from-ip :by :by-ip)
    (defstruct recipient :domain :mx-records)
    ;; refactoring_teaser_2/network.clj
     
    (ns refactoring-teaser-2.network)
     
    ; fake dns resolution methods
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (defn ip-of [domain]
      (cond
       (= domain "mail.test.com") "1.2.3.4"
       (= domain "mail.gmail.com") "2.2.2.2"
       (= domain "exception.com") (throw (new java.lang.Exception "testfully thrown"))
       true "5.6.7.8"))
     
    (defn host-of [ip]
      (cond
       (= ip "1.2.3.4") "mail.test.com"
       (= ip "3.3.3.3") (throw (new java.lang.Exception "testfully thrown"))
       true "example.com"))
    (ns spec)
     
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Home grown spec framework
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     
    (defn given [& params]
     (apply array-map params))
     
    (defn returns [expected] (fn [result] (= expected result)))
     
    (defn should [fn] fn)
     
    (defn spec [desc given-parameter-map test-fn expected]
      (let [result (apply test-fn (vals given-parameter-map))]
           (print
    	(if (expected result)
    	    "."
    	  (str "n" desc
    	       "nGiven: " (prn-str given-parameter-map)
    	       "Expectation failedn" )))))
     
    (defn tests [& rest] (println))
  • http://finalprefix.com Joel Rosario

    Here’s my take in clojure:

    ;; refactoring_teaser_2/tests.clj
    (ns refactoring-teaser-2.tests
        (:use refactoring-teaser-2.edge-header))
     
    (defn same-recipient-mx? [recipient header]
      (let [{[recipient-mx & others] :mx-records} recipient]
           (if-not (#{(:from-ip header) "127.0.0.1"} (:by-ip header))
    	       (= recipient-mx (:by header)))))
     
    (defn same-recipient-mx-ip? [recipient header]
      (let [{[recipient-mx-ip & others] :mx-ips} recipient]
           (= recipient-mx-ip (:by-ip header))))
     
    (defn same-second-level-domain? [recipient header]
      (let [{[recipient-mx & others] :mx-records} recipient]
           (= (sld recipient-mx) (sld (:by header)))))
     
    (defn ip-address-from-headers [received-headers recipient]
      (if-let [header (header-at-the-edge received-headers
    				      (ways-to-identify-edge-header
    				       recipient
    				       same-recipient-mx? same-recipient-mx-ip? same-second-level-domain?))]
    	  (sender-from header)))
     
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Specs
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     
    (use 'spec)
    (use 'refactoring-teaser-4.network)
    (use 'refactoring-teaser-4.header-info)
     
    (tests
     (spec
      "When a header matches, it should return it"
      (given
       :headers [(struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.test.com" "")])
      (fn [headers] (header-at-the-edge headers (fn [header] (= (sld "mail.test.com") (sld (:by header))))))
      (returns (struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.test.com" "")))
     
     (spec
      "When no header matches, it should find none"
      (given
       :headers [(struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.stuff.com" "")])
      (fn [headers] (header-at-the-edge headers (fn [header] (= (sld "mail.test.com") (sld (:by header))))))
      (returns nil))
     
     (spec
      "Get the sender when the receiving server has the same name as the recipient's mx."
      (given
       :headers [(struct received-header "mail.test.com" "1.2.3.4" "internal.mail.test.com" "")
    	     (struct received-header "mail.gmail.com" "2.2.2.2" "mail.test.com" "")
    	     (struct received-header "internal.mail.gmail.com" "8.8.8.8" "mail.test.com" "")]
       :recipient (struct recipient "test.com" ["mail.test.com"]))
      (fn [headers recipient] (ip-address-from-headers headers (with-mx-ips recipient)))
      (returns ["mail.gmail.com" "2.2.2.2"]))
     
     (spec
      "Get the sender when the receiving server has the same ip as the recipient's mx's ip."
      (given
       :headers [(struct received-header "1.mail.stuff.com" "5.6.7.8" "localhost" "127.0.0.1")
    	     (struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.stuff.com" "1.2.3.4")]
       :recipient (struct recipient "test.com" ["mail.test.com"]))
      (fn [headers recipient] (ip-address-from-headers headers (with-mx-ips recipient)))
      (returns ["mail.gmail.com" "2.2.2.2"]))
     
     (spec
      "Get the sender when the receiving server's sld is the same as the recipient's mx's sld."
      (given
       :headers [(struct received-header "1.mail.stuff.com" "5.6.7.8" "localhost" "127.0.0.1")
    	     (struct received-header "mail.gmail.com" "2.2.2.2" "1.mail.test.com" "")]
       :recipient (struct recipient "test.com" ["mail.test.com"]))
      (fn [headers recipient] (ip-address-from-headers headers (with-mx-ips recipient)))
      (returns ["mail.gmail.com" "2.2.2.2"]))
     )
    ;; refactoring_teaser_2/edge-header.clj
     
    (ns refactoring-teaser-2.edge-header
        (:use refactoring-teaser-2.network)
        (:use refactoring-teaser-2.header-info))
     
    (defn sld [domain] (last (re-matches #"(.*\.)?(.*\..*)" domain)))
    (defn ip-in [str] (last (re-matches #".+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).+" str)))
     
    (defn is-ip [str] (re-matches #"[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" str))
    (defn is-not-ip [str] (not (is-ip str)))
    (defn is-domain [str] (and (re-matches #"([\w-]+\.)+(\w+)+" str)
    			   (is-not-ip str)))
     
    (defn only-if [test? something]
      (when (test? something) something))
     
    (defmulti header-at-the-edge (fn [_ recipient-match] (when (ifn? recipient-match) :matching-strategy)))
     
    (defmethod header-at-the-edge :default [headers matching-conditions]
      (try
       (some #(header-at-the-edge headers %) matching-conditions)
       (catch java.lang.RuntimeException e nil)))
     
    (defmethod header-at-the-edge :matching-strategy [headers matching?]
      (some #(only-if matching? %) headers))
     
    (defn with-mx-ips [recipient]
      (conj recipient [:mx-ips (map #(ip-of %) (:mx-records recipient))]))
     
    (defn ways-to-identify-edge-header [recipient & strategies]
      (map (fn [strategy] (fn [header] (strategy recipient header)))
           strategies))
     
    (defn sender-from [{from :from from-ip :from-ip :as header}]
      (cond
       (is-domain from) [from (try (ip-of from) (catch java.lang.Exception e from-ip))]
       (is-ip from) (try [(host-of from) from-ip] (catch java.lang.Exception e nil))))
    ;; refactoring_teaser_2/header-info.clj
     
    (ns refactoring-teaser-2.header-info)
     
    (defstruct received-header :from :from-ip :by :by-ip)
    (defstruct recipient :domain :mx-records)
    ;; refactoring_teaser_2/network.clj
     
    (ns refactoring-teaser-2.network)
     
    ; fake dns resolution methods
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (defn ip-of [domain]
      (cond
       (= domain "mail.test.com") "1.2.3.4"
       (= domain "mail.gmail.com") "2.2.2.2"
       (= domain "exception.com") (throw (new java.lang.Exception "testfully thrown"))
       true "5.6.7.8"))
     
    (defn host-of [ip]
      (cond
       (= ip "1.2.3.4") "mail.test.com"
       (= ip "3.3.3.3") (throw (new java.lang.Exception "testfully thrown"))
       true "example.com"))
    (ns spec)
     
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Home grown spec framework
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     
    (defn given [& params]
     (apply array-map params))
     
    (defn returns [expected] (fn [result] (= expected result)))
     
    (defn should [fn] fn)
     
    (defn spec [desc given-parameter-map test-fn expected]
      (let [result (apply test-fn (vals given-parameter-map))]
           (print
    	(if (expected result)
    	    "."
    	  (str "\n" desc
    	       "\nGiven: " (prn-str given-parameter-map)
    	       "Expectation failed\n" )))))
     
    (defn tests [& rest] (println))

    Licensed under
Creative Commons License