com.blacklocus.metrics.DemuxedKey.java Source code

Java tutorial

Introduction

Here is the source code for com.blacklocus.metrics.DemuxedKey.java

Source

/**
 * Copyright 2013-2016 BlackLocus
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.blacklocus.metrics;

import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import static com.blacklocus.metrics.Constants.*;

/**
 * @author Jason Dunkelberger (dirkraft)
 */
class DemuxedKey {

    final PermutableChain<String> nameChain;
    final PermutableChain<Dimension> dimensionChain;

    DemuxedKey(String s) {
        String[] segments = s.split(NAME_TOKEN_DELIMITER_RGX);

        PermutableChain<String> names = null;
        PermutableChain<Dimension> dimensions = null;

        // Build NameSegmentChain in reverse. Dimension order is irrelevant.
        for (int i = segments.length - 1; i >= 0; i--) {
            String segment = segments[i];

            boolean permutable = segment.endsWith(NAME_PERMUTE_MARKER);
            if (permutable) {
                segment = segment.substring(0, segment.length() - 1);
            }

            if (segment.contains(NAME_DIMENSION_SEPARATOR)) {
                String[] dimensionParts = segment.split(NAME_DIMENSION_SEPARATOR, 2);
                Dimension dimension = new Dimension().withName(dimensionParts[0]).withValue(dimensionParts[1]);
                dimensions = new PermutableChain<Dimension>(dimension, permutable, dimensions);

            } else {
                assert !segment.contains(NAME_PERMUTE_MARKER);
                names = new PermutableChain<String>(segment, permutable, names);
            }
        }

        this.nameChain = names;
        this.dimensionChain = dimensions;
    }

    /**
     * @param typeName           dimension name to use for the metric type dimension
     * @param typeValue          dimension value to use for the metric type dimension
     * @param datumSpecification writes the metric's data into to the prepared MetricDatum
     * @return the generated <i>datums</i> which should be ready for submission to CloudWath
     */
    Iterable<MetricDatum> newDatums(String typeName, String typeValue,
            Function<MetricDatum, MetricDatum> datumSpecification) {

        // All dimension sets include the type dimension.
        PermutableChain<Dimension> withDimensionChain = new PermutableChain<Dimension>(
                new Dimension().withName(typeName).withValue(typeValue), false, dimensionChain);

        List<MetricDatum> data = new ArrayList<MetricDatum>();

        for (Iterable<String> nameSet : nameChain) {
            String name = StringUtils.join(nameSet, " ");
            if (StringUtils.isBlank(name)) {
                // If all name segments are permutable, there is one combination where all of them are omitted.
                // This is expected and supported but of course can not be submitted.
                continue;
            }
            for (Iterable<Dimension> dimensionSet : withDimensionChain) {
                data.add(datumSpecification.apply(
                        new MetricDatum().withMetricName(name).withDimensions(Lists.newArrayList(dimensionSet))));
            }
        }

        return data;
    }
}

class PermutableChain<T> implements Iterable<Iterable<T>> {

    final T token;
    final boolean permutable;
    final PermutableChain<T> nextSegment;

    PermutableChain(T token, boolean permutable, PermutableChain<T> nextSegment) {
        this.token = token;
        this.permutable = permutable;
        this.nextSegment = nextSegment;
    }

    @Override
    public Iterator<Iterable<T>> iterator() {
        return new Iterator<Iterable<T>>() {

            int permutation = permutable ? 2 : 1;
            Iterator<Iterable<T>> nextSegmentIt = nextSegment == null ? null : nextSegment.iterator();

            @Override
            public boolean hasNext() {
                boolean isTail = nextSegmentIt == null;
                if (isTail) {
                    return permutation > 0;
                } else {
                    return permutation > 0 && nextSegmentIt != null && nextSegmentIt.hasNext();
                }
            }

            @Override
            public Iterable<T> next() {
                assert permutation > 0 && permutation <= 2;
                boolean isTail = nextSegmentIt == null;
                if (isTail) {
                    if (permutation == 2) {
                        permutation = 1;
                        return Collections.emptyList();
                    } else {
                        assert permutation == 1;
                        permutation = 0;
                        return ImmutableList.of(token);
                    }
                } else {
                    if (permutation == 2) {
                        Iterable<T> next = nextSegmentIt.next();
                        if (!nextSegmentIt.hasNext()) {
                            permutation = 1;
                            nextSegmentIt = nextSegment.iterator();
                        }
                        return next;
                    } else {
                        assert permutation == 1;
                        Iterable<T> next = Iterables.concat(ImmutableList.of(token), nextSegmentIt.next());
                        if (!nextSegmentIt.hasNext()) {
                            permutation = 0;
                            nextSegmentIt = null;
                        }
                        return next;
                    }
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Nope.");
            }
        };
    }
    /*
    Three option*
    hasNext == tail && exhausted || next.hasNext()   next: return
    Three -> ""
    Three -> option
     */
}