gov.medicaid.screening.dao.impl.BBHTLicenseDAOBean.java Source code

Java tutorial

Introduction

Here is the source code for gov.medicaid.screening.dao.impl.BBHTLicenseDAOBean.java

Source

/*
 * Copyright (C) 2012 TopCoder Inc., All Rights Reserved.
 */
package gov.medicaid.screening.dao.impl;

import gov.medicaid.entities.Address;
import gov.medicaid.entities.BBHTLicenseSearchCriteria;
import gov.medicaid.entities.License;
import gov.medicaid.entities.LicenseStatus;
import gov.medicaid.entities.LicenseType;
import gov.medicaid.entities.ProviderProfile;
import gov.medicaid.entities.SearchCriteria;
import gov.medicaid.entities.SearchResult;
import gov.medicaid.entities.Sex;
import gov.medicaid.entities.User;
import gov.medicaid.screening.dao.BBHTLicenseDAO;
import gov.medicaid.screening.services.ErrorCode;
import gov.medicaid.screening.services.ParsingException;
import gov.medicaid.screening.services.ServiceException;
import gov.medicaid.screening.services.impl.LogUtil;

import java.io.IOException;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.topcoder.util.log.Level;

/**
 * This defines the logic for searching for Nursing Licenses.
 *
 * @author sampath01, j3_guile
 * @version 1.0
 */
@Stateless
@Local(BBHTLicenseDAO.class)
@TransactionManagement(TransactionManagementType.CONTAINER)
public class BBHTLicenseDAOBean extends BaseDAO implements BBHTLicenseDAO {

    /**
     * Represents all matching rows to parse.
     */
    private static final String GRID_ROW_SELECTOR = "#_ctl7_grdSearchResults tr.TableItem, "
            + "#_ctl7_grdSearchResults tr.TableAlternatingItem";

    /**
     * The supported columns for this implementation.
     */
    private static final Map<String, String> SORT_COLUMNS = new HashMap<String, String>() {
        /**
         * Generated serial version UID.
         */
        private static final long serialVersionUID = -7522184539548312606L;

        {
            put("firstName", "profile.user.firstName");
            put("middleName", "profile.user.middleName");
            put("lastName", "profile.user.lastName");
            put("type", "type.name");
            put("originalIssueDate", "originalIssueDate");
            put("dateOfBirth", "profile.dob");
            put("licenseNumber", "licenseNumber");
        }
    };

    /**
     * The supported license type mappings for this source.
     */
    private static final Map<String, String> TYPES = new HashMap<String, String>() {
        /**
         * Generated serial version UID.
         */
        private static final long serialVersionUID = -7731473546472088567L;

        {
            put("CD", "LADC");
            put("PC", "LPC");
            put("CC", "LPCC");
        }
    };

    /**
     * Date format used by this source.
     */
    private static final String DATE_FORMAT = "MM-dd-yyyy";

    /**
     * Default empty constructor.
     */
    public BBHTLicenseDAOBean() {
    }

