ndaussy
ndaussy

Reputation: 7

Error Iterable Implementation GWTP-Rest Jackson

I use GWT 2.6.1 with GWTP-Rest Api, when i compile, i get an error.

My class Page implements Iterable & Serializable. This class come from the spring framework but was adaptable to GWT (this class is use by an other project which works with RPC. The error comes from an other class "Sort" which implements the same Interface. See code below.

Error come from com.github.nmorel.gwtjackson.rebind.ObjectMapperGenerator which create an exception.

if I delete the Iterator implementation of OrderMobile, no error occure.

Error in console :

Creating deserializer for **.***.**.**.**.***.*****.Page
[INFO]                   [ERROR] Wrong number of argument for a java.lang.Iterable implementation

Code from Sort :

public class SortMobile implements Serializable, Iterable<OrderMobile>
{

    private static final long serialVersionUID = -8226494389482286795L;

    public static final Direction DEFAULT_DIRECTION = Direction.ASC;

    private List<OrderMobile> orders = new ArrayList<OrderMobile>();

    SortMobile() {

    }

    /**
     * Creates a new {@link Sort} instance using the given {@link Order}s.
     * 
     * @param orders must not be {@literal null}.
     */
    public SortMobile(final OrderMobile... orders) {
        this(Arrays.asList(orders));
    }

    /**
     * Creates a new {@link SortMobile} instance.
     * 
     * @param list must not be {@literal null} or contain {@literal null}.
     */
    public SortMobile(final List<OrderMobile> list) {

        if (null == list || list.isEmpty()) {
            throw new IllegalArgumentException("You have to provide at least one sort property to sort by!");
        }

        this.orders = list;
    }

    /**
     * Creates a new {@link SortMobile} instance. Order defaults to {@value Direction#ASC}.
     * 
     * @param properties must not be {@literal null} or contain {@literal null} or empty strings
     */
    public SortMobile(final String... properties) {
        this(DEFAULT_DIRECTION, properties);
    }

    /**
     * Creates a new {@link SortMobile} instance.
     * 
     * @param direction defaults to {@linke Sort#DEFAULT_DIRECTION} (for {@literal null} cases, too)
     * @param properties must not be {@literal null} or contain {@literal null} or empty strings
     */
    public SortMobile(final Direction direction, final String... properties) {
        this(direction, properties == null ? new ArrayList<String>() : Arrays.asList(properties));
    }

    /**
     * Creates a new {@link SortMobile} instance.
     * 
     * @param direction
     * @param properties
     */
    public SortMobile(final Direction direction, final List<String> properties) {

        if (properties == null || properties.isEmpty()) {
            throw new IllegalArgumentException("You have to provide at least one property to sort by!");
        }

        this.orders = new ArrayList<OrderMobile>(properties.size());

        for (String property : properties) {
            this.orders.add(new OrderMobile(direction, property));
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(final Object obj) {

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof SortMobile)) {
            return false;
        }

        SortMobile that = (SortMobile) obj;

        return this.orders.equals(that.orders);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {

        int result = 17;
        result = 31 * result + orders.hashCode();
        return result;
    }

    // GETTER SETTER
    public void setOrders(final List<OrderMobile> orders) {
        this.orders = orders;
    }

    public List<OrderMobile> getOrders() {
        return orders;
    }

    public static Direction getDefaultDirection() {
        return DEFAULT_DIRECTION;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return Arrays.toString(orders.toArray(new OrderMobile[orders.size()]));
    }

    // ### elements of iterator implementation

    @Override
    public Iterator<OrderMobile> iterator() {

        return this.orders.iterator();
    }

    /**
     * Returns a new {@link Sort} consisting of the {@link Order}s of the current {@link Sort} combined with the given
     * ones.
     * 
     * @param sort can be {@literal null}.
     * @return
     */
    public SortMobile and(final SortMobile sort) {

        if (sort == null) {
            return this;
        }

        ArrayList<OrderMobile> these = new ArrayList<OrderMobile>(this.orders);

        for (OrderMobile order : sort) {
            these.add(order);
        }

        return new SortMobile(these);
    }

    /**
     * Returns the order registered for the given property.
     * 
     * @param property
     * @return
     */
    public OrderMobile getOrderFor(final String property) {

        for (OrderMobile order : this) {
            if (order.getProperty().equals(property)) {
                return order;
            }
        }

        return null;
    }

}

OrderMobile is a simple dto :

public class OrderMobile implements Serializable {
    private static final long serialVersionUID = 1522511010900108987L;

    private static final Direction DEFAULT_DIRECTION = Direction.ASC;

    private Direction direction;
    private String property;

    OrderMobile() {
        super();
    }

    /**
     * Creates a new {@link OrderMobile} instance. if order is {@literal null} then order defaults to
     * {@link SortMobile#DEFAULT_DIRECTION}
     * 
     * @param direction can be {@literal null}, will default to {@link SortMobile#DEFAULT_DIRECTION}
     * @param property must not be {@literal null} or empty.
     */
    public OrderMobile(final Direction direction, final String property) {

        if (!hasText(property)) {
            throw new IllegalArgumentException("Property must not null or empty!");
        }

        this.direction = direction == null ? DEFAULT_DIRECTION : direction;
        this.property = property;
    }

    private boolean hasText(final String s) {
        return s != null && s.trim().length() > 0;
    }

    /**
     * Creates a new {@link OrderMobile} instance. Takes a single property. Direction defaults to
     * {@link SortMobile#DEFAULT_DIRECTION}.
     * 
     * @param property must not be {@literal null} or empty.
     */
    public OrderMobile(final String property) {
        this(DEFAULT_DIRECTION, property);
    }

    /**
     * Returns the order the property shall be sorted for.
     * 
     * @return
     */

    public Direction getDirection() {
        return direction;
    }

    public void setDirection(final Direction direction) {
        this.direction = direction;
    }

    /**
     * Returns the property to order for.
     * 
     * @return
     */

    public String getProperty() {
        return property;
    }

    public void setProperty(final String property) {
        this.property = property;
    }

    /**
     * Returns whether sorting for this property shall be ascending.
     * 
     * @return
     */
    public boolean isAscending() {
        return this.direction.equals(Direction.ASC);
    }

    /**
     * Returns a new {@link OrderMobile} with the given {@link OrderMobile}.
     * 
     * @param order
     * @return
     */
    public OrderMobile with(final Direction order) {
        return new OrderMobile(order, this.property);
    }

    /**
     * Returns a new {@link SortMobile} instance for the given properties.
     * 
     * @param properties
     * @return
     */
    public SortMobile withProperties(final String... properties) {
        return new SortMobile(this.direction, properties);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {

        int result = 17;

        result = 31 * result + direction.hashCode();
        result = 31 * result + property.hashCode();

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(final Object obj) {

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof OrderMobile)) {
            return false;
        }

        OrderMobile that = (OrderMobile) obj;

        return this.direction.equals(that.direction) && this.property.equals(that.property);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return property + ":" + direction;
    }  

}

3.Code of Page :

public class PageMobile<T> implements Iterable<T>, Serializable {

    private static final long serialVersionUID = 867755909294344406L;

    private List<T> content = new ArrayList<T>();
    private PageRequestMobile pageable;
    private long total;

    // for serialization
    PageMobile() {

    }

    /**
     * Constructor of {@code Page}.
     * 
     * @param content the content of this page, must not be {@literal null}.
     * @param pageable the paging information, can be {@literal null}.
     * @param total the total amount of items available
     */
    public PageMobile(final List<T> content, final PageRequestMobile pageable, final long total) {

        if (null == content) {
            throw new IllegalArgumentException("Content must not be null!");
        }

        this.content.addAll(content);
        this.total = total;
        this.pageable = pageable;
    }

    /**
     * Creates a new {@link PageMobile} with the given content. This will result in the created {@link PageMobile} being
     * identical
     * to the entire {@link List}.
     * 
     * @param content must not be {@literal null}.
     */
    public PageMobile(final List<T> content) {
        this(content, null, null == content ? 0 : content.size());
    }

    /**
     * Returns the number of the current page. Is always non-negative and less that {@code Page#getTotalPages()}.
     * 
     * @return the number of the current page
     */
    public int getNumber() {
        return pageable == null ? 0 : pageable.getPageNumber();
    }

    /**
     * Returns the size of the page.
     * 
     * @return the size of the page
     */
    public int getSize() {
        return pageable == null ? 0 : pageable.getPageSize();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#getTotalPages()
     */

    public int getTotalPages() {
        return getSize() == 0 ? 0 : (int) Math.ceil((double) total / (double) getSize());
    }

    /**
     * Returns the number of elements currently on this page.
     * 
     * @return the number of elements currently on this page
     */
    public int getNumberOfElements() {
        return content.size();
    }

    /**
     * Returns the total amount of elements.
     * 
     * @return the total amount of elements
     */
    public long getTotalElements() {
        return total;
    }

    /**
     * set the total amount of elements.
     * 
     * 
     */
    public void setTotalElements(final Long total) {
        this.total = total;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#hasPreviousPage()
     */

    public boolean hasPreviousPage() {
        return getNumber() > 0;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#isFirstPage()
     */

    public boolean isFirstPage() {
        return !hasPreviousPage();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#hasNextPage()
     */

    public boolean hasNextPage() {
        return (getNumber() + 1) * getSize() < total;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#isLastPage()
     */

    public boolean isLastPage() {
        return !hasNextPage();
    }

    /**
     * Returns the page content as {@link List}.
     * 
     * @return
     */
    public List<T> getContent() {
        return Collections.unmodifiableList(content);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#hasContent()
     */

    public boolean hasContent() {
        return !content.isEmpty();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.data.domain.Page#getSort()
     */
    // FIXME to the version in
    public SortMobile getSort() {
        return pageable == null ? null : pageable.getSort();
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */

    @Override
    public String toString() {

        String contentType = "UNKNOWN";

        if (content.size() > 0) {
            contentType = content.get(0).getClass().getName();
        }

        return "Page " + getNumber() + " of " + getTotalPages() + " containing " + contentType + " instances";
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */

    @Override
    public boolean equals(final Object obj) {

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof PageMobile<?>)) {
            return false;
        }

        PageMobile<?> that = (PageMobile<?>) obj;

        boolean totalEqual = this.total == that.total;
        boolean contentEqual = this.content.equals(that.content);
        boolean pageableEqual = this.pageable == null ? that.pageable == null : this.pageable.equals(that.pageable);

        return totalEqual && contentEqual && pageableEqual;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */

    @Override
    public int hashCode() {

        int result = 17;

        result = 31 * result + (int) (total ^ total >>> 32);
        result = 31 * result + (pageable == null ? 0 : pageable.hashCode());
        result = 31 * result + content.hashCode();

        return result;
    }

    /**
     * Adjust the page size, distributing items equally (+/- 1 item) on each page, given a maximum page size.
     * 
     * WARNING: this function reverse pages order.
     * TODO: delegate page order reversing to another function, to limit a single behavior for a given function.
     * 
     * @param page The page
     * @param request The associated page request
     * @param maxPageSize The maximum page size
     * @return The new ajusted page
     */
    public static <T> PageMobile<T> adjustPageSize(final PageMobile<T> page, final PageRequestMobile request, final int maxPageSize) {
        int totalElnts = (int) page.getTotalElements();
        int currentPageNumber = request.getPageNumber();
        if (totalElnts == 0) {
            List<T> newContent = new ArrayList<T>(0);
            PageRequestMobile newRequest = new PageRequestMobile(currentPageNumber, maxPageSize);
            return new PageMobile<T>(newContent, newRequest, totalElnts);
        }
        int nbPages = (int) Math.ceil((double) totalElnts / maxPageSize);
        int pageSize = totalElnts / nbPages;
        int nbOrphans = totalElnts % nbPages;
        if (nbOrphans > 0) {
            ++pageSize;
        }
        int startIndex = totalElnts;
        int endIndex = totalElnts;
        for (int pageNumber = 0; pageNumber <= currentPageNumber; ++pageNumber) {
            int currentPageSize = nbOrphans == 0 || pageNumber < nbOrphans ? pageSize : pageSize - 1;
            startIndex -= currentPageSize;
            endIndex = startIndex + currentPageSize;
        }

        List<T> newContent = page.getContent().subList(startIndex, endIndex);
        PageRequestMobile newRequest = new PageRequestMobile(currentPageNumber, pageSize);
        return new PageMobile<T>(newContent, newRequest, totalElnts);
    }

    // GETTER SETTER

    public void setContent(final List<T> content) {
        this.content = content;
    }

    public void setPageable(final PageRequestMobile pageable) {
        this.pageable = pageable;
    }

    public PageRequestMobile getPageable() {
        return pageable;
    }

    public long getTotal() {
        return total;
    }

    public void setTotal(final long total)
    {
        this.total = total;
    }

    @Override
    public Iterator<T> iterator() {

        return this.content.iterator();
    }

}

Wrapper to response :

public class Result<T> {

    protected Boolean error;

    protected String errorMessage;

    private HashMap<String, String> errorDetails;

    private T objectReceive;

    public Result()
    {

    }

    /**
     * Initialize with the error at true
     * 
     * @param objectReceive : object to transmit
     * @param messageError : message error
     */
    public Result(final T objectReceive, final String messageError)
    {
        this.objectReceive = objectReceive;
        hasError(messageError);
    }

    /**
     * Initialize with the error at false
     * 
     * @param objectReceive : object to transmit
     */
    public Result(final T objectReceive)
    {
        this.objectReceive = objectReceive;
        this.error = false;

    }

    public void hasError(final String errorMessage)
    {
        this.errorDetails = new HashMap<String, String>();

        this.error = false;

        if (errorMessage != null)
        {
            this.error = true;
            this.errorMessage = errorMessage;
        }
    }

    public Result(final String errorMessage)
    {
        this.errorDetails = new HashMap<String, String>();

        this.error = false;

        if (errorMessage != null)
        {
            this.error = true;
            this.errorMessage = errorMessage;
        }
    }

    public Result<T> addDetail(final String name, final String value) {
        errorDetails.put(name, value);
        return this;
    }

    public Boolean getError() {
        return error;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public HashMap<String, String> getErrorDetails() {
        return errorDetails;
    }

    public void setError(final Boolean error) {
        this.error = error;
    }

    public void setErrorDetails(final HashMap<String, String> errorDetails) {
        this.errorDetails = errorDetails;
    }

    public void setErrorMessage(final String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public T getObjectReceive() {
        return objectReceive;
    }

    public void setObjectReceive(final T objectReceive) {
        this.objectReceive = objectReceive;
    }

}

Upvotes: 0

Views: 242

Answers (1)

Nicolas Morel
Nicolas Morel

Reputation: 315

With the current version of gwt-jackson (0.6.1), what is sure, you won't be able to use SortMobile as a property in a class. I'll look how I can fix it.

gwt-jackson fails to parse SortMobile because it implements directly Iterable<OrderMobile> and not Iterable<T> with SortMobile<T>.

As a workaround, you have a few solutions :

  • you can declare the property as a Iterable<OrderMobile> in Page. This way, gwt-jackson will use the serializer/deserializer for Iterable but the instance created by the deserializer will be an ArrayList.
  • change SortMobile to SortMobile<T extends OrderMobile> implements Iterable<T>
  • declare your own serializer/deserializer for SortMobile by following the wiki.

Edit : Since version 0.6.2, you should be able to use SortMobile without compilation error.

Upvotes: 0

Related Questions