All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.shredzone.acme4j.connector.ResourceIterator Maven / Gradle / Ivy

/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2016 Richard "Shred" Körber
 *   http://acme4j.shredzone.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.shredzone.acme4j.connector;

import java.net.HttpURLConnection;
import java.net.URI;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.BiFunction;

import org.shredzone.acme4j.AcmeResource;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.util.JSON;

/**
 * An {@link Iterator} that fetches a batch of URIs from the ACME server, and generates
 * {@link AcmeResource} instances.
 *
 * @param 
 *            {@link AcmeResource} type to iterate over
 */
public class ResourceIterator implements Iterator {

    private final Session session;
    private final String field;
    private final Deque uriList = new ArrayDeque<>();
    private final BiFunction creator;
    private boolean eol = false;
    private URI nextUri;

    /**
     * Creates a new {@link ResourceIterator}.
     *
     * @param session
     *            {@link Session} to bind this iterator to
     * @param field
     *            Field name to be used in the JSON response
     * @param start
     *            URI of the first JSON array, may be {@code null} for an empty iterator
     * @param creator
     *            Creator for an {@link AcmeResource} that is bound to the given
     *            {@link Session} and {@link URI}.
     */
    public ResourceIterator(Session session, String field, URI start, BiFunction creator) {
        this.session = Objects.requireNonNull(session, "session");
        this.field = Objects.requireNonNull(field, "field");
        this.nextUri = start;
        this.creator = Objects.requireNonNull(creator, "creator");
    }

    /**
     * Checks if there is another object in the result.
     *
     * @throws AcmeProtocolException
     *             if the next batch of URIs could not be fetched from the server
     */
    @Override
    public boolean hasNext() {
        if (eol) {
            return false;
        }

        if (uriList.isEmpty()) {
            fetch();
        }

        if (uriList.isEmpty()) {
            eol = true;
        }

        return !uriList.isEmpty();
    }

    /**
     * Returns the next object of the result.
     *
     * @throws AcmeProtocolException
     *             if the next batch of URIs could not be fetched from the server
     * @throws NoSuchElementException
     *             if there are no more entries
     */
    @Override
    public T next() {
        if (!eol && uriList.isEmpty()) {
            fetch();
        }

        URI next = uriList.poll();
        if (next == null) {
            eol = true;
            throw new NoSuchElementException("no more " + field);
        }

        return creator.apply(session, next);
    }

    /**
     * Unsupported operation, only here to satisfy the {@link Iterator} interface.
     */
    @Override
    public void remove() {
        throw new UnsupportedOperationException("cannot remove " + field);
    }

    /**
     * Fetches the next batch of URIs. Handles exceptions. Does nothing if there is no
     * URI of the next batch.
     */
    private void fetch() {
        if (nextUri == null) {
            return;
        }

        try {
            readAndQueue();
        } catch (AcmeException ex) {
            throw new AcmeProtocolException("failed to read next set of " + field, ex);
        }
    }

    /**
     * Reads the next batch of URIs from the server, and fills the queue with the URIs. If
     * there is a "next" header, it is used for the next batch of URIs.
     */
    private void readAndQueue() throws AcmeException {
        try (Connection conn = session.provider().connect()) {
            conn.sendRequest(nextUri, session);
            conn.accept(HttpURLConnection.HTTP_OK);

            JSON json = conn.readJsonResponse();
            fillUriList(json);

            nextUri = conn.getLink("next");
        }
    }

    /**
     * Fills the uri list with the URIs found in the desired field.
     *
     * @param json
     *            JSON map to read from
     */
    private void fillUriList(JSON json) {
        JSON.Array array = json.get(field).asArray();
        if (array == null) {
            return;
        }

        array.stream().map(JSON.Value::asURI).forEach(uriList::add);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy