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

com.yahoo.bard.webservice.util.Utils Maven / Gradle / Ivy

Go to download

Fili web service library provides core capabilities for RESTful aggregation navigation, query planning and metadata

There is a newer version: 1.1.13
Show newest version
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.
package com.yahoo.bard.webservice.util;

import com.yahoo.bard.webservice.data.config.metric.MetricInstance;
import com.yahoo.bard.webservice.data.metric.MetricDictionary;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.core.JsonProcessingException;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.stream.Collectors;

import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;

/**
 * Utils.
 */
public class Utils {

    /**
     * Logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(Utils.class);

    /**
     * A strategy that deletes a directory.
     */
    private static final FileVisitor DELETE_VISITOR = new SimpleFileVisitor() {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            Files.delete(dir);
            return FileVisitResult.CONTINUE;
        }
    };

    /**
     * Given a collection of objects which share the same super class, return the subset of objects that share a common
     * sub class.
     *
     * @param set  Input set
     * @param   Input set type
     * @param type  sub class
     * @param   sub class type
     *
     * @return ordered subset of objects that share a common sub class
     */
    public static  LinkedHashSet getSubsetByType(Collection set, Class type) {
        return set.stream()
                .filter(type::isInstance)
                .map(StreamUtils.uncheckedCast(type))
                .collect(Collectors.toCollection(LinkedHashSet::new));
    }

    /**
     * Easily turn a few instances of an object into a LinkedHashSet.
     *
     * 
     * LinkedHashSet<String> stooges = Utils.asLinkedHashSet("Larry", "Moe", "Curly");
     * 
* * @param The element type for the linked hash set * @param e The array from which the LinkedHashSet will be built * * @return a LinkedHashSet view of the specified array */ @SafeVarargs public static LinkedHashSet asLinkedHashSet(E... e) { return new LinkedHashSet<>(Arrays.asList(e)); } /** * Create parent directories if they don't exist in a given file path. * * @param path The pathname */ public static void createParentDirectories(String path) { File targetFile = new File(path); File parent = targetFile.getParentFile(); if (!parent.exists() && !parent.mkdirs()) { throw new IllegalStateException("Couldn't create dir: " + parent); } } /** * Delete files or directories in the specified path. * * @param path The pathname */ public static void deleteFiles(String path) { Path file = Paths.get(path); // do nothing if there is nothing to delete if (!Files.exists(file)) { LOG.trace(String.format("'%s' does not exist. Nothing is deleted", path)); return; } try { Files.walkFileTree(file, DELETE_VISITOR); } catch (IOException e) { throw new UncheckedIOException(e); } } /** * Helper method to throw an exception when a result is expected as a return value (e.g. in a ternary operator) * * @param The type of exception being returned * @param exception The exception to be thrown * * @return is only used for type inference. No object is actually ever returned. An exception is always being thrown * instead */ public static T insteadThrowRuntime(RuntimeException exception) { throw exception; } /** * Helper method to return request headers as a map of the same type with its keys lower cased. * * @param headers The request headers. * @return The headers with their names lower cased. */ public static MultivaluedMap headersToLowerCase(MultivaluedMap headers) { return headers.entrySet().stream() .collect( StreamUtils.toMap( e -> e.getKey().toLowerCase(Locale.ENGLISH), Map.Entry::getValue, MultivaluedHashMap::new ) ); } /** * Given a field name and a tree of json nodes, empty the contents of all the json nodes matching the field name. * This method is recursive. * * @param node The root of the tree of json nodes. * @param fieldName The name of the node to be omitted. * @param mapper The object mapper that creates and empty node. * @deprecated Should avoid this method and instead use {@link #canonicalize(JsonNode, ObjectMapper, boolean)} * which preserves JSON object ordering that guarantees consistent hash values. */ @Deprecated public static void omitField(JsonNode node, String fieldName, ObjectMapper mapper) { if (node.has("context")) { ((ObjectNode) node).replace(fieldName, mapper.createObjectNode()); } for (JsonNode child : node) { omitField(child, fieldName, mapper); } } /** * Given a JsonNode, order the ObjectNodes and ArrayNodes recursively and replace context blocks with empty nodes. *

* * @param node The root of the tree of json nodes. * @param mapper The object mapper that creates and empty node. * @param preserveContext Boolean indicating whether context should be omitted. */ public static void canonicalize(JsonNode node, ObjectMapper mapper, boolean preserveContext) { boolean arrayWithNameObjects = true; if (node.isObject()) { canonicalizeObject(node, mapper, preserveContext); } else if (node.isArray()) { canonicalizeArray(node, mapper, preserveContext, arrayWithNameObjects); } } /** * Find the minimum value between two comparable objects. * * @param one Item 1 * @param two Item 2 * @param Type of object to compare * * @return the minimum of the two objects */ @SuppressWarnings("unchecked") public static T getMinValue(T one, T two) { return one.compareTo(two) < 1 ? one : two; } /** * Given an ImmutablePair, and a right value, returns a new ImmutablePair with the same left value, * and the specified right value. * * @param pair Immutable Pair instance * @param right The right value, may be null * @param Left type of the pair * @param Right type of the pair * @param The right value to have new Immutable Pair * * @return New instance of Immutable Pair */ public static ImmutablePair withRight(ImmutablePair pair, V right) { return new ImmutablePair<>(pair.getLeft(), right); } /** * Create metrics from instance descriptors and store in the metric dictionary. * * @param metricDictionary The dictionary to store metrics in * @param metrics The list of metric descriptors */ public static void addToMetricDictionary(MetricDictionary metricDictionary, List metrics) { metrics.stream().map(MetricInstance::make).forEach(metricDictionary::add); } /** * Given a JsonObjectNode, order the ObjectNode entries on the key recursively and * replace context blocks with empty nodes. *

* This method is recursive. * * @param node The root of the tree of json nodes. * @param mapper The object mapper that creates and empty node. * @param preserveContext Boolean indicating whether context should be omitted. */ public static void canonicalizeObject(JsonNode node, ObjectMapper mapper, boolean preserveContext) { ObjectNode objectNode = ((ObjectNode) node); if (objectNode.has("context") && !preserveContext) { objectNode.replace("context", mapper.createObjectNode()); } Iterator> iterator = objectNode.fields(); // collect and sort the entries TreeMap fieldMap = new TreeMap<>(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); fieldMap.put(entry.getKey(), entry.getValue()); // canonicalize all child nodes canonicalize(entry.getValue(), mapper, preserveContext); } // remove the existing entries objectNode.removeAll(); // replace the entries in sorted order objectNode.setAll(fieldMap); } /** * Given a JsonArrayNode, store the ArrayNode entries in order and recursively canonicalize JsonNodes. *

* This method is recursive. * * @param node The root of the tree of json nodes. * @param mapper The object mapper that creates and empty node. * @param preserveContext Boolean indicating whether context should be omitted. * @param arrayWithNameObjects Boolean indicating whether all objects within the ArrayNode have a name field. */ public static void canonicalizeArray( JsonNode node, ObjectMapper mapper, boolean preserveContext, boolean arrayWithNameObjects ) { ArrayNode arrayNode = ((ArrayNode) node); Iterator iterator = arrayNode.elements(); // collect and store the entries in order LinkedHashSet fieldSet = new LinkedHashSet<>(); while (iterator.hasNext()) { JsonNode entry = iterator.next(); fieldSet.add(entry); if (entry.isObject() && entry.get("name") == null) { arrayWithNameObjects = false; } // canonicalize all child nodes canonicalize(entry, mapper, preserveContext); } // if all objects in the array contain name field, then order on its value if (arrayWithNameObjects) { List arrayValues = new ArrayList<>(); for (int i = 0; i < arrayNode.size(); i++) { arrayValues.add(arrayNode.get(i)); } Collections.sort(arrayValues, new Comparator() { private static final String KEY_NAME = "name"; @Override public int compare(JsonNode a, JsonNode b) { String valA = new String(); String valB = new String(); try { valA = mapper.writeValueAsString(a.get(KEY_NAME)); valB = mapper.writeValueAsString(b.get(KEY_NAME)); } catch (JsonProcessingException e) { LOG.warn(String.format("Error processing json for cache key. %s", e.getMessage()), e); } return valA.compareTo(valB); } }); // remove the existing entries arrayNode.removeAll(); // replace the entries in order arrayValues.stream().forEach(val -> arrayNode.add(val)); } else { // remove the existing entries arrayNode.removeAll(); // replace the entries in order arrayNode.addAll(fieldSet); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy