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

org.swisspush.gateleen.expansion.RecursiveExpansionHandler Maven / Gradle / Ivy

The newest version!
package org.swisspush.gateleen.expansion;

import org.swisspush.gateleen.core.util.ResourceCollectionException;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * A class for handeling the recursive get request.
 * She holds exactly one collection and handles all of their
 * children, before she calls here parent.
 * 
 * @author https://github.com/ljucam [Mario Ljuca]
 */
public class RecursiveExpansionHandler implements DeltaHandler {
    private static final int PROCESS_DONE = 0;
    private Logger log = LoggerFactory.getLogger(RecursiveExpansionHandler.class);

    private ResourceNode seriousError;
    private AtomicInteger processCount;
    private String collectionName;
    private DeltaHandler parentHandler;
    private Map nodeMap;
    private String collectioneTag;
    private Map resourceCollectionExceptionMap;
    private AtomicLong xDeltaResponseNumber;

    /**
     * Creates a new instance of the RecursiveExpansionHandler.
     * 
     * @param subResourceNames - a list with the child names, needed for preserving the order of the resources
     * @param collectionName - the name of the collection
     * @param collectioneTag - eTag of the request that lead to this collection
     * @param parentHandler - the parent handler
     */
    public RecursiveExpansionHandler(List subResourceNames, String collectionName, String collectioneTag, DeltaHandler parentHandler) {
        if (log.isTraceEnabled()) {
            log.trace("RecursiveExpansionHandler created for collection '{}' with a child count of {}.", collectionName, subResourceNames.size());
        }

        this.xDeltaResponseNumber = new AtomicLong(0);
        this.processCount = new AtomicInteger(subResourceNames.size());
        this.parentHandler = parentHandler;
        this.collectionName = collectionName;
        this.collectioneTag = collectioneTag;

        resourceCollectionExceptionMap = new HashMap<>();
        nodeMap = createEmptyNodeMap(subResourceNames);
    }

    /**
     * Creates a linked HashMap with the capacity of the
     * subResourceNames. The subResourceNames are used
     * as keys and the value is set per default as null.
     * 
     * @param subResourceNames subResourceNames
     * @return Map
     */
    private Map createEmptyNodeMap(List subResourceNames) {
        Map map = new LinkedHashMap<>(subResourceNames.size());
        for (String resourceName : subResourceNames) {
            map.put(resourceName.replace("/", ""), null);
        }
        return map;
    }

    /**
     * Handles the given node.
     * A node has to field, his name
     * and an object. Normaly this object is either
     * a JsonObject or an JSonArray.
     * If an error occures, this object can
     * carry an exception (serious error) or
     * null (no serious error).
     * 
     * @param node node
     */
    @SuppressWarnings("unchecked")
    @Override
    public void handle(ResourceNode node) {
        processCount.decrementAndGet();

        /*
         * a node can contain:
         * > null
         * > JsonObject
         * > - JsonArray
         * > Map
         * > - ResourceCollectionException
         */

        // we have a node (no 404)
        if (node != null) {

            /*
             * if the data is NOT json, an
             * error will be created.
             */

            // pure data
            if (node.getObject() instanceof Buffer) {
                try {
                    node.setObject(new JsonObject(((Buffer) node.getObject()).toString("UTF-8")));
                } catch (Exception e) {
                    log.error("Error in result of sub resource with path '{}' Message: {}", node.getPath(), e.getMessage());
                    node.setObject(new ResourceCollectionException(e.getMessage()));
                }
            }

            // Json Object or Array
            if (node.getObject() instanceof JsonObject || node.getObject() instanceof JsonArray) {
                if (log.isTraceEnabled()) {
                    log.trace("handle collection '{}' for node '{}'.", collectionName, node.getNodeName());
                }

                nodeMap.put(node.getNodeName(), node);
            }
            // error
            else if (node.getObject() instanceof ResourceCollectionException) {
                // serious error (eg. max. request limit exceeded)
                if (node.getNodeName().equals(ExpansionHandler.SERIOUS_EXCEPTION)) {
                    if (log.isTraceEnabled()) {
                        log.trace("(serious error) handle collection '{}'.", collectionName);
                    }

                    // only the first serious error will be 'passed' down the handler
                    if (seriousError == null) {
                        seriousError = node;
                    }
                }
                // resource related error occured
                else {
                    if (log.isTraceEnabled()) {
                        log.trace("(no serious error) handle collection '{}'.", collectionName);
                    }

                    resourceCollectionExceptionMap.put(node.getNodeName(), (ResourceCollectionException) node.getObject());
                }
            }
            // error collection
            else if (node.getObject() instanceof Map) {
                resourceCollectionExceptionMap.putAll((Map) node.getObject());
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("No match found for handling node. This should not happen!");
                }
            }
        }

        // process done
        if (processCount.get() == PROCESS_DONE) {
            if (log.isTraceEnabled()) {
                log.trace("finishing process");
                log.trace(" -> serious error:    " + (seriousError != null));
                log.trace(" -> resource errors:  " + (!resourceCollectionExceptionMap.isEmpty()));
            }

            // serious error occured
            if (seriousError != null) {
                parentHandler.handle(seriousError);
            }
            // resource related errors occured
            else if (!resourceCollectionExceptionMap.isEmpty()) {
                parentHandler.handle(new ResourceNode(collectionName, resourceCollectionExceptionMap));
            }
            // everything is fine
            else {
                /*
                 * creating a json object
                 * based on the linked map
                 * for preserving the order
                 * of the resources.
                 */

                StringBuilder eTags = new StringBuilder();
                eTags.append(collectioneTag);

                JsonObject nodes = new JsonObject();

                for (String key : nodeMap.keySet()) {
                    ResourceNode orderedNode = nodeMap.get(key);
                    eTags.append(orderedNode.geteTag());
                    nodes.put(key, orderedNode.getObject());
                }

                parentHandler.storeXDeltaResponseHeader("" + xDeltaResponseNumber.get());
                parentHandler.handle(new ResourceNode(collectionName, nodes, eTags.toString()));
            }
        }
    }

    @Override
    public void storeXDeltaResponseHeader(String xDeltaResponseNumber) {
        if (log.isTraceEnabled()) {
            log.trace("storeXDeltaResponseHeader > {}", xDeltaResponseNumber);
        }

        // do we have a x-delta number?
        if (xDeltaResponseNumber != null) {

            try {
                // try to parse it
                long tempxDeltaResponseNumber = Long.parseLong(xDeltaResponseNumber);

                // compare numbers
                if (this.xDeltaResponseNumber.get() < tempxDeltaResponseNumber) {
                    this.xDeltaResponseNumber.set(tempxDeltaResponseNumber);
                }
            } catch (NumberFormatException e) {
                log.warn("Delta response value was not a number", e);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy