org.books.integration.AmazonCatalogBean.java Source code

Java tutorial

Introduction

Here is the source code for org.books.integration.AmazonCatalogBean.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.books.integration;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import org.apache.commons.lang3.StringUtils;
import org.books.application.exception.BookNotFoundException;
import org.books.base.Util;
import org.books.integration.amazon.Errors.Error;
import org.books.integration.amazon.Errors;
import org.books.integration.amazon.Item;
import org.books.integration.amazon.ItemAttributes;
import org.books.integration.amazon.ItemLookup;
import org.books.integration.amazon.ItemLookupRequest;
import org.books.integration.amazon.ItemLookupResponse;
import org.books.integration.amazon.Items;
import org.books.integration.amazon.ItemSearch;
import org.books.integration.amazon.ItemSearchRequest;
import org.books.integration.amazon.ItemSearchResponse;
import org.books.integration.amazon.OperationRequest;
import org.books.integration.amazon.Price;
import org.books.integration.amazon.Request;
import org.books.persistence.dto.BookInfo;
import org.books.persistence.entity.Book;
import org.books.persistence.enumeration.BookBinding;

/**
 * Catalog service bean to provide access to books
 * 
 * @author me
 */
@Stateless(name = "AmazonCatalog", mappedName = "AmazonCatalog")
public class AmazonCatalogBean implements AmazonCatalog {

    protected final static Logger LOGGER = Logger.getLogger(AmazonCatalogBean.class.getName());
    private static final Pattern YEAR_PATTERN = Pattern.compile("(\\d\\d\\d\\d)");
    private static final int MAX_ITEM_PAGE = 10;
    private static final int PAGE_PER_REQUEST = 2;

    @EJB
    private AmazonRequestsExecuter amazon;

    @Override
    public Book findBook(String isbn) throws BookNotFoundException {
        Book result = null;

        ItemLookup itemLookup = new ItemLookup();
        ItemLookupRequest lookupRequest = new ItemLookupRequest();
        lookupRequest.setSearchIndex("Books");
        lookupRequest.setIdType("ISBN");
        lookupRequest.getResponseGroup().add("ItemAttributes");
        lookupRequest.getItemId().add(isbn);
        itemLookup.getRequest().add(lookupRequest);

        ItemLookupResponse response = amazon.itemLookup(itemLookup);

        OperationRequest operationRequest = response.getOperationRequest();
        for (Items items : response.getItems()) {
            Request itemRequest = items.getRequest();

            if (!itemRequest.getIsValid().equalsIgnoreCase("true")) {
                continue;
            }

            for (Item item : items.getItem()) {
                ItemAttributes itemAttributes = item.getItemAttributes();

                String itemIsbn = extractIsbn(itemAttributes, isbn);

                if (itemIsbn == null) {
                    continue;
                }
                if (itemAttributes != null) {
                    result = toBook(itemAttributes, itemIsbn);
                }
                if (result != null) {
                    break;
                }
            }

            if (result != null) {
                break;
            }
        }

        if (result == null) {
            throw new BookNotFoundException();
        }
        return result;
    }

    @Override
    public List<BookInfo> searchBooks(String keywords) {

        if (Util.isNullOrEmpty(keywords)) {
            throw new IllegalArgumentException();
        }

        // ItemSearch: for Books amazon offers following local information for US
        // search index: Books
        // root browse node: 1000 
        // sort value: relevancerank, salesrank, reviewrank, pricerank, inverse-pricerank, daterank, titlerank, -titlerank, -unit-sales, price, -price, -publication_date, 
        // item search parameters: Author, Availability, ItemPage, Keywords, MaximumPrice, MerchantId, MinPercentageOff, MinimumPrice, Power, Publisher, Sort, Title
        // Valid item identifier in US: ASIN, EAN, EISBN, ISBN, SKU, UPC
        // Only the US have ISBN!!!!!
        List<BookInfo> bookInfoList = new ArrayList<BookInfo>();
        int itemPage = 0;
        int maxItemPage = MAX_ITEM_PAGE;
        int totalItemPage = maxItemPage; // at the start we expect to get at least one page

        // Loop for to make the item page requests
        while (true) {

            itemPage++;

            if (itemPage > totalItemPage) {
                LOGGER.log(Level.INFO, "searchBooks: all item page are read. {0} book found", bookInfoList.size());
                break;
            }
            if (itemPage > maxItemPage) {
                LOGGER.log(Level.INFO, "searchBooks: max item page reached. {0} book found", bookInfoList.size());
                break;
            }

            ItemSearch itemElement = new ItemSearch();

            for (int pages = 0; pages < PAGE_PER_REQUEST; pages++) {
                if (pages > 0) {
                    // only increate if it is not the first page we add to the request
                    itemPage++;
                }
                if (itemPage > totalItemPage) {
                    // don't add aditional page because we would not get more result
                    continue;
                }
                if (itemPage > maxItemPage) {
                    // don't add aditional page because more then index 10 is not allowed
                    continue;
                }

                ItemSearchRequest itemRequest = new ItemSearchRequest();

                itemRequest.setSearchIndex("Books"); // search only books
                itemRequest.setAvailability("Available"); // IMPORTANT: only available books !!!
                itemRequest.getResponseGroup().add("ItemAttributes"); // get as much as possible attributes
                itemRequest.setSort("salesrank"); // IMPORTANT: order by sales rank
                itemRequest.setKeywords(keywords); // keywords which the resulting books shall have
                itemRequest.setItemPage(BigInteger.valueOf(itemPage));

                itemElement.getRequest().add(itemRequest);
            }

            ItemSearchResponse response = null;
            try {
                response = amazon.itemSearch(itemElement);
            } catch (Exception ex) {
                LOGGER.log(Level.SEVERE, "searchBooks: catched a AmazonThrottlingException: {0}", ex.getMessage());
                continue;
            }

            if (response == null) {
                LOGGER.log(Level.SEVERE, "searchBooks: responce is null");
                break;
            }
            if (!checkOperationRequest("searchBooks", response.getOperationRequest())) {
                // logs made in the method
                break;
            }

            for (Items responseItem : response.getItems()) {

                Request request = responseItem.getRequest();
                if (!request.getIsValid().equalsIgnoreCase("true")) {
                    LOGGER.log(Level.SEVERE, "searchBooks: result is invalid");
                    if (request.getErrors() != null) {
                        reportError("searchBooks", request.getErrors().getError());
                    }
                    continue;
                }

                totalItemPage = responseItem.getTotalPages().intValue();
                for (Item bookItem : responseItem.getItem()) {

                    ItemAttributes itemAttributes = bookItem.getItemAttributes();
                    Book book = toBook(itemAttributes, null);

                    if (book != null) {

                        BookInfo bookInfo = new BookInfo(book.getIsbn(), book.getTitle(), book.getPrice());
                        bookInfoList.add(bookInfo);
                    }
                }
            }
        }
        return bookInfoList;
    }

    private Book toBook(ItemAttributes itemAttributes, String isbn) {

        if (isbn == null) {
            isbn = extractIsbn(itemAttributes, null);
        }
        if (itemAttributes == null || isbn == null) {
            return null;
        }

        Book book = book = new Book(isbn);
        try {
            String title = itemAttributes.getTitle();
            book.setTitle(title);

            String authors = StringUtils.join(itemAttributes.getAuthor(), "; ");
            book.setAuthors(authors);

            String publisher = itemAttributes.getPublisher();
            book.setPublisher(publisher);

            String publicationDate = itemAttributes.getPublicationDate();
            book.setPublicationYear(toYear(publicationDate));

            String binding = itemAttributes.getBinding();
            book.setBinding(toBinding(binding));

            BigInteger numberOfPages = itemAttributes.getNumberOfPages();
            book.setNumberOfPages(toInteger(numberOfPages));

            Price price = itemAttributes.getListPrice();
            book.setPrice(toPrice(price));

        } catch (Exception e) {
            LOGGER.log(Level.WARNING, "Failed convertion. Assume book is not complete: {0}", e.getMessage());
            book = null;
        }

        return book;
    }

    private Integer toYear(String publicationDate) {

        Matcher m = YEAR_PATTERN.matcher(publicationDate);
        if (m.find()) {
            return Integer.parseInt(m.group(0));
        }
        throw new IllegalArgumentException("unexpected date format:" + publicationDate);
    }

    private BookBinding toBinding(String binding) {
        try {
            return BookBinding.valueOf(binding.toUpperCase());
        } catch (IllegalArgumentException e) {
            return BookBinding.UNKNOWN;
        }
    }

    private Integer toInteger(BigInteger numberOfPages) {
        return numberOfPages.intValue();
    }

    private BigDecimal toPrice(Price price) {
        return new BigDecimal(price.getAmount(), 2);
    }

    private String extractIsbn(ItemAttributes itemAttributes, String matchingIsbn) {
        if (itemAttributes == null) {
            return null;
        }
        if (isMatching(itemAttributes.getISBN(), matchingIsbn)) {
            return itemAttributes.getISBN();
        }
        if (itemAttributes.getEANList() != null && itemAttributes.getEANList().getEANListElement() != null) {
            for (String ean : itemAttributes.getEANList().getEANListElement()) {
                if (isMatching(ean, matchingIsbn)) {
                    return ean;
                }
            }
        }
        return null;
    }

    private boolean isMatching(String value, String matchingString) {
        if (value == null) {
            return false;
        }
        if (matchingString == null) {
            return true;
        }
        return value.compareToIgnoreCase(matchingString) == 0;
    }

    private boolean checkOperationRequest(String methodName, OperationRequest operationRequest) {

        if (operationRequest == null) {
            LOGGER.log(Level.SEVERE, "{0}: operation request is null", methodName);
            return false;
        }
        // operationRequest.getArguments()
        Errors errors = operationRequest.getErrors();
        if (errors != null) {
            if (!errors.getError().isEmpty()) {
                reportError(methodName, errors.getError());
                return false;
            }
        }
        return true;
    }

    private void reportError(String methodName, List<Error> errorList) {

        LOGGER.log(Level.SEVERE, "{0}: response with errors", methodName);
        for (Error error : errorList) {
            LOGGER.log(Level.SEVERE, "{0}: - error({1}): {2}",
                    new Object[] { methodName, error.getCode(), error.getMessage() });
        }
    }

}