    /**
     * Searches for providers with BBHT license using the given criteria.
     *
     * @param criteria the search criteria
     * @return the matched results
     * @throws ParsingException if any parsing errors are encountered
     * @throws ServiceException for any other exceptions encountered
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public SearchResult<License> search(BBHTLicenseSearchCriteria criteria)
            throws ParsingException, ServiceException {
        String signature = "BBHTLicenseDataAccessImpl#search";
        LogUtil.traceEntry(getLog(), signature, new String[] { "criteria" }, new Object[] { criteria });

        if (criteria == null) {
            throw new ServiceException(ErrorCode.MITA10005.getDesc());
        }

        if (Util.isBlank(criteria.getLastName()) && Util.isBlank(criteria.getFirstName())
                && Util.isBlank(criteria.getIdentifier()) && criteria.getLicenseType() == null) {
            throw new ServiceException(ErrorCode.MITA10005.getDesc());
        }

        boolean byName = false;
        if (Util.isNotBlank(criteria.getLastName()) || Util.isNotBlank(criteria.getFirstName())) {
            byName = true;
        } else {
            // transform the license type mapping
            boolean found = false;
            for (Map.Entry<String, String> type : TYPES.entrySet()) {
                if (type.getValue().equals(criteria.getLicenseType().getName())) {
                    criteria.getLicenseType().setName(type.getKey());
                    found = true;
                }
            }
            if (!found) {
                throw new ServiceException(ErrorCode.MITA10009.getDesc());
            }

            if (Util.isBlank(criteria.getIdentifier())) {
                throw new ServiceException(ErrorCode.MITA10004.getDesc());
            }

            if (!Util.isDigits(criteria.getIdentifier())) {
                throw new ServiceException(ErrorCode.MITA10010.getDesc());
            }
        }

        validateSortOptions(criteria, SORT_COLUMNS);

        try {
            SearchResult<License> allResults = getAllResults(criteria, byName);
            SearchResult<License> results = trimResults(allResults, criteria.getPageSize(),
                    criteria.getPageNumber(), SORT_COLUMNS.get(criteria.getSortColumn()), criteria.getSortOrder());

            logSearchEntry(criteria);
            return LogUtil.traceExit(getLog(), signature, results);
        } catch (ClientProtocolException e) {
            LogUtil.traceError(getLog(), signature, e);
            throw new ServiceException(ErrorCode.MITA50001.getDesc(), e);
        } catch (URISyntaxException e) {
            LogUtil.traceError(getLog(), signature, e);
            throw new ServiceException(ErrorCode.MITA50001.getDesc(), e);
        } catch (IOException e) {
            LogUtil.traceError(getLog(), signature, e);
            throw new ServiceException(ErrorCode.MITA50001.getDesc(), e);
        } catch (ParseException e) {
            LogUtil.traceError(getLog(), signature, e);
            throw new ServiceException(ErrorCode.MITA50001.getDesc(), e);
        }
    }

    /**
     * Performs verification of the given license.
     *
     * @param license the license to verify
     * @return true if the license has been verified, false otherwise
     *
     * @throws ParsingException if any parsing errors are encountered
     * @throws ServiceException for any other exceptions encountered
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public boolean verifyLicense(License license) throws ParsingException, ServiceException {
        String signature = "BBHTLicenseDataAccessImpl#verifyLicense";
        LogUtil.traceEntry(getLog(), signature, new String[] { "license" }, new Object[] { license });

        if (license == null) {
            throw new ServiceException(ErrorCode.MITA10005.getDesc());
        }

        BBHTLicenseSearchCriteria criteria = new BBHTLicenseSearchCriteria();
        criteria.setLicenseType(license.getType());
        criteria.setIdentifier(license.getLicenseNumber());
        SearchResult<License> result = search(criteria);
        // single result expected
        return result != null && !result.getItems().isEmpty()
                && "Active".equals(result.getItems().get(0).getStatus().getName());
    }

    /**
     * Performs renewal of the given license. Currently unsupported.
     *
     * @param license the license to renew
     *
     * @throws ParsingException if any parsing errors are encountered
     * @throws ServiceException for any other exceptions encountered
     */
    @Override
    public void renew(License license) throws ParsingException, ServiceException {
        throw new UnsupportedOperationException("Operation currently unsupported");
    }

