org.extensiblecatalog.ncip.v2.voyager.VoyagerLookupItemSetService.java Source code

Java tutorial

Introduction

Here is the source code for org.extensiblecatalog.ncip.v2.voyager.VoyagerLookupItemSetService.java

Source

/**
 * Copyright (c) 2010 eXtensible Catalog Organization
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the MIT/X11 license. The text of the license can be
 * found at http://www.opensource.org/licenses/mit-license.php.
 */

package org.extensiblecatalog.ncip.v2.voyager;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.extensiblecatalog.ncip.v2.common.ConnectorConfigurationFactory;
import org.extensiblecatalog.ncip.v2.service.*;
import org.extensiblecatalog.ncip.v2.voyager.util.ILSException;
import org.extensiblecatalog.ncip.v2.voyager.util.ItemToken;
import org.extensiblecatalog.ncip.v2.voyager.util.VoyagerConfiguration;
import org.extensiblecatalog.ncip.v2.voyager.util.VoyagerConstants;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.xpath.XPath;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * This class implements the Lookup Item Set service for the Voyager back-end connector.
 *
 * @author SharmilaR
 */
public class VoyagerLookupItemSetService implements LookupItemSetService {

    static Logger log = Logger.getLogger(VoyagerLookupItemSetService.class);

    private VoyagerConfiguration voyagerConfig;
    {
        try {
            voyagerConfig = (VoyagerConfiguration) new ConnectorConfigurationFactory(new Properties())
                    .getConfiguration();
        } catch (ToolkitException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    final int MAX_ITEMS_TO_RETURN = Integer
            .parseInt((String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_VOYAGER_MAX_LUIS_ITEMS));

    static Random random = new Random();

    static HashMap<String, ItemToken> tokens = new HashMap<String, ItemToken>();

    VoyagerRemoteServiceManager voyagerSvcMgr;

    // Helper class for spinning off threads
    class HoldingInfoFromWeb implements Callable {
        private final int index;
        private final BibliographicId bibId;
        private Document restful;
        private Document xml;

        HoldingInfoFromWeb(int index, BibliographicId bibId) {
            this.index = index;
            this.bibId = bibId;
        }

        public HoldingInfoFromWeb call() {
            restful = getHoldingRecordsFromRestful(bibId);
            xml = getHoldingRecordsFromXml(bibId);
            return this;
        }

        public int getIndex() {
            return index;
        }

        public Document getRestful() {
            return restful;
        }

        public Document getXml() {
            return xml;
        }
    }

    /**
     * Construct a VoyagerRemoteServiceManager; 
     * this class is not configurable so there are no parameters.
     */
    public VoyagerLookupItemSetService() {
    }

    /**
     * Handles a NCIP LookupItem service by returning data from voyager.
     *
     * @param initData       the LookupItemInitiationData
     * @param serviceManager provides access to remote services
     * @return LookupItemResponseData
     */
    @Override
    public LookupItemSetResponseData performService(LookupItemSetInitiationData initData,
            ServiceContext serviceContext, RemoteServiceManager serviceManager) throws ServiceException {

        voyagerSvcMgr = (VoyagerRemoteServiceManager) serviceManager;
        LookupItemSetResponseData luisResponseData = new LookupItemSetResponseData();
        List<Problem> problems = new ArrayList<Problem>();
        Date sService = new Date();

        log.info("Performing LUIS service.");

        List<BibliographicId> bibIds = initData.getBibliographicIds();
        if (bibIds == null) {
            problems.addAll(ServiceHelper.generateProblems(Version1GeneralProcessingError.NEEDED_DATA_MISSING, null,
                    null, "Missing Bib IDs"));
            luisResponseData.setProblems(problems);
            return luisResponseData;
        }

        List<HoldingsSet> holdingSets = new ArrayList<HoldingsSet>();
        List<String> holdingIds = null;
        List<String> itemIds = null;
        Document holdingsDocFromRestful = new Document();
        Document holdingsDocFromXml = new Document();

        int itemCount = 0;
        boolean reachedMaxItemCount = false;

        String token = initData.getNextItemToken();
        ItemToken nextItemToken = null;
        if (token != null) {
            nextItemToken = tokens.get(token);
            if (nextItemToken != null) {
                int index = getIndexOfBibId(bibIds, nextItemToken.getBibliographicId());
                if (index != -1) {
                    bibIds.subList(0, index).clear();
                }
                // Remove token from memory hashmap
                tokens.remove(token);
            } else {
                problems.addAll(
                        ServiceHelper.generateProblems(Version1GeneralProcessingError.TEMPORARY_PROCESSING_FAILURE,
                                null, token, "Invalid nextItemToken"));
                luisResponseData.setProblems(problems);
                return luisResponseData;
            }
            log.debug("after removing already processed Bib ids = " + bibIds);
        }

        // Retrieve XML from vxws web services
        List<Document> holdingsDocsFromRestful = new ArrayList<Document>(bibIds.size());
        List<Document> holdingsDocsFromXml = new ArrayList<Document>(bibIds.size());
        int i;
        int numBibs = bibIds.size();

        // In a non-consortial environment, most LUIS calls will be on a single bib
        if (numBibs < 2) {
            for (i = 0; i < numBibs; i++) {
                BibliographicId bibId = bibIds.get(i);
                holdingsDocsFromRestful.add(i, getHoldingRecordsFromRestful(bibId));
                holdingsDocsFromXml.add(i, getHoldingRecordsFromXml(bibId));
            }
            // But in a consortial environment, there can be many bibs in a LUIS call
            // For now, let's look up each bib in a separate thread; perhaps later, we may need to get more sophisticated/smart w/r/t resources?
        } else {
            ExecutorService exec = Executors.newFixedThreadPool(numBibs);
            List<Future<HoldingInfoFromWeb>> futureList = new ArrayList<Future<HoldingInfoFromWeb>>(numBibs);
            List<Callable<HoldingInfoFromWeb>> callList = new ArrayList<Callable<HoldingInfoFromWeb>>(numBibs);

            for (i = 0; i < numBibs; i++) {
                BibliographicId bibId = bibIds.get(i);
                callList.add(new HoldingInfoFromWeb(i, bibId));
            }
            try {
                futureList = exec.invokeAll(callList);
                for (Future<HoldingInfoFromWeb> future : futureList) {
                    HoldingInfoFromWeb result = future.get();
                    holdingsDocsFromRestful.add(result.getIndex(), result.getRestful());
                    holdingsDocsFromXml.add(result.getIndex(), result.getXml());
                }
            } catch (InterruptedException ex) {
                log.error("Error calling vxws services via ExecutorService: " + ex);
            } catch (ExecutionException ex) {
                log.error("Error calling vxws services via ExecutorService: " + ex);
            } finally {
                exec.shutdownNow();
            }
        }

        List<BibInformation> bibInformations = new ArrayList<BibInformation>();

        for (i = 0; i < numBibs; i++) {
            BibliographicId bibId = bibIds.get(i);

            String id = null;
            String itemAgencyId = null;

            id = bibId.getBibliographicRecordId().getBibliographicRecordIdentifier();
            itemAgencyId = bibId.getBibliographicRecordId().getAgencyId().getValue();

            try {
                BibInformation bibInformation = new BibInformation();
                bibInformation.setBibliographicId(bibId);

                if (!checkValidAgencyId(itemAgencyId)) {
                    log.error("Unrecognized Bibliographic Record Agency Id: " + itemAgencyId);
                    problems.addAll(
                            ServiceHelper.generateProblems(Version1GeneralProcessingError.NEEDED_DATA_MISSING, null,
                                    null, "Unrecognized Bibliographic Record Agency Id"));
                    bibInformation.setProblems(problems);
                    bibInformations.add(bibInformation);
                    continue;
                }

                // Is the bib field empty?
                if (id.equals("") || itemAgencyId.equals("")) {
                    log.error("Missing Bib Id or Agency Id");
                    problems.addAll(
                            ServiceHelper.generateProblems(Version1GeneralProcessingError.NEEDED_DATA_MISSING, null,
                                    null, "Missing Bib ID or item Agency Id"));
                    bibInformation.setProblems(problems);
                    bibInformations.add(bibInformation);
                    continue;
                }

                // We had already called these services in the beginning
                holdingsDocFromRestful = holdingsDocsFromRestful.get(i);
                holdingsDocFromXml = holdingsDocsFromXml.get(i);

                if (holdingsDocFromXml == null) {
                    problems.addAll(ServiceHelper.generateProblems(
                            Version1GeneralProcessingError.TEMPORARY_PROCESSING_FAILURE, null, id,
                            "Problem contacting the vxws service"));
                    luisResponseData.setProblems(problems);
                    return luisResponseData;
                }

                if (!doesRecordExist(holdingsDocFromXml)) {
                    log.error("Record does not exist");
                    problems.addAll(ServiceHelper.generateProblems(Version1LookupItemProcessingError.UNKNOWN_ITEM,
                            null, id, "Record does not exist"));
                    bibInformation.setProblems(problems);
                    bibInformations.add(bibInformation);
                    continue;
                }

                // Get holding Ids belonging to this bib
                holdingIds = getHoldingIdsFromHoldingDoc(holdingsDocFromXml);

                if (nextItemToken != null) {
                    int index = holdingIds.indexOf(nextItemToken.getHoldingsId());
                    if (index != -1) {
                        holdingIds.subList(0, index).clear();
                    }
                }

                if (holdingIds == null) {
                    log.error("Bib does not have a holding record associated with it");
                    problems.addAll(ServiceHelper.generateProblems(Version1LookupItemProcessingError.UNKNOWN_ITEM,
                            null, id, "Record does not have a holding record associated with it"));
                    bibInformation.setProblems(problems);
                    bibInformations.add(bibInformation);
                    continue; // Bib record exists but has no Holding records
                }

                // Set bib desc
                BibliographicDescription bDesc = null;
                if (initData.getBibliographicDescriptionDesired()) {
                    bDesc = getBibliographicDescriptionForBibId(holdingsDocFromXml);
                    bibInformation.setBibliographicDescription(bDesc);
                }

                // title hold queue length
                // Ignoring in vxws release
                /*BigDecimal titleHoldQueue = voyagerSvcMgr.getTitleLevelHoldQueueLength(id);
                if (titleHoldQueue != null) {
                bibInformation.setTitleHoldQueueLength(titleHoldQueue);
                }*/

                holdingSets = new ArrayList<HoldingsSet>();

                // Build HoldingSet with items in it
                for (String holdingId : holdingIds) {
                    log.debug("Processing Holding id = " + holdingId);
                    itemIds = getItemIdsFromHoldingDoc(holdingId, holdingsDocFromRestful);
                    log.debug("All itemIds: " + itemIds);

                    // We need to distinguish between Holdings with actual Item Records vs. ones without.
                    // This is so we may parse Item Record-less Holdings records differently, e.g.,
                    // add item-like info to them later (sans ItemID, of course, since they doen't refer to actual Item Records).
                    // There are quite a few instances of Holdings without Item Records, but we still want to supply
                    // useful information about them. The major (only?) difference will be the lack of ItemID.
                    boolean hasItems = false;
                    // Get Bib desc, holding set info only if items exist for that holdings
                    if (itemIds != null && itemIds.size() > 0) {
                        hasItems = true;
                    }

                    if (nextItemToken != null) {
                        int index = itemIds.indexOf(nextItemToken.getItemId());
                        log.debug("Index of nextitem: " + index);
                        if (index != -1) {
                            itemIds.subList(0, index + 1).clear();
                        }
                        log.debug("after removing already processed item ids = " + itemIds);
                        if (itemIds.size() < 1) {
                            continue;
                        }
                    }

                    HoldingsSet holdingSet = new HoldingsSet();
                    // Set Bib Id and holdings set id
                    holdingSet.setHoldingsSetId(holdingId);

                    //                    if (initData.getElectronicResourceDesired()) {
                    ElectronicResource eResource = getElectronicResourceForHoldingId(holdingId, holdingsDocFromXml);
                    if (eResource != null) {
                        holdingSet.setElectronicResource(eResource);
                    }
                    //                    }

                    String callNumber = null;
                    if (hasItems) {
                        callNumber = getCallNumberForHoldingDoc(holdingId, holdingsDocFromRestful);
                    } else {
                        callNumber = getCallNumberForHoldingDocFromXml(holdingId, holdingsDocFromXml);
                    }
                    if (callNumber != null) {
                        holdingSet.setCallNumber(callNumber);
                    }

                    if (hasItems) {
                        int newItemCount = itemCount + itemIds.size();

                        if (newItemCount > MAX_ITEMS_TO_RETURN) {
                            itemIds = getItemIdSubset(itemIds, itemCount);
                            log.debug("Subset itemIds: " + itemIds);
                        }

                        Map<String, ItemInformation> itemInformations = new HashMap<String, ItemInformation>();

                        for (String itemId : itemIds) {
                            ItemInformation itemInformation = new ItemInformation();
                            ItemId item = new ItemId();
                            item.setItemIdentifierValue(itemId);
                            item.setAgencyId(new AgencyId(itemAgencyId));
                            itemInformation.setItemId(item);
                            itemInformations.put(itemId, itemInformation);
                        }

                        Map<String, String> statuses = null;
                        if (initData.getCirculationStatusDesired()) {
                            statuses = getCirculationStatusForItemIds(itemIds, holdingsDocFromRestful);
                        }

                        // TODO: Double check that this really isn't available through GetHoldings
                        /* Ignoring for vxws release
                        Map<String, BigDecimal> lengths = null;
                        if (initData.getHoldQueueLengthDesired()) {
                        lengths = voyagerSvcMgr.getHoldQueueLengthForItemIds(itemIds);
                        }
                        */

                        Map<String, ItemDescription> itemDescriptions = null;
                        //if (initData.getItemDescriptionDesired()) {
                        itemDescriptions = getItemDescriptionForItemIds(itemIds, holdingsDocFromRestful);
                        //}

                        Map<String, Location> locations = null;
                        if (initData.getLocationDesired()) {
                            locations = getLocationForItemIds(itemIds, holdingsDocFromRestful);
                        }

                        Map<String, String> copyNumbers = new HashMap<String, String>();
                        Iterator<String> itrId = itemDescriptions.keySet().iterator();
                        while (itrId.hasNext()) {
                            String key = itrId.next();
                            copyNumbers.put(itemDescriptions.get(key).getCopyNumber(), key);
                        }

                        Map<String, GregorianCalendar> dueDates = null;
                        dueDates = getDueDateForItemIds(itemIds, holdingsDocFromXml, copyNumbers, holdingId);

                        Iterator<String> itr = itemInformations.keySet().iterator();
                        while (itr.hasNext()) {
                            ItemOptionalFields iof = new ItemOptionalFields();
                            String key = itr.next();
                            if (statuses != null) {
                                String status = statuses.get(key);
                                log.debug("Status for key " + status);
                                try {
                                    if (statuses.get(key) != null) {
                                        iof.setCirculationStatus(XcCirculationStatus
                                                .find(XcCirculationStatus.XC_CIRCULATION_STATUS, status));
                                    }
                                } catch (ServiceException se) {
                                    log.error("Unrecognized item status");
                                }
                            }

                            /*  Ignoring for vxws release
                            if (lengths != null) {
                            iof.setHoldQueueLength(lengths.get(key));
                            } */

                            if (itemDescriptions != null) {
                                iof.setItemDescription(itemDescriptions.get(key));
                            }

                            if (locations != null) {
                                //List<Location> tempLocations = locations.get(key);
                                List<Location> tempLocations = new ArrayList<Location>();
                                tempLocations.add(locations.get(key));
                                if (tempLocations != null) {
                                    LocationNameInstance lni = tempLocations.get(0).getLocationName()
                                            .getLocationNameInstances().get(0);
                                    iof.setLocations(tempLocations);
                                }
                            }

                            ItemInformation itemInformation = itemInformations.get(key);
                            itemInformation.setItemOptionalFields(iof);
                            if (dueDates != null) {
                                itemInformation.setDateDue(dueDates.get(key));
                            }
                            itemInformations.put(key, itemInformation);
                        }

                        holdingSet.setItemInformations(new ArrayList<ItemInformation>(itemInformations.values()));

                        itemCount = itemCount + itemIds.size();
                        log.debug("Item count: " + itemCount);

                        if (itemCount == MAX_ITEMS_TO_RETURN) {
                            // Set next item token
                            ItemToken itemToken = new ItemToken();
                            itemToken.setBibliographicId(itemAgencyId + "_" + id);
                            itemToken.setHoldingsId(holdingId);
                            itemToken.setItemId(itemIds.get(itemIds.size() - 1));
                            int newToken = random.nextInt();
                            itemToken.setNextToken(Integer.toString(newToken));
                            tokens.put(Integer.toString(newToken), itemToken);

                            luisResponseData.setNextItemToken(Integer.toString(newToken));

                            reachedMaxItemCount = true;

                            log.info("Adding new holding set");
                            holdingSets.add(holdingSet);

                            break;
                        }

                        // No Item Records, but we still want to return some Holdings info.
                    } else {
                        itemCount = itemCount + 1;
                        log.debug("Item count: " + itemCount);

                        Map<String, ItemInformation> itemInformations = new HashMap<String, ItemInformation>();
                        ItemInformation itemInformation = new ItemInformation();
                        ItemId item = new ItemId();
                        item.setItemIdentifierValue("");
                        item.setAgencyId(new AgencyId(itemAgencyId));

                        itemInformation.setItemId(item);

                        /*** plug in item-like information for the non-item ***/
                        ItemOptionalFields iof2 = new ItemOptionalFields();
                        // Set location
                        Location location = null;
                        if (initData.getLocationDesired()) {
                            location = getLocationForHoldingDocFromXml(holdingId, holdingsDocFromXml);
                            if (location != null) {
                                List<Location> tempLocations2 = new ArrayList<Location>();
                                tempLocations2.add(location);
                                iof2.setLocations(tempLocations2);
                            }
                        }
                        itemInformation.setItemOptionalFields(iof2);
                        itemInformations.put("N/A", itemInformation);
                        holdingSet.setItemInformations(new ArrayList<ItemInformation>(itemInformations.values()));

                        if (itemCount == MAX_ITEMS_TO_RETURN) {
                            // Set next item token
                            ItemToken itemToken = new ItemToken();
                            itemToken.setBibliographicId(itemAgencyId + "_" + id);
                            itemToken.setHoldingsId(holdingId);
                            itemToken.setItemId("");
                            int newToken = random.nextInt();
                            itemToken.setNextToken(Integer.toString(newToken));
                            tokens.put(Integer.toString(newToken), itemToken);

                            luisResponseData.setNextItemToken(Integer.toString(newToken));

                            reachedMaxItemCount = true;

                            log.info("Adding new holding set");
                            holdingSets.add(holdingSet);

                            break;
                        }
                    }

                    log.info("Adding new holding set");
                    holdingSets.add(holdingSet);
                }

                if (holdingIds.size() != 0) {
                    bibInformation.setHoldingsSets(holdingSets);
                }

                bibInformations.add(bibInformation);

                if (reachedMaxItemCount) {
                    break;
                }

            } catch (ILSException e) {
                Problem p = new Problem();
                p.setProblemType(new ProblemType("Processing error"));
                p.setProblemDetail(e.getMessage());
                problems.add(p);
                luisResponseData.setProblems(problems);
            }
        }

        Date eService = new Date();
        log.debug("LUIS Service time log : " + (eService.getTime() - sService.getTime()) + "  "
                + ((eService.getTime() - sService.getTime()) / 1000) + " sec");

        luisResponseData.setBibInformations(bibInformations);

        return luisResponseData;
    }

    private boolean checkValidAgencyId(String itemAgencyId) {
        String ubid = (String) voyagerConfig.getProperty(itemAgencyId);
        if (ubid != null) {
            return true;
        } else {
            return false;
        }
    }

    private ElectronicResource getElectronicResource(Document doc) throws ILSException {
        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Namespace slimNs = Namespace.getNamespace("slim", "http://www.loc.gov/MARC21/slim");
        XPath xpath;
        try {
            xpath = XPath.newInstance("//slim:datafield[@tag='856']/slim:subfield[@code='u']");
            xpath.addNamespace(serNs);
            xpath.addNamespace(holNs);
            xpath.addNamespace(slimNs);
            Element url = (Element) xpath.selectSingleNode(doc);
            if (url != null) {
                log.debug("Found url " + url.getTextTrim());
                ElectronicResource electronicResource = new ElectronicResource();
                electronicResource.setReferenceToResource(url.getTextTrim());
                return electronicResource;
            } else {
                log.debug("Did not find url");
            }
        } catch (JDOMException e) {
            throw new ILSException(e);
        }

        return null;
    }

    private boolean doesRecordExist(Document holdingsDocFromXml) throws ILSException {
        Namespace ns = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        XPath xpath;
        try {
            xpath = XPath.newInstance("/ser:voyagerServiceData/ser:messages/ser:message");
            xpath.addNamespace(ns);
            Element message = (Element) xpath.selectSingleNode(holdingsDocFromXml);
            if (message != null && message.getText().startsWith("Could not retrieve bib record")) {
                log.debug("The message text is: " + message.getText());
                log.debug("Could not retrieve bib record");
                return false;
            } else {
                return true;
            }
        } catch (JDOMException e) {
            throw new ILSException(e);
        }
    }

    private int getIndexOfBibId(List<BibliographicId> bibIds, String bibId) {
        for (int i = 0; i < bibIds.size(); i++) {
            String id = bibIds.get(i).getBibliographicRecordId().getBibliographicRecordIdentifier();
            String itemAgencyId = bibIds.get(i).getBibliographicRecordId().getAgencyId().getValue();
            if (bibId.equalsIgnoreCase(itemAgencyId + "_" + id)) {
                return i;
            }
        }
        return -1;
    }

    private List<String> getItemIdSubset(List<String> itemIds, int itemCount) {

        int numOfitemIdsToProcess = MAX_ITEMS_TO_RETURN - itemCount;

        return itemIds.subList(0, numOfitemIdsToProcess);
    }

    private Document getHoldingRecordsFromRestful(BibliographicId bibliographicId) {
        String itemAgencyId;
        String host;
        String bibId = bibliographicId.getBibliographicRecordId().getBibliographicRecordIdentifier();

        if (bibliographicId.getBibliographicRecordId().getAgencyId() != null) {
            itemAgencyId = bibliographicId.getBibliographicRecordId().getAgencyId().getValue();
        } else {
            itemAgencyId = (String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_ILS_DEFAULT_AGENCY);
        }

        boolean consortialUse = Boolean
                .parseBoolean((String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_CONSORTIUM));
        if (consortialUse) {
            host = voyagerSvcMgr.getUrlFromAgencyId(itemAgencyId);
        } else {
            host = (String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_VOYAGER_WEB_SERVICE_URL);
        }

        String webServicesUrl = host + "/vxws/record/" + bibId + "/holdings?view=items";
        Document doc = voyagerSvcMgr.getWebServicesDoc(webServicesUrl);

        return doc;
    }

    private List<String> getHoldingIdsFromHoldingDoc(Document doc) throws ILSException {
        List<String> holdingIds = new ArrayList<String>();

        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Namespace mfhdNs = Namespace.getNamespace("mfhd", "http://www.endinfosys.com/Voyager/mfhd");
        try {
            XPath xpath = XPath.newInstance("//mfhd:mfhdRecord");
            xpath.addNamespace(holNs);
            xpath.addNamespace(serNs);
            xpath.addNamespace(mfhdNs);
            List<Element> mfhdElements = xpath.selectNodes(doc);
            for (Element mfhdElement : mfhdElements) {
                holdingIds.add(mfhdElement.getAttributeValue("mfhdId"));
                log.debug("Found mfhd Id " + mfhdElement.getAttributeValue("mfhdId"));
            }
        } catch (JDOMException e) {
            throw new ILSException(e);
        }
        return holdingIds;
    }

    private Map<String, GregorianCalendar> getDueDateForItemIds(List<String> itemIds, Document doc,
            Map<String, String> copyNumbers, String mfhdId) {

        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Namespace mfhdNs = Namespace.getNamespace("mfhd", "http://www.endinfosys.com/Voyager/mfhd");
        Namespace itemNs = Namespace.getNamespace("item", "http://www.endinfosys.com/Voyager/item");

        List<Element> mfhdElements;
        List<Element> itemRecords;
        List<Element> itemData;
        Element itemCollection;
        Element mfhdCollection;

        String copyNumber = null;
        String statusDate = null;

        Map<String, GregorianCalendar> dueDates = new HashMap<String, GregorianCalendar>();

        try {
            List<Element> list = doc.getRootElement().getChild("serviceData", serNs)
                    .getChild("holdingsRecord", holNs).getChildren();
            mfhdCollection = doc.getRootElement().getChild("serviceData", serNs).getChild("holdingsRecord", holNs)
                    .getChild("mfhdCollection", holNs);
            mfhdElements = mfhdCollection.getChildren("mfhdRecord", mfhdNs);

            for (Element mfhdRecord : mfhdElements) {
                if (!mfhdRecord.getAttributeValue("mfhdId").equalsIgnoreCase(mfhdId))
                    continue;

                if (mfhdRecord.getChild("itemCollection", mfhdNs) != null) {
                    itemCollection = mfhdRecord.getChild("itemCollection", mfhdNs);
                    itemRecords = itemCollection.getChildren("itemRecord", itemNs);
                    for (Element itemRecord : itemRecords) {
                        itemData = itemRecord.getChildren("itemData", itemNs);
                        boolean foundCopy = false;
                        for (Element item : itemData) {
                            if (item.getAttributeValue("name").equalsIgnoreCase("copyNumber")) {
                                copyNumber = item.getTextTrim();
                                if (copyNumbers.containsKey(copyNumber)) {
                                    log.info("Found match between copy numbers");
                                    foundCopy = true;
                                }
                            }
                            if (item.getAttributeValue("name").equalsIgnoreCase("statusDate")) {
                                statusDate = item.getTextTrim();
                            }
                        }
                        if (foundCopy) {
                            if (statusDate != null && !"".equals(statusDate)) {
                                log.debug("Found statusDate: " + statusDate);
                                String[] dateComponents = statusDate.substring(0, 10).split("-");
                                String[] timeComponents = statusDate.substring(11).split(":");

                                GregorianCalendar gc = new GregorianCalendar(Integer.parseInt(dateComponents[0]),
                                        //GC months start at 0 so subtract 1
                                        Integer.parseInt(dateComponents[1]) - 1,
                                        Integer.parseInt(dateComponents[2]), Integer.parseInt(timeComponents[0]),
                                        Integer.parseInt(timeComponents[1]), Integer.parseInt(timeComponents[2]));

                                dueDates.put(copyNumbers.get(copyNumber), gc);
                                foundCopy = false;
                            } else {
                                log.debug("Warning: statusDate is null");
                                foundCopy = false;
                            }
                        }
                    }
                }
            }
        } catch (NullPointerException e) {
            log.debug("No due dates found");
            return null;
        }
        return dueDates;
    }

    private ElectronicResource getElectronicResourceForHoldingId(String mfhdId, Document doc) throws ILSException {

        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Namespace slimNs = Namespace.getNamespace("slim", "http://www.loc.gov/MARC21/slim");
        ElectronicResource electronicResource = new ElectronicResource();

        try {
            XPath xpath = XPath.newInstance("//slim:datafield[@tag='856']/slim:subfield[@code='u']");
            xpath.addNamespace(holNs);
            xpath.addNamespace(serNs);
            xpath.addNamespace(slimNs);
            Element urlElement = (Element) xpath.selectSingleNode(doc);
            String href = urlElement.getTextTrim();
            log.debug("Found eResource url " + href);
            electronicResource.setReferenceToResource(href);
        } catch (JDOMException e) {
            log.error("Error processing document for eResource");
            throw new ILSException(e);
        } catch (NullPointerException e) {
            log.debug("eResource not found");
            return null;
        }

        return electronicResource;
    }

    private List<String> getItemIdsFromHoldingDoc(String holdingId, Document doc) throws ILSException {
        List<Element> items, holdings = null;
        List<String> itemIds = new ArrayList<String>();
        String itemId, holdingIdFound;
        String href;
        int index;
        try {
            XPath xpath = XPath.newInstance("/response/holdings/institution/holding");
            holdings = xpath.selectNodes(doc);
            for (Element holding : holdings) {
                href = holding.getAttributeValue("href");
                index = href.lastIndexOf("/");
                holdingIdFound = href.substring(index).substring(1);
                if (holdingIdFound.equalsIgnoreCase(holdingId)) {
                    items = holding.getChildren("item");
                    for (Element item : items) {
                        href = item.getAttributeValue("href");
                        index = href.lastIndexOf("/");
                        itemId = href.substring(index).substring(1);
                        itemIds.add(itemId);
                    }
                }
            }
        } catch (JDOMException e) {
            log.error("Error processing document for eResource");
            throw new ILSException(e);
        }
        return itemIds;
    }

    private String getCallNumberForHoldingDoc(String holdingId, Document doc) throws ILSException {
        List<Element> holdings = null;
        Element itemData;
        String callNumber = null;
        String href, holdingIdFound;
        int index;
        try {
            XPath xpath = XPath.newInstance("/response/holdings/institution/holding");
            holdings = xpath.selectNodes(doc);
            for (Element holding : holdings) {
                href = holding.getAttributeValue("href");
                index = href.lastIndexOf("/");
                holdingIdFound = href.substring(index).substring(1);
                if (holdingIdFound.equalsIgnoreCase(holdingId)) {
                    xpath = XPath.newInstance("/response/holdings/institution/holding[@href='" + href
                            + "']/item/itemData[@name='callNumber']");
                    itemData = (Element) xpath.selectSingleNode(doc);
                    callNumber = itemData.getTextTrim();
                }
            }
        } catch (JDOMException e) {
            log.error("Error processing document in getCallNumberForHoldingDoc()");
            throw new ILSException(e);
        }
        return callNumber;
    }

    static public String getCallNumberForHoldingDocFromXml(String holdingId, Document doc) throws ILSException {
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace mhNs = Namespace.getNamespace("mfhd", "http://www.endinfosys.com/Voyager/mfhd");
        String callNumber = null;

        try {
            XPath xpath = XPath.newInstance(
                    "/ser:voyagerServiceData/ser:serviceData/hol:holdingsRecord/hol:mfhdCollection/mfhd:mfhdRecord[@mfhdId='"
                            + holdingId + "']/mfhd:mfhdData[@name='callNumber']");
            xpath.addNamespace(serNs);
            xpath.addNamespace(holNs);
            xpath.addNamespace(mhNs);

            Element thisMFHD = (Element) xpath.selectSingleNode(doc);
            callNumber = thisMFHD.getTextTrim();

        } catch (JDOMException e) {
            log.error("Error processing document in getCallNumberForHoldingDocFromXml()");
            throw new ILSException(e);
        }
        return callNumber;
    }

    static public Location getLocationForHoldingDocFromXml(String holdingId, Document doc) throws ILSException {
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace mhNs = Namespace.getNamespace("mfhd", "http://www.endinfosys.com/Voyager/mfhd");
        Location location = null;

        try {
            XPath xpath = XPath.newInstance(
                    "/ser:voyagerServiceData/ser:serviceData/hol:holdingsRecord/hol:mfhdCollection/mfhd:mfhdRecord[@mfhdId='"
                            + holdingId + "']/mfhd:mfhdData[@name='locationDisplayName']");
            xpath.addNamespace(serNs);
            xpath.addNamespace(holNs);
            xpath.addNamespace(mhNs);

            Element thisMFHD = (Element) xpath.selectSingleNode(doc);
            String locationNameStr = thisMFHD.getTextTrim();

            LocationNameInstance locationNameInstance = new LocationNameInstance();
            locationNameInstance.setLocationNameValue(locationNameStr);
            //temporarily set to 1.
            locationNameInstance.setLocationNameLevel(new BigDecimal("1"));
            List<LocationNameInstance> locationNameInstances = new ArrayList<LocationNameInstance>();
            locationNameInstances.add(locationNameInstance);
            LocationName locationName = new LocationName();
            locationName.setLocationNameInstances(locationNameInstances);
            location = new Location();
            location.setLocationName(locationName);
            location.setLocationType(Version1LocationType.TEMPORARY_LOCATION);

        } catch (JDOMException e) {
            log.error("Error processing document in getLocationForHoldingDocFromXml()");
            throw new ILSException(e);
        }
        return location;
    }

    private Location getPermanentLocationForHoldingDoc(String holdingId, Document doc) {
        List<Element> items, holdings = null;
        String permLocation = null;
        String href, holdingIdFound;
        Location location = null;
        int index;

        if (doc.getRootElement().getChild("holdings").getChild("institution").getChildren() != null) {
            holdings = doc.getRootElement().getChild("holdings").getChild("institution").getChildren("holding");
            for (Element holding : holdings) {
                href = holding.getAttributeValue("href");
                index = href.lastIndexOf("/");
                holdingIdFound = href.substring(index).substring(1);
                if (holdingIdFound.equalsIgnoreCase(holdingId)) {
                    items = holding.getChild("item").getChildren("itemData");
                    for (Element itemData : items) {
                        if (itemData.getAttribute("name") != null
                                && itemData.getAttributeValue("name").equalsIgnoreCase("permLocation")) {
                            permLocation = itemData.getTextTrim();
                            LocationNameInstance locationNameInstance = new LocationNameInstance();

                            locationNameInstance.setLocationNameValue(permLocation);
                            //TODO: more to come from requirement for level
                            //temporarily set to 1.
                            locationNameInstance.setLocationNameLevel(new BigDecimal("1"));

                            List<LocationNameInstance> locationNameInstances = new ArrayList<LocationNameInstance>();
                            locationNameInstances.add(locationNameInstance);

                            LocationName locationName = new LocationName();
                            locationName.setLocationNameInstances(locationNameInstances);

                            location = new Location();
                            location.setLocationName(locationName);
                            location.setLocationType(Version1LocationType.PERMANENT_LOCATION);
                        }
                    }
                }
            }
        }
        return location;
    }

    private Map<String, Location> getLocationForItemIds(List<String> itemIds, Document doc) {

        List<Element> items, itemData, holdings = null;
        String itemId, href, tempLocation;
        Map<String, Location> tempLocations = new HashMap<String, Location>();

        int index;
        if (doc.getRootElement().getChild("holdings").getChild("institution").getChildren() != null) {
            holdings = doc.getRootElement().getChild("holdings").getChild("institution").getChildren("holding");
            for (Element holding : holdings) {
                items = holding.getChildren("item");
                for (Element item : items) {

                    //List<Location> locations = new ArrayList<Location>();
                    //Location location = new Location();
                    Location location = null;
                    href = item.getAttributeValue("href");
                    index = href.lastIndexOf("/");
                    itemId = href.substring(index).substring(1);
                    if (itemIds.contains(itemId)) {
                        itemData = item.getChildren("itemData");
                        for (Element id : itemData) {
                            if (id.getAttribute("name") != null) {
                                if (id.getAttributeValue("name").equalsIgnoreCase("tempLocation")) {
                                    tempLocation = id.getTextTrim();
                                    if (tempLocation.equalsIgnoreCase("")) {
                                        log.info("Found empty temp location");
                                        continue;
                                    }
                                    LocationNameInstance locationNameInstance = new LocationNameInstance();

                                    locationNameInstance.setLocationNameValue(StringUtils.trim(tempLocation));
                                    locationNameInstance.setLocationNameLevel(new BigDecimal("1"));

                                    List<LocationNameInstance> locationNameInstances = new ArrayList<LocationNameInstance>();
                                    locationNameInstances.add(locationNameInstance);

                                    LocationName locationName = new LocationName();
                                    locationName.setLocationNameInstances(locationNameInstances);

                                    location = new Location();
                                    location.setLocationName(locationName);
                                    location.setLocationType(Version1LocationType.TEMPORARY_LOCATION);
                                }
                                if (id.getAttributeValue("name").equalsIgnoreCase("permLocation")) {
                                    tempLocation = id.getTextTrim();
                                    if (tempLocation.equalsIgnoreCase("")) {
                                        log.info("Found empty perm location");
                                        continue;
                                    }
                                    LocationNameInstance locationNameInstance = new LocationNameInstance();

                                    locationNameInstance.setLocationNameValue(StringUtils.trim(tempLocation));
                                    locationNameInstance.setLocationNameLevel(new BigDecimal("1"));

                                    List<LocationNameInstance> locationNameInstances = new ArrayList<LocationNameInstance>();
                                    locationNameInstances.add(locationNameInstance);

                                    LocationName locationName = new LocationName();
                                    locationName.setLocationNameInstances(locationNameInstances);
                                    if (location == null) {
                                        location = new Location();
                                        location.setLocationName(locationName);
                                        location.setLocationType(Version1LocationType.PERMANENT_LOCATION);
                                    }
                                }

                            }
                        }
                    }
                    //if (locations.size() > 0)
                    tempLocations.put(itemId, location);

                }
            }
            return tempLocations;
        } else {
            log.error("No children of institution found");
            return null;
        }
    }

    private Map<String, String> getCirculationStatusForItemIds(List<String> itemIds, Document doc) {

        Map<String, String> statuses = new HashMap<String, String>();
        List<Element> items, itemData, holdings = null;
        String itemId, href, status;

        int index;
        if (doc.getRootElement().getChild("holdings").getChild("institution").getChildren() != null) {
            holdings = doc.getRootElement().getChild("holdings").getChild("institution").getChildren("holding");
            for (Element holding : holdings) {
                items = holding.getChildren("item");
                for (Element item : items) {
                    href = item.getAttributeValue("href");
                    index = href.lastIndexOf("/");
                    itemId = href.substring(index).substring(1);
                    if (itemIds.contains(itemId)) {
                        itemData = item.getChildren("itemData");
                        for (Element id : itemData) {
                            if (id.getAttribute("name") != null) {
                                if (id.getAttributeValue("name").equalsIgnoreCase("itemStatus")) {
                                    status = id.getTextTrim();
                                    log.debug("Found status: " + status);
                                    if (status.endsWith("+")) {
                                        status = status.substring(0, status.length() - 1);
                                    }
                                    if (status.contains("Not Charged")) {
                                        status = "Not Charged";
                                    } else if (status.contains("Charged")) {
                                        status = "Charged";
                                    } else if (status.contains("Missing")) {
                                        status = "Missing";
                                    } else if (status.contains("Lost")) {
                                        status = "Missing";
                                    } /*else {
                                      String arr[] = status.split(" ", 2);
                                      status = arr[0];
                                      } */
                                    statuses.put(itemId, status);
                                }
                            }
                        }
                    }
                }
            }
            return statuses;
        } else {
            log.error("No children of institution found");
            return null;
        }
    }

    private Map<String, ItemDescription> getItemDescriptionForItemIds(List<String> itemIds, Document doc) {

        Map<String, ItemDescription> itemDescriptions = new HashMap<String, ItemDescription>();

        List<Element> items, itemData, holdings = null;
        String itemId, href, status;

        int index;
        if (doc.getRootElement().getChild("holdings").getChild("institution").getChildren() != null) {
            holdings = doc.getRootElement().getChild("holdings").getChild("institution").getChildren("holding");
            for (Element holding : holdings) {
                items = holding.getChildren("item");
                for (Element item : items) {
                    href = item.getAttributeValue("href");
                    index = href.lastIndexOf("/");
                    itemId = href.substring(index).substring(1);
                    if (itemIds.contains(itemId)) {
                        itemData = item.getChildren("itemData");
                        ItemDescription itemDescription = new ItemDescription();
                        String infoString = "";
                        String callNumber = "";
                        String copy = "";

                        for (Element id : itemData) {
                            if (id.getAttribute("name") != null) {
                                if (id.getAttributeValue("name").equalsIgnoreCase("enumeration")) {
                                    infoString += id.getTextTrim();
                                } else if (id.getAttributeValue("name").equalsIgnoreCase("chron")) {
                                    infoString += id.getTextTrim();
                                } else if (id.getAttributeValue("name").equalsIgnoreCase("year")) {
                                    infoString += id.getTextTrim();
                                } else if (id.getAttributeValue("name").equalsIgnoreCase("callNumber")) {
                                    callNumber = id.getTextTrim();
                                } else if (id.getAttributeValue("name").equalsIgnoreCase("copy")) {
                                    copy = id.getTextTrim();
                                }
                            }
                        }

                        if (infoString != null && infoString.trim().length() > 0) {
                            HoldingsInformation holdingsInformation = new HoldingsInformation();
                            holdingsInformation.setUnstructuredHoldingsData(infoString);
                            itemDescription.setHoldingsInformation(holdingsInformation);
                        }
                        if (callNumber.length() > 0) {
                            itemDescription.setCallNumber(callNumber);
                        }
                        if (copy.length() > 0) {
                            itemDescription.setCopyNumber(copy);
                        }
                        itemDescriptions.put(itemId, itemDescription);
                    }
                }
            }
            log.info("Returning item descriptions: " + itemDescriptions);
            return itemDescriptions;
        } else {
            log.error("No children of institution found");
            return null;
        }
    }

    private Document getHoldingRecordsFromXml(BibliographicId bibliographicId) {

        String bibId = bibliographicId.getBibliographicRecordId().getBibliographicRecordIdentifier();
        String itemAgencyId;
        String host;

        if (bibliographicId.getBibliographicRecordId().getAgencyId() != null) {
            itemAgencyId = bibliographicId.getBibliographicRecordId().getAgencyId().getValue();
        } else {
            itemAgencyId = (String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_ILS_DEFAULT_AGENCY);
        }

        boolean consortialUse = Boolean
                .parseBoolean((String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_CONSORTIUM));
        if (consortialUse) {
            host = voyagerSvcMgr.getUrlFromAgencyId(itemAgencyId);
        } else {
            host = (String) voyagerConfig.getProperty(VoyagerConstants.CONFIG_VOYAGER_WEB_SERVICE_URL);
        }

        String url = host + "/vxws/GetHoldingsService?bibId=" + bibId;

        Document doc = voyagerSvcMgr.getWebServicesDoc(url);

        return doc;
    }

    private BibliographicDescription getBibliographicDescriptionForBibId(Document doc) {

        Namespace slimNs = Namespace.getNamespace("slim", "http://www.loc.gov/MARC21/slim");
        Namespace holNs = Namespace.getNamespace("hol", "http://www.endinfosys.com/Voyager/holdings");
        Namespace serNs = Namespace.getNamespace("ser", "http://www.endinfosys.com/Voyager/serviceParameters");
        Element marcElement;
        List<Element> dataFieldElements = null;
        List<Element> subFieldElements = null;

        String isbn = null, issn = null, title = null, author = null;
        String titleA = "", titleB = "", titleC = "", titleF = "", titleG = "";
        String titleH = "", titleK = "", titleN = "", titleP = "", titleS = "";

        if (doc.getRootElement().getChild("serviceData", serNs).getChild("holdingsRecord", holNs)
                .getChild("bibRecord", holNs).getChild("marcRecord", holNs) != null) {
            marcElement = doc.getRootElement().getChild("serviceData", serNs).getChild("holdingsRecord", holNs)
                    .getChild("bibRecord", holNs).getChild("marcRecord", holNs);
            dataFieldElements = marcElement.getChildren("datafield", slimNs);
            for (Element eDf : dataFieldElements) {
                if (eDf.getAttributeValue("tag") != null && eDf.getAttributeValue("tag").equalsIgnoreCase("245")) {
                    subFieldElements = eDf.getChildren("subfield", slimNs);
                    for (Element eSf : subFieldElements) {
                        if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("a"))
                            titleA = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("b"))
                            titleB = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("c"))
                            titleC = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("f"))
                            titleF = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("g"))
                            titleG = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("h"))
                            titleH = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("k"))
                            titleK = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("n"))
                            titleN = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("p"))
                            titleP = eSf.getTextTrim();
                        else if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("s"))
                            titleS = eSf.getTextTrim();
                    }
                    title = titleA + titleB + titleC + titleF + titleG + titleH + titleK + titleN + titleP + titleS;
                }
                if (eDf.getAttributeValue("tag") != null && eDf.getAttributeValue("tag").equalsIgnoreCase("020")) {
                    subFieldElements = eDf.getChildren("subfield", slimNs);
                    for (Element eSf : subFieldElements) {
                        if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("a")) {
                            isbn = eSf.getTextTrim();
                            log.info("Found ISBN: " + isbn);
                        } else {
                            log.error("020 code a not found");
                        }
                    }
                }
                if (eDf.getAttributeValue("tag") != null && eDf.getAttributeValue("tag").equalsIgnoreCase("022")) {
                    subFieldElements = eDf.getChildren("subfield", slimNs);
                    for (Element eSf : subFieldElements) {
                        if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("a")) {
                            issn = eSf.getTextTrim();
                            log.info("Found ISSN: " + issn);
                        } else {
                            log.error("022 code a not found");
                        }
                    }
                }
                if (eDf.getAttributeValue("tag") != null && eDf.getAttributeValue("tag").equalsIgnoreCase("100")) {
                    subFieldElements = eDf.getChildren("subfield", slimNs);
                    for (Element eSf : subFieldElements) {
                        if (eSf.getAttributeValue("code") != null
                                && eSf.getAttributeValue("code").equalsIgnoreCase("a")) {
                            author = eSf.getTextTrim();
                            log.info("Found author: " + author);
                        } else {
                            log.error("100 code a not found");
                        }
                    }
                }
            }
        } else {
            log.error("marc element not found");
        }

        BibliographicDescription bibliographicDescription = new BibliographicDescription();
        boolean hasBibDesc = false;

        List<BibliographicItemId> bibliographicItemIds = new ArrayList<BibliographicItemId>();

        if (author != null && author.length() > 0) {
            bibliographicDescription.setAuthor(author);
            hasBibDesc = true;
        }

        // Set bib item id
        if (isbn != null && isbn.length() > 0) {
            BibliographicItemId bibliographicItemId = new BibliographicItemId();
            bibliographicItemId.setBibliographicItemIdentifier(isbn);
            BibliographicItemIdentifierCode code = new BibliographicItemIdentifierCode(
                    Version1BibliographicItemIdentifierCode.VERSION_1_BIBLIOGRAPHIC_ITEM_IDENTIFIER_CODE, "ISBN");
            bibliographicItemId.setBibliographicItemIdentifierCode(code);
            bibliographicItemIds.add(bibliographicItemId);
            bibliographicDescription.setBibliographicItemIds(bibliographicItemIds);
            hasBibDesc = true;
        } else if (issn != null && issn.length() > 0) {
            BibliographicItemId bibliographicItemId = new BibliographicItemId();
            bibliographicItemId.setBibliographicItemIdentifier(issn);
            BibliographicItemIdentifierCode code = new BibliographicItemIdentifierCode(
                    Version1BibliographicItemIdentifierCode.VERSION_1_BIBLIOGRAPHIC_ITEM_IDENTIFIER_CODE, "ISSN");
            bibliographicItemId.setBibliographicItemIdentifierCode(code);
            bibliographicItemIds.add(bibliographicItemId);
            bibliographicDescription.setBibliographicItemIds(bibliographicItemIds);
            hasBibDesc = true;
        }

        if (title != null && title.length() > 0) {
            bibliographicDescription.setTitle(title);
            hasBibDesc = true;
        }

        if (!hasBibDesc) {
            return null;
        }

        return bibliographicDescription;
    }

}