http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateInitializer.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateInitializer.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateInitializer.java deleted file mode 100644 index 589acbe..0000000 --- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateInitializer.java +++ /dev/null @@ -1,346 +0,0 @@ -/** - * 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.sentry.service.thrift; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hive.metastore.HiveMetaStoreClient; -import org.apache.hadoop.hive.metastore.api.Database; -import org.apache.hadoop.hive.metastore.api.MetaException; -import org.apache.hadoop.hive.metastore.api.Partition; -import org.apache.hadoop.hive.metastore.api.StorageDescriptor; -import org.apache.hadoop.hive.metastore.api.Table; -import org.apache.thrift.TException; -import org.junit.Assert; -import org.junit.Test; -import org.mockito.Mockito; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class TestFullUpdateInitializer { - - private static Configuration conf = new Configuration(); - - static { - conf.setInt(org.apache.sentry.hdfs.ServiceConstants.ServerConfig - .SENTRY_HDFS_SYNC_METASTORE_CACHE_MAX_PART_PER_RPC, 1); - conf.setInt(org.apache.sentry.hdfs.ServiceConstants.ServerConfig - .SENTRY_HDFS_SYNC_METASTORE_CACHE_MAX_TABLES_PER_RPC, 1); - conf.setInt(org.apache.sentry.hdfs.ServiceConstants.ServerConfig - .SENTRY_HDFS_SYNC_METASTORE_CACHE_INIT_THREADS, 8); - } - - /** - * Representation of a Hive table. A table has a name and a list of partitions. - */ - private static class HiveTable { - String name; - List<String> partitions; - - HiveTable(String name) { - this.name = name; - this.partitions = new ArrayList<>(); - } - - HiveTable(String name, List<String> partitions) { - this.name = name; - this.partitions = partitions; - if (this.partitions == null) { - this.partitions = new ArrayList<>(); - } - } - - HiveTable add(String partition) { - partitions.add(partition); - return this; - } - } - - /** - * Representation of a Hive database. A database has a name and a list of tables - */ - private static class HiveDb { - String name; - Collection<HiveTable> tables; - - HiveDb(String name) { - this.name = name; - tables = new ArrayList<>(); - } - - HiveDb(String name, Collection<HiveTable> tables) { - this.name = name; - this.tables = tables; - if (this.tables == null) { - this.tables = new ArrayList<>(); - } - } - - void add(HiveTable table) { - this.tables.add(table); - } - } - - /** - * Representation of a full Hive snapshot. A snapshot is collection of databases - */ - private static class HiveSnapshot { - List<HiveDb> databases = new ArrayList<>(); - - HiveSnapshot() { - } - - HiveSnapshot(Collection<HiveDb> dblist) { - if (dblist != null) { - databases.addAll(dblist); - } - } - - HiveSnapshot add(HiveDb db) { - this.databases.add(db); - return this; - } - } - - /** - * Convert Hive snapshot to mock client that will return proper values - * for the snapshot. - */ - private static class MockClient { - HiveMetaStoreClient client; - - MockClient(HiveSnapshot snapshot) throws TException { - client = Mockito.mock(HiveMetaStoreClient.class); - List<String> dbNames = new ArrayList<>(snapshot.databases.size()); - // Walk over all databases and mock appropriate objects - for (HiveDb mdb: snapshot.databases) { - String dbName = mdb.name; - dbNames.add(dbName); - Database db = makeDb(dbName); - Mockito.when(client.getDatabase(dbName)).thenReturn(db); - List<String> tableNames = new ArrayList<>(mdb.tables.size()); - // Walk over all tables for the database and mock appropriate objects - for (HiveTable table: mdb.tables) { - String tableName = table.name; - tableNames.add(tableName); - Table mockTable = makeTable(dbName, tableName); - Mockito.when(client.getTableObjectsByName(dbName, - Lists.newArrayList(tableName))) - .thenReturn(Lists.newArrayList(mockTable)); - Mockito.when(client.listPartitionNames(dbName, tableName, (short) -1)) - .thenReturn(table.partitions); - // Walk across all partitions and mock appropriate objects - for (String partName: table.partitions) { - Partition p = makePartition(dbName, tableName, partName); - Mockito.when(client.getPartitionsByNames(dbName, tableName, - Lists.<String>newArrayList(partName))) - .thenReturn(Lists.<Partition>newArrayList(p)); - } - } - Mockito.when(client.getAllTables(dbName)).thenReturn(tableNames); - } - // Return all database names - Mockito.when(client.getAllDatabases()).thenReturn(dbNames); - } - } - - private static class MockHMSClientFactory implements HiveConnectionFactory { - - private final HiveMetaStoreClient mClient; - - private MockHMSClientFactory(MockClient mClient) { - this.mClient = mClient.client; - } - - private MockHMSClientFactory(HiveMetaStoreClient client) { - this.mClient = client; - } - - @Override - public HMSClient connect() throws IOException, InterruptedException, MetaException { - return new HMSClient(mClient); - } - - @Override - public void close() throws Exception { - } - } - - /** - * Create mock database with the given name - * @param name Database name - * @return Mock database object - */ - private static Database makeDb(String name) { - Database db = Mockito.mock(Database.class); - Mockito.when(db.getName()).thenReturn(name); - Mockito.when(db.getLocationUri()).thenReturn("hdfs:///" + name); - return db; - } - - /** - * Create mock table - * @param dbName db for this table - * @param tableName name of the table - * @return mock table object - */ - private static Table makeTable(String dbName, String tableName) { - Table table = Mockito.mock(Table.class); - Mockito.when(table.getDbName()).thenReturn(dbName); - Mockito.when(table.getTableName()).thenReturn(tableName); - StorageDescriptor sd = Mockito.mock(StorageDescriptor.class); - Mockito.when(sd.getLocation()).thenReturn( - String.format("hdfs:///%s/%s", dbName, tableName)); - Mockito.when(table.getSd()).thenReturn(sd); - return table; - } - - /** - * Create mock partition - * @param dbName database for this partition - * @param tableName table for this partition - * @param partName partition name - * @return mock partition object - */ - private static Partition makePartition(String dbName, String tableName, String partName) { - Partition partition = Mockito.mock(Partition.class); - StorageDescriptor sd = Mockito.mock(StorageDescriptor.class); - Mockito.when(sd.getLocation()).thenReturn( - String.format("hdfs:///%s/%s/%s", dbName, tableName, partName)); - Mockito.when(partition.getSd()).thenReturn(sd); - return partition; - } - - @Test - // Test basic operation with small database - public void testSimple() throws Exception { - HiveTable tab21 = new HiveTable("tab21"); - HiveTable tab31 = new HiveTable("tab31").add("part311").add("part312"); - HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); - HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); - HiveDb db1 = new HiveDb("db1"); - HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); - MockClient c = new MockClient(snap); - - Map<String, Collection<String>> update; - try(FullUpdateInitializer cacheInitializer = - new FullUpdateInitializer(new MockHMSClientFactory(c), conf)) { - update = cacheInitializer.getFullHMSSnapshot(); - } - Assert.assertEquals(5, update.size()); - Assert.assertEquals(Sets.newHashSet("db1"), update.get("db1")); - Assert.assertEquals(Sets.newHashSet("db2"), update.get("db2")); - Assert.assertEquals(Sets.newHashSet("db3"), update.get("db3")); - Assert.assertEquals(Sets.newHashSet("db2/tab21"), update.get("db2.tab21")); - Assert.assertEquals(Sets.newHashSet("db3/tab31", - "db3/tab31/part311", "db3/tab31/part312"), update.get("db3.tab31")); - } - - @Test - // Test that invalid paths are handled correctly - public void testInvalidPaths() throws Exception { - //Set up mocks: db1.tb1, with tb1 returning a wrong dbname (db2) - Database db1 = makeDb("db1"); - - Table tab1 = Mockito.mock(Table.class); - //Return a wrong db name, so that this triggers an exception - Mockito.when(tab1.getDbName()).thenReturn("db2"); - Mockito.when(tab1.getTableName()).thenReturn("tab1"); - - HiveMetaStoreClient client = Mockito.mock(HiveMetaStoreClient.class); - Mockito.when(client.getAllDatabases()).thenReturn(Lists.newArrayList("db1")); - Mockito.when(client.getDatabase("db1")).thenReturn(db1); - - Table tab12 = Mockito.mock(Table.class); - Mockito.when(tab12.getDbName()).thenReturn("db1"); - Mockito.when(tab12.getTableName()).thenReturn("tab21"); - StorageDescriptor sd21 = Mockito.mock(StorageDescriptor.class); - Mockito.when(sd21.getLocation()).thenReturn("hdfs:///db1/tab21"); - Mockito.when(tab12.getSd()).thenReturn(sd21); - - Mockito.when(client.getTableObjectsByName("db1", - Lists.newArrayList("tab1"))).thenReturn(Lists.newArrayList(tab1)); - Mockito.when(client.getTableObjectsByName("db1", - Lists.newArrayList("tab12"))).thenReturn(Lists.newArrayList(tab12)); - Mockito.when(client.getAllTables("db1")). - thenReturn(Lists.newArrayList("tab1", "tab12")); - - - Map<String, Collection<String>> update; - try(FullUpdateInitializer cacheInitializer = - new FullUpdateInitializer(new MockHMSClientFactory(client), conf)) { - update = cacheInitializer.getFullHMSSnapshot(); - } - Assert.assertEquals(2, update.size()); - Assert.assertEquals(Sets.newHashSet("db1"), update.get("db1")); - Assert.assertEquals(Sets.newHashSet("db1/tab21"), update.get("db1.tab21")); - } - - @Test - // Test handling of a big tables and partitions - public void testBig() throws Exception { - int ndbs = 3; - int ntables = 51; - int nparts = 131; - - HiveSnapshot snap = new HiveSnapshot(); - - for (int i = 0; i < ndbs; i++) { - HiveDb db = new HiveDb("db" + i); - for (int j = 0; j < ntables; j++) { - HiveTable table = new HiveTable("table" + i + j); - for (int k = 0; k < nparts; k++) { - table.add("part" + i + j + k); - } - db.add(table); - } - snap.add(db); - } - MockClient c = new MockClient(snap); - Map<String, Collection<String>> update; - try(FullUpdateInitializer cacheInitializer = - new FullUpdateInitializer(new MockHMSClientFactory(c), conf)) { - update = cacheInitializer.getFullHMSSnapshot(); - } - Assert.assertEquals((ntables * ndbs) + ndbs, update.size()); - for (int i = 0; i < ndbs; i++) { - String dbName = "db" + i; - Assert.assertEquals(Sets.newHashSet(dbName), update.get(dbName)); - - for (int j = 0; j < ntables; j++) { - String tableName = "table" + i + j; - Set<String> values = new HashSet<>(); - values.add(String.format("%s/%s", dbName, tableName)); - for (int k = 0; k < nparts; k++) { - String partName = "part" + i + j + k; - values.add(String.format("%s/%s/%s", dbName, tableName, partName)); - } - String authz = dbName + "." + tableName; - Assert.assertEquals(values, update.get(authz)); - } - } - } - -}
http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateModifier.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateModifier.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateModifier.java deleted file mode 100644 index c6be80d..0000000 --- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestFullUpdateModifier.java +++ /dev/null @@ -1,482 +0,0 @@ -/** - * 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.sentry.service.thrift; - -import org.apache.hadoop.hive.metastore.api.NotificationEvent; -import org.apache.hadoop.hive.metastore.api.Partition; -import org.apache.hadoop.hive.metastore.api.StorageDescriptor; -import org.apache.hadoop.hive.metastore.api.Table; -import org.apache.hadoop.hive.metastore.messaging.MessageDeserializer; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONAddPartitionMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONAlterPartitionMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONAlterTableMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONCreateDatabaseMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONCreateTableMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONDropDatabaseMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONDropPartitionMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONDropTableMessage; -import org.apache.sentry.binding.metastore.messaging.json.SentryJSONMessageDeserializer; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static org.apache.hadoop.hive.metastore.messaging.EventMessage.EventType.*; -import static org.junit.Assert.*; - -public class TestFullUpdateModifier { - private static final String SERVER = "s"; - private static final String PRINCIPAL = "p"; - private static final String DB = "Db1"; - private static final String TABLE = "Tab1"; - private static final String AUTH = DB.toLowerCase() + "." + TABLE.toLowerCase(); - private static final String PATH = "foo/bar"; - private static final String LOCATION = uri(PATH); - - private static final Table TABLE_OBJ = new Table(TABLE, DB, "", 0, 0, 0, - buildStorageDescriptor(LOCATION), null, null, "", "", ""); - - /** - * Convert path to HDFS URI - */ - private static final String uri(String path) { - return "hdfs:///" + path; - } - - /** - * Creates a StorageDescriptor using the location as parameter. - * - * @param location The location string for the StorageDescriptor - * @return A StorageDescriptor object - */ - private static StorageDescriptor buildStorageDescriptor(String location) { - return new StorageDescriptor(null, location, "", "", false, 0, null, null, null, null); - } - - /** - * Creates a Table object using the db name, table name and table location as parameters. - * - * @param dbName The database name string. - * @param tableName The table name string. - * @param location The table location string. - * @return A Table object - */ - private static Table buildTable(String dbName, String tableName, String location) { - return new Table(tableName, dbName, "", 0, 0, 0, - buildStorageDescriptor(location), null, null, "", "", ""); - } - - /** - * Test create database event. It should add database and its location. - * As a result we should have entry {"db1": {foo/bar}} - * @throws Exception - */ - @Test - public void testCreateDatabase() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - NotificationEvent event = new NotificationEvent(0, 0, CREATE_DATABASE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONCreateDatabaseMessage message = - new SentryJSONCreateDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, LOCATION); - Mockito.when(deserializer.getCreateDatabaseMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Map<String, Set<String>> expected = new HashMap<>(); - expected.put(DB.toLowerCase(), Collections.singleton(PATH)); - assertEquals(expected, update); - } - - /** - * Test drop database event. It should drop database record. - * @throws Exception - */ - @Test - public void testDropDatabase() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - NotificationEvent event = new NotificationEvent(0, 0, DROP_DATABASE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONDropDatabaseMessage message = - new SentryJSONDropDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, LOCATION); - Mockito.when(deserializer.getDropDatabaseMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - assertTrue(update.isEmpty()); - } - - /** - * Test drop database event when dropped database location doesn't - * match original database location. Should leave update intact. - * @throws Exception - */ - @Test - public void testDropDatabaseWrongLocation() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - - NotificationEvent event = new NotificationEvent(0, 0, DROP_DATABASE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONDropDatabaseMessage message = - new SentryJSONDropDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, - "hdfs:///bad/location"); - Mockito.when(deserializer.getDropDatabaseMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - // DB should stay - Map<String, Set<String>> expected = new HashMap<>(); - expected.put(DB.toLowerCase(), Collections.singleton(PATH)); - assertEquals(expected, update); - } - - /** - * Test drop database which has tables/partitions. - * Should drop all reated database records but leave unrelated records in place. - * @throws Exception - */ - @Test - public void testDropDatabaseWithTables() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - update.put(AUTH, Collections.singleton(PATH)); - update.put("unrelated", Collections.singleton(PATH)); - NotificationEvent event = new NotificationEvent(0, 0, DROP_DATABASE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONDropDatabaseMessage message = - new SentryJSONDropDatabaseMessage(SERVER, PRINCIPAL, DB, 0L, LOCATION); - Mockito.when(deserializer.getDropDatabaseMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Map<String, Set<String>> expected = new HashMap<>(); - expected.put("unrelated", Collections.singleton(PATH)); - assertEquals(expected, update); - } - - /** - * Test create table event. It should add table and its location. - * As a result we should have entry {"db1.tab1": {foo/bar}} - * @throws Exception - */ - @Test - public void testCreateTable() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - NotificationEvent event = new NotificationEvent(0, 0, CREATE_TABLE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONCreateTableMessage message = - new SentryJSONCreateTableMessage(SERVER, PRINCIPAL, TABLE_OBJ, Collections.emptyIterator(), 0L); - Mockito.when(deserializer.getCreateTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Map<String, Set<String>> expected = new HashMap<>(); - expected.put(AUTH, Collections.singleton(PATH)); - assertEquals(expected, update); - } - - /** - * Test drop table event. It should drop table record. - * @throws Exception - */ - @Test - public void testDropTable() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(AUTH, Collections.singleton(PATH)); - NotificationEvent event = new NotificationEvent(0, 0, DROP_TABLE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONDropTableMessage message = - new SentryJSONDropTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L, LOCATION); - Mockito.when(deserializer.getDropTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - assertTrue(update.isEmpty()); - } - - /** - * Test drop table event. It should drop table record. - * @throws Exception - */ - @Test - public void testDropTableWrongLocation() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(AUTH, Collections.singleton(PATH)); - NotificationEvent event = new NotificationEvent(0, 0, DROP_TABLE.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONDropTableMessage message = - new SentryJSONDropTableMessage(SERVER, PRINCIPAL, DB, TABLE, 0L, - "hdfs:///bad/location"); - Mockito.when(deserializer.getDropTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - // DB should stay - assertEquals(Collections.singleton(PATH), update.get(AUTH)); - assertEquals(1, update.size()); - } - - /** - * Test add partition event. It should add table and its location. - * As a result we should have entry {"db1.tab1": {foo/bar, hello/world}} - * @throws Exception - */ - @Test - public void testAddPartition() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - Set<String> locations = new HashSet<>(); - locations.add(PATH); - update.put(AUTH, locations); - - NotificationEvent event = new NotificationEvent(0, 0, ADD_PARTITION.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - String partPath = "hello/world"; - String partLocation = uri(partPath); - - SentryJSONAddPartitionMessage message = - new SentryJSONAddPartitionMessage(SERVER, PRINCIPAL, TABLE_OBJ, - Collections.emptyIterator(), Collections.emptyIterator(), - 0L, Collections.singletonList(partLocation)); - Mockito.when(deserializer.getAddPartitionMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Set<String> expected = new HashSet<>(2); - expected.add(PATH); - expected.add(partPath); - assertEquals(expected, update.get(AUTH)); - } - - /** - * Test drop partition event. It should drop partition info from the list of locations. - * @throws Exception - */ - @Test - public void testDropPartitions() throws Exception { - String partPath = "hello/world"; - String partLocation = uri(partPath); - Map<String, Collection<String>> update = new HashMap<>(); - Set<String> locations = new HashSet<>(); - locations.add(PATH); - locations.add(partPath); - update.put(AUTH, locations); - - NotificationEvent event = new NotificationEvent(0, 0, DROP_PARTITION.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONDropPartitionMessage message = - new SentryJSONDropPartitionMessage(SERVER, PRINCIPAL, TABLE_OBJ, - Collections.<Map<String,String>>emptyList(), 0L, Collections.singletonList(partLocation)); - Mockito.when(deserializer.getDropPartitionMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - assertEquals(Collections.singleton(PATH), update.get(AUTH)); - } - - /** - * Test alter partition event. It should change partition location - * @throws Exception - */ - @Test - public void testAlterPartition() throws Exception { - String partPath = "hello/world"; - String partLocation = uri(partPath); - - String newPath = "better/world"; - String newLocation = uri(newPath); - - Map<String, Collection<String>> update = new HashMap<>(); - Set<String> locations = new HashSet<>(); - locations.add(PATH); - locations.add(partPath); - update.put(AUTH, locations); - - NotificationEvent event = new NotificationEvent(0, 0, ALTER_PARTITION.toString(), ""); - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - Partition partitionObjBefore = new Partition(null, DB, TABLE, 0, 0, buildStorageDescriptor(partLocation), null); - Partition partitionObjAfter = new Partition(null, DB, TABLE, 0, 0, buildStorageDescriptor(newLocation), null); - - SentryJSONAlterPartitionMessage message = - new SentryJSONAlterPartitionMessage(SERVER, PRINCIPAL, TABLE_OBJ, - partitionObjBefore, partitionObjAfter, 0L); - - Mockito.when(deserializer.getAlterPartitionMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - - Set<String> expected = new HashSet<>(2); - expected.add(PATH); - expected.add(newPath); - assertEquals(expected, update.get(AUTH)); - } - - /** - * Test alter table event that changes database name when there are no tables. - * @throws Exception - */ - @Test - public void testAlterTableChangeDbNameNoTables() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - String newDbName = "Db2"; - - NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), ""); - event.setDbName(newDbName); - event.setTableName(TABLE); - - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONAlterTableMessage message = - new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, TABLE_OBJ, TABLE_OBJ, 0L); - - Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - assertEquals(Collections.singleton(PATH), update.get(newDbName.toLowerCase())); - assertFalse(update.containsKey(DB.toLowerCase())); - } - - @Test - /** - * Test alter table event that changes database name when there are tables. - * All entries like "dbName.tableName" should have dbName changed to the new name. - * @throws Exception - */ - public void testAlterTableChangeDbNameWithTables() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - Set<String> locations = new HashSet<>(1); - locations.add(PATH); - update.put(AUTH, locations); - - String newDbName = "Db2"; - String newAuth = newDbName.toLowerCase() + "." + TABLE.toLowerCase(); - - NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), ""); - event.setDbName(newDbName); - event.setTableName(TABLE); - - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONAlterTableMessage message = - new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, TABLE_OBJ, TABLE_OBJ, 0L); - - Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Map<String, Set<String>> expected = new HashMap<>(2); - expected.put(newDbName.toLowerCase(), Collections.singleton(PATH)); - expected.put(newAuth, Collections.singleton(PATH)); - assertEquals(expected, update); - } - - /** - * Test alter table event that changes table name. - * @throws Exception - */ - @Test - public void testAlterTableChangeTableName() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - Set<String> locations = new HashSet<>(1); - locations.add(PATH); - update.put(AUTH, locations); - - String newTableName = "Table2"; - String newAuth = DB.toLowerCase() + "." + newTableName.toLowerCase(); - - NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), ""); - event.setDbName(DB); - event.setTableName(newTableName); - - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - SentryJSONAlterTableMessage message = - new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, TABLE_OBJ, TABLE_OBJ, 0L); - - Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Map<String, Set<String>> expected = new HashMap<>(2); - expected.put(DB.toLowerCase(), Collections.singleton(PATH)); - expected.put(newAuth, Collections.singleton(PATH)); - assertEquals(expected, update); - } - - /** - * Test alter table event that changes object location. - * @throws Exception - */ - @Test - public void testAlterTableChangeLocation() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put(DB.toLowerCase(), Collections.singleton(PATH)); - Set<String> locations = new HashSet<>(1); - locations.add(PATH); - update.put(AUTH, locations); - - NotificationEvent event = new NotificationEvent(0, 0, ALTER_TABLE.toString(), ""); - event.setDbName(DB); - event.setTableName(TABLE); - - String newPath = "hello/world"; - String newLocation = uri(newPath); - - MessageDeserializer deserializer = Mockito.mock(SentryJSONMessageDeserializer.class); - - Table tableWithNewLocation = buildTable(DB, TABLE, newLocation); - SentryJSONAlterTableMessage message = - new SentryJSONAlterTableMessage(SERVER, PRINCIPAL, TABLE_OBJ, tableWithNewLocation, 0L); - - Mockito.when(deserializer.getAlterTableMessage("")).thenReturn(message); - FullUpdateModifier.applyEvent(update, event, deserializer); - Map<String, Set<String>> expected = new HashMap<>(2); - expected.put(DB.toLowerCase(), Collections.singleton(PATH)); - expected.put(AUTH.toLowerCase(), Collections.singleton(newPath)); - assertEquals(expected, update); - } - - /** - * Test renamePrefixKeys function. - * We ask to rename "foo.bar" key to "foo.baz" key. - * @throws Exception - */ - @Test - public void testRenamePrefixKeys() throws Exception { - String oldKey = "foo."; - String newKey = "baz."; - String postfix = "bar"; - Map<String, Collection<String>> update = new HashMap<>(); - update.put(oldKey + postfix , Collections.<String>emptySet()); - FullUpdateModifier.renamePrefixKeys(update, oldKey, newKey); - assertEquals(1, update.size()); - assertTrue(update.containsKey(newKey + postfix)); - } - - /** - * Test renamePostfixKeys and RenamePrefixKeys functions mwhen the destination keys exist. - * Should nto change anything. - * We ask to rename "foo.bar" key to "baz.bar" key. - * @throws Exception - */ - @Test - public void testRenameKeysWithConflicts() throws Exception { - Map<String, Collection<String>> update = new HashMap<>(); - update.put("foo.bar", Collections.<String>emptySet()); - update.put("baz.bar", Collections.<String>emptySet()); - Map<String, Collection<String>> expected = new HashMap<>(update); - - FullUpdateModifier.renamePrefixKeys(update, "foo.", "baz."); - assertEquals(update, expected); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestHiveNotificationFetcher.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestHiveNotificationFetcher.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestHiveNotificationFetcher.java deleted file mode 100644 index 83a1bec..0000000 --- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestHiveNotificationFetcher.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - 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 - <p> - http://www.apache.org/licenses/LICENSE-2.0 - <p> - 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.sentry.service.thrift; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.apache.hadoop.hive.metastore.HiveMetaStoreClient; -import org.apache.hadoop.hive.metastore.IMetaStoreClient.NotificationFilter; -import org.apache.hadoop.hive.metastore.api.NotificationEvent; -import org.apache.hadoop.hive.metastore.api.NotificationEventResponse; -import org.apache.sentry.hdfs.UniquePathsUpdate; -import org.apache.sentry.provider.db.service.persistent.SentryStore; -import org.junit.Test; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -public class TestHiveNotificationFetcher { - @Test - public void testGetEmptyNotificationsWhenHmsReturnsANullResponse() throws Exception { - SentryStore store = Mockito.mock(SentryStore.class); - HiveConnectionFactory hmsConnection = Mockito.mock(HiveConnectionFactory.class); - HiveMetaStoreClient hmsClient = Mockito.mock(HiveMetaStoreClient.class); - - Mockito.when(hmsConnection.connect()).thenReturn(new HMSClient(hmsClient)); - - try (HiveNotificationFetcher fetcher = new HiveNotificationFetcher(store, hmsConnection)) { - List<NotificationEvent> events; - - Mockito.when(hmsClient.getNextNotification(0, Integer.MAX_VALUE, null)) - .thenReturn(null); - - events = fetcher.fetchNotifications(0); - assertTrue(events.isEmpty()); - } - } - - @Test - public void testGetEmptyNotificationsWhenHmsReturnsEmptyEvents() throws Exception { - SentryStore store = Mockito.mock(SentryStore.class); - HiveConnectionFactory hmsConnection = Mockito.mock(HiveConnectionFactory.class); - HiveMetaStoreClient hmsClient = Mockito.mock(HiveMetaStoreClient.class); - - Mockito.when(hmsConnection.connect()).thenReturn(new HMSClient(hmsClient)); - - try (HiveNotificationFetcher fetcher = new HiveNotificationFetcher(store, hmsConnection)) { - List<NotificationEvent> events; - - Mockito.when(hmsClient.getNextNotification(0, Integer.MAX_VALUE, null)) - .thenReturn(new NotificationEventResponse(Collections.<NotificationEvent>emptyList())); - - events = fetcher.fetchNotifications(0); - assertTrue(events.isEmpty()); - } - } - - @Test - public void testGetAllNotificationsReturnedByHms() throws Exception { - SentryStore store = Mockito.mock(SentryStore.class); - HiveConnectionFactory hmsConnection = Mockito.mock(HiveConnectionFactory.class); - HiveMetaStoreClient hmsClient = Mockito.mock(HiveMetaStoreClient.class); - - Mockito.when(hmsConnection.connect()).thenReturn(new HMSClient(hmsClient)); - - try (HiveNotificationFetcher fetcher = new HiveNotificationFetcher(store, hmsConnection)) { - List<NotificationEvent> events; - - Mockito.when(hmsClient.getNextNotification(0, Integer.MAX_VALUE, null)) - .thenReturn(new NotificationEventResponse( - Arrays.<NotificationEvent>asList( - new NotificationEvent(1L, 0, "CREATE_DATABASE", ""), - new NotificationEvent(2L, 0, "CREATE_TABLE", "") - ) - )); - - events = fetcher.fetchNotifications(0); - assertEquals(2, events.size()); - assertEquals(1, events.get(0).getEventId()); - assertEquals("CREATE_DATABASE", events.get(0).getEventType()); - assertEquals(2, events.get(1).getEventId()); - assertEquals("CREATE_TABLE", events.get(1).getEventType()); - } - } - - @Test - public void testGetDuplicatedEventsAndFilterEventsAlreadySeen() throws Exception { - final SentryStore store = Mockito.mock(SentryStore.class); - HiveConnectionFactory hmsConnection = Mockito.mock(HiveConnectionFactory.class); - HiveMetaStoreClient hmsClient = Mockito.mock(HiveMetaStoreClient.class); - - Mockito.when(hmsConnection.connect()).thenReturn(new HMSClient(hmsClient)); - - try (HiveNotificationFetcher fetcher = new HiveNotificationFetcher(store, hmsConnection)) { - List<NotificationEvent> events; - - /* - * Requesting an ID > 0 will request all notifications from 0 again but filter those - * already seen notifications with ID = 1 - */ - - // This mock will also test that the NotificationFilter works as expected - Mockito.when(hmsClient.getNextNotification(Mockito.eq(0L), Mockito.eq(Integer.MAX_VALUE), - (NotificationFilter) Mockito.notNull())).thenAnswer(new Answer<NotificationEventResponse>() { - @Override - public NotificationEventResponse answer(InvocationOnMock invocation) - throws Throwable { - NotificationFilter filter = (NotificationFilter) invocation.getArguments()[2]; - NotificationEventResponse response = new NotificationEventResponse(); - - List<NotificationEvent> events = Arrays.<NotificationEvent>asList( - new NotificationEvent(1L, 0, "CREATE_DATABASE", ""), - new NotificationEvent(1L, 0, "CREATE_TABLE", ""), - new NotificationEvent(2L, 0, "ALTER_TABLE", "") - ); - - for (NotificationEvent event : events) { - String hash = UniquePathsUpdate.sha1(event); - - // We simulate that CREATE_DATABASE is already processed - if (event.getEventType().equals("CREATE_DATABASE")) { - Mockito.when(store.isNotificationProcessed(Mockito.eq(hash))).thenReturn(true); - } else { - Mockito.when(store.isNotificationProcessed(Mockito.eq(hash))).thenReturn(false); - } - - if (filter.accept(event)) { - response.addToEvents(event); - } - } - - return response; - } - }); - - events = fetcher.fetchNotifications(1); - assertEquals(2, events.size()); - assertEquals(1, events.get(0).getEventId()); - assertEquals("CREATE_TABLE", events.get(0).getEventType()); - assertEquals(2, events.get(1).getEventId()); - assertEquals("ALTER_TABLE", events.get(1).getEventType()); - } - } -} http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHMSClient.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHMSClient.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHMSClient.java deleted file mode 100644 index 38668ca..0000000 --- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryHMSClient.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * 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.sentry.service.thrift; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hive.metastore.HiveMetaStoreClient; -import org.apache.hadoop.hive.metastore.api.CurrentNotificationEventId; -import org.apache.hadoop.hive.metastore.api.Database; -import org.apache.hadoop.hive.metastore.api.MetaException; -import org.apache.hadoop.hive.metastore.api.Partition; -import org.apache.hadoop.hive.metastore.api.StorageDescriptor; -import org.apache.hadoop.hive.metastore.api.Table; -import org.apache.sentry.provider.db.service.persistent.PathsImage; -import org.apache.thrift.TException; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import javax.security.auth.login.LoginException; - -/** - * Test mocks HiveMetaStoreClient class and tests SentryHMSClient. - */ -public class TestSentryHMSClient { - - private static final Configuration conf = new Configuration(); - private static SentryHMSClient client; - private static MockHMSClientFactory hiveConnectionFactory; - - /** - * Create mock database with the given name - * - * @param name Database name - * @return Mock database object - */ - private static Database makeDb(String name) { - Database db = Mockito.mock(Database.class); - Mockito.when(db.getName()).thenReturn(name); - Mockito.when(db.getLocationUri()).thenReturn("hdfs:///" + name); - return db; - } - - /** - * Create mock table - * - * @param dbName db for this table - * @param tableName name of the table - * @return mock table object - */ - private static Table makeTable(String dbName, String tableName) { - Table table = Mockito.mock(Table.class); - Mockito.when(table.getDbName()).thenReturn(dbName); - Mockito.when(table.getTableName()).thenReturn(tableName); - StorageDescriptor sd = Mockito.mock(StorageDescriptor.class); - Mockito.when(sd.getLocation()).thenReturn( - String.format("hdfs:///%s/%s", dbName, tableName)); - Mockito.when(table.getSd()).thenReturn(sd); - return table; - } - - /** - * Create mock partition - * - * @param dbName database for this partition - * @param tableName table for this partition - * @param partName partition name - * @return mock partition object - */ - private static Partition makePartition(String dbName, String tableName, String partName) { - Partition partition = Mockito.mock(Partition.class); - StorageDescriptor sd = Mockito.mock(StorageDescriptor.class); - Mockito.when(sd.getLocation()).thenReturn( - String.format("hdfs:///%s/%s/%s", dbName, tableName, partName)); - Mockito.when(partition.getSd()).thenReturn(sd); - return partition; - } - - @BeforeClass - static public void initialize() throws IOException, LoginException { - hiveConnectionFactory = new MockHMSClientFactory(); - client = new SentryHMSClient(conf, (HiveConnectionFactory)hiveConnectionFactory); - } - - /** - * Creating snapshot when SentryHMSClient is not connected to HMS - */ - @Test - public void testSnapshotCreationWithOutClientConnected() throws Exception { - // Make sure that client is not connected - Assert.assertFalse(client.isConnected()); - PathsImage snapshotInfo = client.getFullSnapshot(); - Assert.assertTrue(snapshotInfo.getPathImage().isEmpty()); - } - - /** - * Creating snapshot when HMS doesn't have any data - */ - @Test - public void testSnapshotCreationWithNoHmsData() throws Exception { - MockClient mockClient = new MockClient(new HiveSnapshot(), 1); - client.setClient(mockClient.client); - // Make sure that client is connected - Assert.assertTrue(client.isConnected()); - PathsImage snapshotInfo = client.getFullSnapshot(); - Assert.assertTrue(snapshotInfo.getPathImage().isEmpty()); - } - - /** - * Creating a snapshot when there is data but there are updates to HMS data mean while - */ - @Test - public void testSnapshotCreationWhenDataIsActivelyUpdated() throws Exception { - HiveTable tab21 = new HiveTable("tab21"); - HiveTable tab31 = new HiveTable("tab31").add("part311").add("part312"); - HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); - HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); - HiveDb db1 = new HiveDb("db1"); - HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); - final MockClient mockClient = new MockClient(snap, 1); - - client.setClient(mockClient.client); - hiveConnectionFactory.setClient(mockClient); - // Make sure that client is connected - Assert.assertTrue(client.isConnected()); - PathsImage snapshotInfo = client.getFullSnapshot(); - // Make sure that snapshot is not empty - Assert.assertTrue(!snapshotInfo.getPathImage().isEmpty()); - - Mockito.when(mockClient.client.getCurrentNotificationEventId()). - thenAnswer(new Answer<CurrentNotificationEventId>() { - @Override - public CurrentNotificationEventId answer(InvocationOnMock invocation) - throws Throwable { - return new CurrentNotificationEventId(mockClient.incrementNotificationEventId()); - } - - }); - - snapshotInfo = client.getFullSnapshot(); - Assert.assertTrue(snapshotInfo.getPathImage().isEmpty()); - } - - /** - * Creating a snapshot when there is data in HMS. - */ - @Test - public void testSnapshotCreationSuccess() throws Exception { - HiveTable tab21 = new HiveTable("tab21"); - HiveTable tab31 = new HiveTable("tab31"); - HiveDb db3 = new HiveDb("db3", Lists.newArrayList(tab31)); - HiveDb db2 = new HiveDb("db2", Lists.newArrayList(tab21)); - HiveDb db1 = new HiveDb("db1"); - HiveSnapshot snap = new HiveSnapshot().add(db1).add(db2).add(db3); - MockClient mockClient = new MockClient(snap, 1); - Mockito.when(mockClient.client.getCurrentNotificationEventId()). - thenReturn(new CurrentNotificationEventId(mockClient.eventId)); - client.setClient(mockClient.client); - hiveConnectionFactory.setClient(mockClient); - // Make sure that client is connected - Assert.assertTrue(client.isConnected()); - - PathsImage snapshotInfo = client.getFullSnapshot(); - Assert.assertEquals(5, snapshotInfo.getPathImage().size()); - Assert.assertEquals(Sets.newHashSet("db1"), snapshotInfo.getPathImage().get("db1")); - Assert.assertEquals(Sets.newHashSet("db2"), snapshotInfo.getPathImage().get("db2")); - Assert.assertEquals(Sets.newHashSet("db3"), snapshotInfo.getPathImage().get("db3")); - Assert.assertEquals(Sets.newHashSet("db2/tab21"), - snapshotInfo.getPathImage().get("db2.tab21")); - Assert.assertEquals(Sets.newHashSet("db3/tab31"), snapshotInfo.getPathImage().get("db3.tab31")); - Assert.assertEquals(snapshotInfo.getId(), mockClient.eventId); - - } - - /** - * Representation of a Hive table. A table has a name and a list of partitions. - */ - private static class HiveTable { - - private final String name; - private final List<String> partitions; - - HiveTable(String name) { - this.name = name; - this.partitions = new ArrayList<>(); - } - - HiveTable add(String partition) { - partitions.add(partition); - return this; - } - } - - /** - * Representation of a Hive database. A database has a name and a list of tables - */ - private static class HiveDb { - - final String name; - Collection<HiveTable> tables; - - @SuppressWarnings("SameParameterValue") - HiveDb(String name) { - this.name = name; - tables = new ArrayList<>(); - } - - HiveDb(String name, Collection<HiveTable> tables) { - this.name = name; - this.tables = tables; - if (this.tables == null) { - this.tables = new ArrayList<>(); - } - } - - void add(HiveTable table) { - this.tables.add(table); - } - } - - /** - * Representation of a full Hive snapshot. A snapshot is collection of databases - */ - private static class HiveSnapshot { - - final List<HiveDb> databases = new ArrayList<>(); - - HiveSnapshot() { - } - - HiveSnapshot(Collection<HiveDb> dblist) { - if (dblist != null) { - databases.addAll(dblist); - } - } - - HiveSnapshot add(HiveDb db) { - this.databases.add(db); - return this; - } - } - - /** - * Mock for HMSClientFactory - */ - private static class MockHMSClientFactory implements HiveConnectionFactory { - - private HiveMetaStoreClient mClient; - - public MockHMSClientFactory() { - mClient = null; - } - - void setClient(MockClient mockClient) { - this.mClient = mockClient.client; - } - @Override - public HMSClient connect() throws IOException, InterruptedException, MetaException { - return new HMSClient(mClient); - } - - @Override - public void close() throws Exception { - } - } - - /** - * Convert Hive snapshot to mock client that will return proper values - * for the snapshot. - */ - private static class MockClient { - - public HiveMetaStoreClient client; - public long eventId; - - MockClient(HiveSnapshot snapshot, long eventId) throws TException { - this.eventId = eventId; - client = Mockito.mock(HiveMetaStoreClient.class); - List<String> dbNames = new ArrayList<>(snapshot.databases.size()); - // Walk over all databases and mock appropriate objects - for (HiveDb mdb : snapshot.databases) { - String dbName = mdb.name; - dbNames.add(dbName); - Database db = makeDb(dbName); - Mockito.when(client.getDatabase(dbName)).thenReturn(db); - List<String> tableNames = new ArrayList<>(mdb.tables.size()); - // Walk over all tables for the database and mock appropriate objects - for (HiveTable table : mdb.tables) { - String tableName = table.name; - tableNames.add(tableName); - Table mockTable = makeTable(dbName, tableName); - Mockito.when(client.getTableObjectsByName(dbName, - Lists.newArrayList(tableName))) - .thenReturn(Lists.newArrayList(mockTable)); - Mockito.when(client.listPartitionNames(dbName, tableName, (short) -1)) - .thenReturn(table.partitions); - // Walk across all partitions and mock appropriate objects - for (String partName : table.partitions) { - Partition p = makePartition(dbName, tableName, partName); - Mockito.when(client.getPartitionsByNames(dbName, tableName, - Lists.<String>newArrayList(partName))) - .thenReturn(Lists.<Partition>newArrayList(p)); - } - } - Mockito.when(client.getAllTables(dbName)).thenReturn(tableNames); - } - // Return all database names - Mockito.when(client.getAllDatabases()).thenReturn(dbNames); - Mockito.when(client.getCurrentNotificationEventId()). - thenReturn(new CurrentNotificationEventId(eventId)); - - } - - public Long incrementNotificationEventId() { - eventId = eventId + 1; - return eventId; - } - } -} http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryStateBank.java ---------------------------------------------------------------------- diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryStateBank.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryStateBank.java deleted file mode 100644 index 4f71e1c..0000000 --- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/service/thrift/TestSentryStateBank.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * 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.sentry.service.thrift; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.HashSet; -import org.junit.Before; -import org.junit.Test; - -/** - * - */ -public class TestSentryStateBank { - - @Before - public void setUp() { - SentryStateBank.clearAllStates(); - } - - @Test - public void testEnableState() { - SentryStateBank.enableState(TestState.COMPONENT, TestState.FIRST_STATE); - assertTrue("Expected FIRST_STATE to be enabled", - SentryStateBank.isEnabled(TestState.COMPONENT, TestState.FIRST_STATE)); - assertFalse("Expected SECOND_STATE to be disabled", - SentryStateBank.isEnabled(TestState.COMPONENT, TestState.SECOND_STATE)); - } - - @Test - public void testStatesGetDisabled() { - SentryStateBank.enableState(TestState.COMPONENT, TestState.FIRST_STATE); - assertTrue("Expected FIRST_STATE to be enabled", - SentryStateBank.isEnabled(TestState.COMPONENT, TestState.FIRST_STATE)); - SentryStateBank.disableState(TestState.COMPONENT, TestState.FIRST_STATE); - assertFalse("Expected FIRST_STATE to be disabled", - SentryStateBank.isEnabled(TestState.COMPONENT, TestState.FIRST_STATE)); - } - - @Test - public void testCheckMultipleStateCheckSuccess() { - SentryStateBank.enableState(TestState.COMPONENT, TestState.FIRST_STATE); - SentryStateBank.enableState(TestState.COMPONENT, TestState.SECOND_STATE); - - assertTrue("Expected both FIRST_STATE and SECOND_STATE to be enabled", - SentryStateBank.hasStatesEnabled(TestState.COMPONENT, new HashSet<SentryState>( - Arrays.asList(TestState.FIRST_STATE, TestState.SECOND_STATE)))); - } - - @Test - public void testCheckMultipleStateCheckFailure() { - SentryStateBank.enableState(TestState.COMPONENT, TestState.FIRST_STATE); - assertFalse("Expected only FIRST_STATE to be enabled", - SentryStateBank.hasStatesEnabled(TestState.COMPONENT, new HashSet<SentryState>( - Arrays.asList(TestState.FIRST_STATE, TestState.SECOND_STATE)))); - } - - - public enum TestState implements SentryState { - FIRST_STATE, - SECOND_STATE; - - public static final String COMPONENT = "TestState"; - - @Override - public long getValue() { - return 1 << this.ordinal(); - } - } -} http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-service/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-service/pom.xml b/sentry-service/pom.xml index 0388476..b63467b 100644 --- a/sentry-service/pom.xml +++ b/sentry-service/pom.xml @@ -31,6 +31,8 @@ limitations under the License. <modules> <module>sentry-service-api</module> + <module>sentry-service-server</module> + <module>sentry-service-client</module> </modules> </project> http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-service/sentry-service-client/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/pom.xml b/sentry-service/sentry-service-client/pom.xml new file mode 100644 index 0000000..a1ae8c8 --- /dev/null +++ b/sentry-service/sentry-service-client/pom.xml @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<!-- +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. +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-service</artifactId> + <version>2.1.0-SNAPSHOT</version> + </parent> + + <artifactId>sentry-service-client</artifactId> + <name>Sentry Service Client</name> + + <dependencies> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> + <groupId>org.apache.derby</groupId> + <artifactId>derby</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + <dependency> + <groupId>org.apache.thrift</groupId> + <artifactId>libfb303</artifactId> + <version>${libfb303.version}</version> + </dependency> + <dependency> + <groupId>org.apache.thrift</groupId> + <artifactId>libthrift</artifactId> + <version>${libthrift.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-service-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java new file mode 100644 index 0000000..d146a0d --- /dev/null +++ b/sentry-service/sentry-service-client/src/main/java/org/apache/sentry/service/thrift/SentryServiceClientFactory.java @@ -0,0 +1,111 @@ +/** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.sentry.service.thrift; + +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.core.common.transport.RetryClientInvocationHandler; +import org.apache.sentry.core.common.transport.SentryPolicyClientTransportConfig; +import org.apache.sentry.core.common.transport.SentryTransportFactory; +import org.apache.sentry.core.common.transport.SentryTransportPool; +import org.apache.sentry.api.service.thrift.SentryPolicyServiceClient; +import org.apache.sentry.api.service.thrift.SentryPolicyServiceClientDefaultImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.concurrent.ThreadSafe; +import java.lang.reflect.Proxy; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Client factory for Hive clients. The factory uses connection pooling. + */ +@ThreadSafe +public final class SentryServiceClientFactory { + private static final Logger LOGGER = LoggerFactory.getLogger(SentryServiceClientFactory.class); + + private static final SentryPolicyClientTransportConfig transportConfig = + new SentryPolicyClientTransportConfig(); + private final Configuration conf; + private final SentryTransportPool transportPool; + + /** Keep track of singleton instances */ + private static final AtomicReference<SentryServiceClientFactory> clientFactory = + new AtomicReference<>(); + + /** + * Create a client instance. The supplied configuration is only used the first time and + * ignored afterwords. Tests that want to supply different configurations + * should call {@link #factoryReset(SentryServiceClientFactory)} to force new configuration + * read. + * @param conf Configuration + * @return client instance + * @throws Exception + */ + public static SentryPolicyServiceClient create(Configuration conf) throws Exception { + SentryServiceClientFactory factory = clientFactory.get(); + if (factory != null) { + return factory.create(); + } + factory = new SentryServiceClientFactory(conf); + boolean ok = clientFactory.compareAndSet(null, factory); + if (ok) { + return factory.create(); + } + // Close old factory + factory.close(); + return clientFactory.get().create(); + } + + /** + * Create a new instance of the factory which will hand hand off connections from + * the pool. + * @param conf Configuration object + */ + private SentryServiceClientFactory(Configuration conf) { + this.conf = conf; + + transportPool = new SentryTransportPool(conf, transportConfig, + new SentryTransportFactory(conf, transportConfig)); + } + + private SentryPolicyServiceClient create() throws Exception { + return (SentryPolicyServiceClient) Proxy + .newProxyInstance(SentryPolicyServiceClientDefaultImpl.class.getClassLoader(), + SentryPolicyServiceClientDefaultImpl.class.getInterfaces(), + new RetryClientInvocationHandler(conf, + new SentryPolicyServiceClientDefaultImpl(conf, transportPool), transportConfig)); + } + + /** + * Reset existing factory and return the old one. + * Only used by tests. + */ + public static SentryServiceClientFactory factoryReset(SentryServiceClientFactory factory) { + LOGGER.debug("factory reset"); + return clientFactory.getAndSet(factory); + } + + public void close() { + try { + transportPool.close(); + } catch (Exception e) { + LOGGER.error("failed to close transport pool", e); + } + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/b97f5c7a/sentry-service/sentry-service-server/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/pom.xml b/sentry-service/sentry-service-server/pom.xml new file mode 100644 index 0000000..a103c1e --- /dev/null +++ b/sentry-service/sentry-service-server/pom.xml @@ -0,0 +1,321 @@ +<?xml version="1.0"?> +<!-- +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. +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-service</artifactId> + <version>2.1.0-SNAPSHOT</version> + </parent> + + <artifactId>sentry-service-server</artifactId> + <name>Sentry Service Server</name> + + <dependencies> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> + <groupId>org.apache.derby</groupId> + <artifactId>derby</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + <dependency> + <groupId>org.apache.thrift</groupId> + <artifactId>libfb303</artifactId> + <version>${libfb303.version}</version> + </dependency> + <dependency> + <groupId>org.apache.thrift</groupId> + <artifactId>libthrift</artifactId> + <version>${libthrift.version}</version> + </dependency> + <dependency> + <groupId>org.apache.curator</groupId> + <artifactId>curator-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-service-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-provider-file</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-service-client</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-model-db</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-model-kafka</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-model-solr</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-model-sqoop</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-hdfs-common</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-binding-hive-follower</artifactId> + <version>${project.version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.hive</groupId> + <artifactId>hive-exec</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.datanucleus</groupId> + <artifactId>datanucleus-core</artifactId> + <version>${datanucleus-core.version}</version> + </dependency> + <dependency> + <groupId>org.datanucleus</groupId> + <artifactId>datanucleus-api-jdo</artifactId> + <version>${datanucleus-api-jdo.version}</version> + </dependency> + <dependency> + <groupId>org.datanucleus</groupId> + <artifactId>datanucleus-rdbms</artifactId> + <version>${datanucleus-rdbms.version}</version> + </dependency> + <dependency> + <groupId>org.datanucleus</groupId> + <artifactId>javax.jdo</artifactId> + <version>${datanucleus-jdo.version}</version> + </dependency> + <dependency> + <groupId>org.apache.hive</groupId> + <artifactId>hive-metastore</artifactId> + <version>${hive.version}</version> + <exclusions> + <!-- This dependency needs to be excluded to avoid compilation errors in the Eclipse build. + Without this change, the Eclipse build classpath contains this jar file ahead of + datanucleus javax.jdo*.jar. This error can not be reproduced with the maven build. + Cause of compilation error : PersistenceManager class provided as part of this version, + does not implement java.lang.AutoClosable interface. This breaks the usage of + PersistenceManager inside try-with-resources clause in Sentry TransactionManager class. + --> + <exclusion> + <groupId>javax.jdo</groupId> + <artifactId>jdo-api</artifactId> + </exclusion> + <exclusion> + <groupId>javax.jdo</groupId> + <artifactId>jdo2-api</artifactId> + </exclusion> + <exclusion> + <groupId>javax.jdo</groupId> + <artifactId>jdo2-api-legacy</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-core-asl</artifactId> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-mapper-asl</artifactId> + </dependency> + <dependency> + <groupId>io.dropwizard.metrics</groupId> + <artifactId>metrics-core</artifactId> + <version>${metrics.version}</version> + </dependency> + <dependency> + <groupId>io.dropwizard.metrics</groupId> + <artifactId>metrics-servlets</artifactId> + <version>${metrics.version}</version> + </dependency> + <dependency> + <groupId>io.dropwizard.metrics</groupId> + <artifactId>metrics-jvm</artifactId> + <version>${metrics.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-minikdc</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <sourceDirectory>${basedir}/src/main/java</sourceDirectory> + <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory> + <resources> + <resource> + <directory>../../sentry-service/sentry-service-server/src/main/java/org/apache/sentry/provider/db/service/model</directory> + <includes> + <include>package.jdo</include> + </includes> + </resource> + <resource> + <directory>${basedir}/src/main</directory> + <includes> + <include>webapp/*</include> + <include>webapp/css/*</include> + </includes> + </resource> + </resources> + <plugins> + <plugin> + <groupId>com.google.code.maven-replacer-plugin</groupId> + <artifactId>replacer</artifactId> + <version>1.5.2</version> + <executions> + <execution> + <id>replaceTokens</id> + <phase>clean</phase> + <goals> + <goal>replace</goal> + </goals> + </execution> + </executions> + <configuration> + <file>${basedir}/src/main/webapp/SentryService.html</file> + <replacements> + <replacement> + <token>%PROJECT_VERSION%</token> + <value>${version}</value> + </replacement> + </replacements> + </configuration> + </plugin> + <plugin> + <groupId>org.datanucleus</groupId> + <artifactId>datanucleus-maven-plugin</artifactId> + <configuration> + <api>JDO</api> + <metadataIncludes>**/*.jdo</metadataIncludes> + <verbose>true</verbose> + </configuration> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>enhance</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>${maven.shade.plugin.version}</version> + <executions> + <execution> + <id>curator-shade</id> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <artifactSet> + <includes> + <!-- This is needed to support projects running on different versions of curator --> + <include>org.apache.curator:curator-recipes</include> + <include>org.apache.curator:curator-x-discovery</include> + <include>org.apache.curator:curator-framework</include> + <include>org.apache.curator:curator-client</include> + </includes> + </artifactSet> + <relocations> + <!-- Adding prefix to the package to make it unique --> + <relocation> + <pattern>org.apache.curator</pattern> + <shadedPattern>sentry.org.apache.curator</shadedPattern> + </relocation> + </relocations> + <shadedArtifactAttached>false</shadedArtifactAttached> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <reuseForks>false</reuseForks> + </configuration> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file
