| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.metron.indexing.integration; |
| |
| import static org.apache.metron.indexing.dao.HBaseDao.HBASE_CF; |
| import static org.apache.metron.indexing.dao.HBaseDao.HBASE_TABLE; |
| import static org.apache.metron.indexing.dao.IndexDao.COMMENTS_FIELD; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.stream.Collectors; |
| import org.apache.metron.hbase.mock.MockHBaseTableProvider; |
| import org.apache.metron.hbase.mock.MockHTable; |
| import org.apache.metron.indexing.dao.AccessConfig; |
| import org.apache.metron.indexing.dao.HBaseDao; |
| import org.apache.metron.indexing.dao.IndexDao; |
| import org.apache.metron.indexing.dao.UpdateIntegrationTest; |
| import org.apache.metron.indexing.dao.search.AlertComment; |
| import org.apache.metron.indexing.dao.search.GetRequest; |
| import org.apache.metron.indexing.dao.update.Document; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class HBaseDaoIntegrationTest extends UpdateIntegrationTest { |
| |
| private static final String TABLE_NAME = "metron_update"; |
| private static final String COLUMN_FAMILY = "cf"; |
| private static final String SENSOR_TYPE = "test"; |
| |
| private static IndexDao hbaseDao; |
| private static byte[] expectedKeySerialization = new byte[] { |
| (byte)0xf5,0x53,0x76,(byte)0x96,0x67,0x3a, |
| (byte)0xc1,(byte)0xaf,(byte)0xff,0x41,0x33,(byte)0x9d, |
| (byte)0xac,(byte)0xb9,0x1a,(byte)0xb0,0x00,0x04, |
| 0x67,0x75,0x69,0x64,0x00,0x0a, |
| 0x73,0x65,0x6e,0x73,0x6f,0x72, |
| 0x54,0x79,0x70,0x65 |
| }; |
| |
| @Before |
| public void startHBase() throws Exception { |
| AccessConfig accessConfig = new AccessConfig(); |
| accessConfig.setMaxSearchResults(1000); |
| accessConfig.setMaxSearchGroups(1000); |
| accessConfig.setGlobalConfigSupplier(() -> new HashMap<String, Object>() {{ |
| put(HBASE_TABLE, TABLE_NAME); |
| put(HBASE_CF, COLUMN_FAMILY); |
| }}); |
| MockHBaseTableProvider.addToCache(TABLE_NAME, COLUMN_FAMILY); |
| accessConfig.setTableProvider(new MockHBaseTableProvider()); |
| |
| hbaseDao = new HBaseDao(); |
| hbaseDao.init(accessConfig); |
| } |
| |
| @After |
| public void clearTable() throws Exception { |
| MockHBaseTableProvider.clear(); |
| } |
| |
| /** |
| * IF this test fails then you have broken the key serialization in that your change has |
| * caused a key to change serialization, so keys from previous releases will not be able to be found |
| * under your scheme. Please either provide a migration plan or undo this change. DO NOT CHANGE THIS |
| * TEST BLITHELY! |
| * @throws Exception |
| */ |
| @Test |
| public void testKeySerializationRemainsConstant() throws IOException { |
| HBaseDao.Key k = new HBaseDao.Key("guid", "sensorType"); |
| byte[] raw = k.toBytes(); |
| Assert.assertArrayEquals(raw, expectedKeySerialization); |
| } |
| |
| |
| @Test |
| public void testKeySerialization() throws Exception { |
| HBaseDao.Key k = new HBaseDao.Key("guid", "sensorType"); |
| Assert.assertEquals(k, HBaseDao.Key.fromBytes(HBaseDao.Key.toBytes(k))); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testKeySerializationWithInvalidGuid() throws Exception { |
| HBaseDao.Key k = new HBaseDao.Key(null, "sensorType"); |
| Assert.assertEquals(k, HBaseDao.Key.fromBytes(HBaseDao.Key.toBytes(k))); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void testKeySerializationWithInvalidSensorType() throws Exception { |
| HBaseDao.Key k = new HBaseDao.Key("guid", null); |
| Assert.assertEquals(k, HBaseDao.Key.fromBytes(HBaseDao.Key.toBytes(k))); |
| } |
| |
| @Test |
| public void shouldGetLatest() throws Exception { |
| // Load alerts |
| List<Document> alerts = buildAlerts(3); |
| Map<Document, Optional<String>> updates = alerts.stream() |
| .collect(Collectors.toMap(document -> document, document -> Optional.empty())); |
| hbaseDao.batchUpdate(updates); |
| |
| Document actualDocument = hbaseDao.getLatest("message_1", SENSOR_TYPE); |
| Document expectedDocument = alerts.get(1); |
| Assert.assertEquals(expectedDocument, actualDocument); |
| } |
| |
| @Test |
| public void shouldGetLatestWithInvalidTimestamp() throws Exception { |
| // Load alert |
| Document alert = buildAlerts(1).get(0); |
| hbaseDao.update(alert, Optional.empty()); |
| |
| Document actualDocument = hbaseDao.getLatest("message_0", SENSOR_TYPE); |
| Assert.assertEquals(alert, actualDocument); |
| |
| alert.getDocument().put("field", "value"); |
| alert.setTimestamp(0L); |
| hbaseDao.update(alert, Optional.empty()); |
| |
| actualDocument = hbaseDao.getLatest("message_0", SENSOR_TYPE); |
| Assert.assertEquals(alert.getDocument(), actualDocument.getDocument()); |
| } |
| |
| @Test |
| public void shouldGetAllLatest() throws Exception { |
| // Load alerts |
| List<Document> alerts = buildAlerts(15); |
| alerts.stream().collect(Collectors.toMap(Document::getGuid, document -> Optional.empty())); |
| Map<Document, Optional<String>> updates = alerts.stream() |
| .collect(Collectors.toMap(document -> document, document -> Optional.empty())); |
| hbaseDao.batchUpdate(updates); |
| |
| int expectedCount = 12; |
| List<GetRequest> getRequests = new ArrayList<>(); |
| for(int i = 1; i < expectedCount + 1; i ++) { |
| getRequests.add(new GetRequest("message_" + i, SENSOR_TYPE)); |
| } |
| Iterator<Document> results = hbaseDao.getAllLatest(getRequests).iterator(); |
| |
| for (int i = 0; i < expectedCount; i++) { |
| Document expectedDocument = alerts.get(i + 1); |
| Document actualDocument = results.next(); |
| Assert.assertEquals(expectedDocument, actualDocument); |
| } |
| |
| Assert.assertFalse("Result size should be 12 but was greater", results.hasNext()); |
| } |
| |
| protected List<Document> buildAlerts(int count) throws IOException { |
| List<Document> alerts = new ArrayList<>(); |
| for (int i = 0; i < count; ++i) { |
| String guid = "message_" + i; |
| String json = "{\"guid\":\"message_" + i + "\", \"source:type\":\"test\"}"; |
| Document alert = new Document(json, guid, SENSOR_TYPE, System.currentTimeMillis()); |
| alerts.add(alert); |
| } |
| return alerts; |
| } |
| |
| @Test |
| @SuppressWarnings("unchecked") |
| public void testRemoveComments() throws Exception { |
| Map<String, Object> fields = new HashMap<>(); |
| fields.put("guid", "add_comment"); |
| fields.put("source.type", SENSOR_NAME); |
| |
| Document document = new Document(fields, "add_comment", SENSOR_NAME, 1526401584951L); |
| hbaseDao.update(document, Optional.of(SENSOR_NAME)); |
| findUpdatedDoc(document.getDocument(), "add_comment", SENSOR_NAME); |
| |
| addAlertComment("add_comment", "New Comment", "test_user", 1526401584951L); |
| // Ensure we have the first comment |
| ArrayList<AlertComment> comments = new ArrayList<>(); |
| comments.add(new AlertComment("New Comment", "test_user", 1526401584951L)); |
| document.getDocument().put(COMMENTS_FIELD, comments.stream().map(AlertComment::asMap).collect( |
| Collectors.toList())); |
| findUpdatedDoc(document.getDocument(), "add_comment", SENSOR_NAME); |
| |
| addAlertComment("add_comment", "New Comment 2", "test_user_2", 1526401584952L); |
| // Ensure we have the second comment |
| comments.add(new AlertComment("New Comment 2", "test_user_2", 1526401584952L)); |
| document.getDocument().put(COMMENTS_FIELD, comments.stream().map(AlertComment::asMap).collect( |
| Collectors.toList())); |
| findUpdatedDoc(document.getDocument(), "add_comment", SENSOR_NAME); |
| |
| removeAlertComment("add_comment", "New Comment 2", "test_user_2", 1526401584952L); |
| // Ensure we only have the first comments |
| comments = new ArrayList<>(); |
| comments.add(new AlertComment(commentOne)); |
| document.getDocument().put(COMMENTS_FIELD, comments.stream().map(AlertComment::asMap).collect( |
| Collectors.toList())); |
| findUpdatedDoc(document.getDocument(), "add_comment", SENSOR_NAME); |
| |
| removeAlertComment("add_comment", "New Comment", "test_user", 1526401584951L); |
| // Ensure we have no comments |
| document.getDocument().remove(COMMENTS_FIELD); |
| findUpdatedDoc(document.getDocument(), "add_comment", SENSOR_NAME); |
| } |
| |
| @Override |
| protected IndexDao getDao() { |
| return hbaseDao; |
| } |
| |
| @Override |
| protected String getIndexName() { |
| return null; |
| } |
| |
| @Override |
| protected MockHTable getMockHTable() { |
| return null; |
| } |
| |
| @Override |
| protected void addTestData(String indexName, String sensorType, List<Map<String, Object>> docs) { |
| } |
| |
| @Override |
| protected List<Map<String, Object>> getIndexedTestData(String indexName, String sensorType) { |
| return null; |
| } |
| } |