    /**
     * Performs a search for all possible results.
     *
     * @param criteria The search criteria.
     * @param byName flag indicating it is a name search
     * @return the search result for licenses
     *
     * @throws URISyntaxException if an error occurs while building the URL.
     * @throws ClientProtocolException if client does not support protocol used.
     * @throws IOException if an error occurs while parsing response.
     * @throws ParseException if an error occurs while parsing response.
     * @throws ServiceException for any other problems encountered
     */
    private SearchResult<License> getAllResults(BBHTLicenseSearchCriteria criteria, boolean byName)
            throws URISyntaxException, ClientProtocolException, IOException, ParseException, ServiceException {
        DefaultHttpClient client = new DefaultHttpClient(getLaxSSLConnectionManager());
        client.setRedirectStrategy(new LaxRedirectStrategy());

        HttpGet getSearch = new HttpGet(new URIBuilder(getSearchURL()).build());
        HttpResponse response = client.execute(getSearch);
        verifyAndAuditCall(getSearchURL(), response);

        Document page = Jsoup.parse(EntityUtils.toString(response.getEntity()));

        HttpPost search = new HttpPost(new URIBuilder(getSearchURL()).build());

        List<License> allLicenses = new ArrayList<License>();

        // switch to search by name screen
        if (byName) {
            HttpEntity entity = postForm(getSearchURL(), client, search,
                    new String[][] { { "__EVENTTARGET", "_ctl7_rbtnSearch_1" }, { "__EVENTARGUMENT", "" },
                            { "_ctl7:ddlbLicenseType", "CD" }, { "_ctl7:rbtnSearch", "2" },
                            { "_ctl7:txtLicenseNumber", "" },
                            { "__VIEWSTATE", page.select("input[name=__VIEWSTATE]").first().val() } },
                    true);

            page = Jsoup.parse(EntityUtils.toString(entity));
            entity = getResultPage(criteria, client, page, search, "_ctl7:cmdSearch", getSearchURL());
            page = Jsoup.parse(EntityUtils.toString(entity));

            // get the data grid entries
            if (page.select("table#_ctl7_grdSearchResults").size() < 1) {
                throw new ParsingException(ErrorCode.MITA50002.getDesc());
            }

            Elements rows = page.select(GRID_ROW_SELECTOR);
            while (rows.size() > 0) {
                for (Element row : rows) {
                    String url = row.select("a").first().attr("href");
                    String licenseNo = row.select("td:eq(5)").text();
                    HttpGet getDetail = new HttpGet(Util.replaceLastURLPart(getSearchURL(), url));
                    response = client.execute(getDetail);
                    verifyAndAuditCall(getSearchURL(), response);
                    Document licenseDetails = Jsoup.parse(EntityUtils.toString(response.getEntity()));
                    allLicenses.add(parseLicense(licenseDetails, licenseNo));
                }
                rows.clear();

                // check for next page
                Element currentPage = page.select("#_ctl7_grdSearchResults tr.TablePager span").first();
                if (getLog() != null) {
                    getLog().log(Level.DEBUG, "Current page is: " + currentPage.text());
                }
                Element pageLink = currentPage.nextElementSibling();
                if (pageLink != null && pageLink.hasAttr("href")) {
                    if (getLog() != null) {
                        getLog().log(Level.DEBUG, "There are more results, getting the next page.");
                    }

                    String target = parseEventTarget(pageLink.attr("href"));
                    entity = getResultPage(criteria, client, page, search, target, getSearchURL());
                    page = Jsoup.parse(EntityUtils.toString(entity));
                    rows = page.select(GRID_ROW_SELECTOR);
                }
            }

        } else { // search by license number (site supports only exact match)
            HttpEntity entity = postForm(getSearchURL(), client, search,
                    new String[][] { { "__EVENTTARGET", "_ctl7:cmdSearch" }, { "__EVENTARGUMENT", "" },
                            { "_ctl7:ddlbLicenseType", Util.defaultString(criteria.getLicenseType().getName()) },
                            { "_ctl7:rbtnSearch", "1" },
                            { "_ctl7:txtLicenseNumber", Util.defaultString(criteria.getIdentifier()) },
                            { "__VIEWSTATE", page.select("input[name=__VIEWSTATE]").first().val() } },
                    true);

            page = Jsoup.parse(EntityUtils.toString(entity));
            if (page.select("span#lblFormTitle").text().equals("License Details")) {
                String prefLicenseNo = criteria.getIdentifier();
                allLicenses.add(parseLicense(page, prefLicenseNo));
            }
        }

        SearchResult<License> searchResult = new SearchResult<License>();
        searchResult.setItems(allLicenses);
        return searchResult;
    }

    /**
     * Parses the nursing license details page.
     *
     * @param page the details page
     * @param licenseNo if user has multiple licenses, this one will be used
     * @return the parsed license details
     * @throws ParsingException if the page does not contain the expected elements
     */
    private License parseLicense(Document page, String licenseNo) throws ParsingException {
        if (!page.select("span#lblFormTitle").text().equals("License Details")) {
            throw new ParsingException(ErrorCode.MITA50002.getDesc());
        }

        License license = new License();
        ProviderProfile profile = new ProviderProfile();
        license.setProfile(profile);

        String fullNameWithType = page.select("#_ctl7_lblName").text();
        String fullName = fullNameWithType.indexOf(",") != -1
                ? fullNameWithType.substring(0, fullNameWithType.indexOf(","))
                : fullNameWithType;

        User user = new User();
        profile.setUser(user);
        String[] nameParts = fullName.split(" ");
        user.setLastName(nameParts[nameParts.length - 1]);
        if (nameParts.length > 1) {
            user.setFirstName(nameParts[0]);
        }
        // everything else goes to middle name (per site behavior)
        if (nameParts.length > 2) {
            StringBuffer sb = new StringBuffer();
            for (int i = 1; i < nameParts.length - 1; i++) {
                if (sb.length() > 0) {
                    sb.append(" ");
                }
                sb.append(nameParts[i]);
            }
            user.setMiddleName(sb.toString());
        }

        String gender = page.select("#_ctl7_lblGender").text();
        if (Util.isNotBlank(gender)) {
            if ("Female".equals(gender)) {
                profile.setSex(Sex.FEMALE);
            } else {
                profile.setSex(Sex.MALE);
            }
        }

        String city = page.select("#_ctl7_lblPublicCity").text();
        if (Util.isNotBlank(city)) {
            List<Address> addresses = new ArrayList<Address>();
            Address address = new Address();
            addresses.add(address);
            address.setCity(city);
            profile.setAddresses(addresses);
        }

        Elements licenses = page.select("#_ctl7_dgLicense tr.Normal");
        for (Element row : licenses) {
            String licenseNumber = row.select("td:eq(1)").text();
            if (licenseNo != null && !licenseNumber.startsWith(licenseNo)) {
                // user has multiple licenses, the results will show this user twice (search by name)
                continue;
            }
            license.setLicenseNumber(licenseNumber);

            LicenseType type = new LicenseType();
            type.setName(row.select("td:eq(0)").text());
            license.setType(type);

            LicenseStatus status = new LicenseStatus();
            status.setName(row.select("td:eq(2)").text());
            license.setStatus(status);

            String issueDate = row.select("td:eq(3)").text();
            if (Util.isNotBlank(issueDate)) {
                license.setOriginalIssueDate(parseDate(issueDate, DATE_FORMAT));
            }

            String expirationDate = row.select("td:eq(4)").text();
            if (Util.isNotBlank(expirationDate)) {
                license.setExpireDate(parseDate(expirationDate, DATE_FORMAT));
            }
        }
        licenses.clear();
        return license;
    }

    /**
     * Retrieves the result page for the name query (paginated).
     *
     * @param criteria the search criteria
     * @param client the HTTP client
     * @param page the current page
     * @param search the search URL
     * @param target the paging command
     * @param searchURL the search URL
     * @return the page with the requested paginated results
     * @throws ClientProtocolException if client does not support protocol used.
     * @throws IOException if an error occurs while parsing response.
     * @throws ServiceException for any other problems encountered
     */
    private HttpEntity getResultPage(SearchCriteria criteria, DefaultHttpClient client, Document page,
            HttpPost search, String target, String searchURL) throws IOException, ServiceException {
        HttpEntity entity;
        entity = postForm(searchURL, client, search,
                new String[][] { { "__EVENTTARGET", target }, { "__EVENTARGUMENT", "" },
                        { "_ctl7:rbtnSearch", "2" },
                        { "_ctl7:txtFirstName", Util.defaultString(criteria.getFirstName()) },
                        { "_ctl7:txtLastName", Util.defaultString(criteria.getLastName()) },
                        { "__VIEWSTATE", page.select("input[name=__VIEWSTATE]").first().val() } },
                true);
        return entity;
    }
}