/* * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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 org.hawkular.inventory.api.test; import static org.hawkular.inventory.paths.ElementTypeVisitor.accept; import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import org.hawkular.inventory.api.model.ContentHash; import org.hawkular.inventory.api.model.DataEntity; import org.hawkular.inventory.api.model.Entity; import org.hawkular.inventory.api.model.Environment; import org.hawkular.inventory.api.model.Feed; import org.hawkular.inventory.api.model.Metric; import org.hawkular.inventory.api.model.MetricDataType; import org.hawkular.inventory.api.model.MetricType; import org.hawkular.inventory.api.model.MetricUnit; import org.hawkular.inventory.api.model.OperationType; import org.hawkular.inventory.api.model.Resource; import org.hawkular.inventory.api.model.ResourceType; import org.hawkular.inventory.api.model.StructuredData; import org.hawkular.inventory.api.model.Tenant; import org.hawkular.inventory.paths.CanonicalPath; import org.hawkular.inventory.paths.ElementTypeVisitor; import org.hawkular.inventory.paths.SegmentType; import org.junit.Assert; import org.junit.Test; /** * @author Lukas Krejci * @since 0.18.0 */ public class ContentHashTest { @Test public void testComputesForEveryEntityType() throws Exception { Map<String, Object> props = new HashMap<>(); props.put("a", "b"); props.put("b", "c"); String name = "name"; ElementTypeVisitor<Entity.Blueprint, Void> generator = new ElementTypeVisitor<Entity.Blueprint, Void>() { @Override public Entity.Blueprint visitTenant(Void parameter) { return Tenant.Blueprint.builder().withId("id").withName(name).withProperties(props).build(); } @Override public Entity.Blueprint visitEnvironment(Void parameter) { return Environment.Blueprint.builder().withId("id").withName(name).withProperties(props).build(); } @Override public Entity.Blueprint visitFeed(Void parameter) { return Feed.Blueprint.builder().withId("id").withName(name).withProperties(props).build(); } @Override public Entity.Blueprint visitMetric(Void parameter) { return Metric.Blueprint.builder().withId("id").withName(name).withProperties(props) .withMetricTypePath("../../metricType").withInterval(1L).build(); } @Override public Entity.Blueprint visitMetricType(Void parameter) { return MetricType.Blueprint.builder(MetricDataType.GAUGE).withId("id").withName(name) .withProperties(props).withInterval(1L).withUnit(MetricUnit.BYTES).build(); } @Override public Entity.Blueprint visitResource(Void parameter) { return Resource.Blueprint.builder().withId("id").withName(name).withProperties(props) .withResourceTypePath("../../resourceType").build(); } @Override public Entity.Blueprint visitResourceType(Void parameter) { return ResourceType.Blueprint.builder().withId("id").withName(name).withProperties(props).build(); } @Override public Entity.Blueprint visitRelationship(Void parameter) { return null; } @Override public Entity.Blueprint visitData(Void parameter) { return DataEntity.Blueprint.builder().withId("configuration").withName(name).withProperties(props) .withValue(StructuredData.get().bool(true)).build(); } @Override public Entity.Blueprint visitOperationType(Void parameter) { return OperationType.Blueprint.builder().withId("id").withName(name).withProperties(props).build(); } @Override public Entity.Blueprint visitMetadataPack(Void parameter) { return null; } @Override public Entity.Blueprint visitUnknown(Void parameter) { return null; } }; String commonHash = hash(name, props); String metricHash = hash("../../mt;metricType1" + name, props); //relative metric type path followed by the //collection interval, followed by the name //and props String resourceHash = hash("../../rt;resourceType" + name, props); String metricTypeHash = hash("" + MetricDataType.GAUGE + MetricUnit.BYTES + "1" + name, props); String dataHash = hash(StructuredData.get().bool(true).toJSON() + "configuration", props); for (SegmentType t : SegmentType.values()) { Entity.Blueprint bl = accept(t, generator, null); if (bl != null) { //no other entity type but the resource needs the canonical path to compute the content hash. //so we can safely just pass here the canonical path of the resource. It will be ignored in all other //but correct cases. String contentHash = ContentHash.of(bl, CanonicalPath.of().tenant("tnt").feed("fd").resource("id").get()); ElementTypeVisitor.accept(t, new ElementTypeVisitor.Simple<Void, Void>() { @Override protected Void defaultAction(SegmentType elementType, Void parameter) { Assert.assertEquals("Unexpected content hash for " + elementType.getSimpleName(), commonHash, contentHash); return null; } @Override public Void visitMetric(Void parameter) { Assert.assertEquals("Unexpected content hash for Metric", metricHash, contentHash); return null; } @Override public Void visitMetricType(Void parameter) { Assert.assertEquals("Unexpected content hash for MetricType", metricTypeHash, contentHash); return null; } @Override public Void visitResource(Void parameter) { Assert.assertEquals("Unexpected content hash for Resource", resourceHash, contentHash); return null; } @Override public Void visitData(Void parameter) { Assert.assertEquals("Unexpected content hash for DataEntity", dataHash, contentHash); return null; } }, null); } } } private String digest(String content) throws NoSuchAlgorithmException { byte[] digest = MessageDigest.getInstance("SHA-1").digest(content.getBytes(Charset.forName("UTF-8"))); StringBuilder bld = new StringBuilder(); for (byte b : digest) { bld.append(Integer.toHexString(Byte.toUnsignedInt(b))); } return bld.toString(); } private String hash(String name, Map<String, Object> properties) throws NoSuchAlgorithmException { Map<String, Object> sorted = new TreeMap<>(Comparator.naturalOrder()); sorted.putAll(properties); StringBuilder content = new StringBuilder(name); sorted.forEach((k, v) -> content.append(k).append(v)); return digest(content.toString()); } }