Java tutorial
/* * 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. * * Copyright 2013 Near Infinity Corporation. */ package integrationtests; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import com.google.common.primitives.Longs; import com.nearinfinity.honeycomb.mysql.HandlerProxy; import com.nearinfinity.honeycomb.mysql.HandlerProxyFactory; import com.nearinfinity.honeycomb.mysql.QueryKey; import com.nearinfinity.honeycomb.mysql.Row; import com.nearinfinity.honeycomb.mysql.gen.QueryType; import com.nearinfinity.honeycomb.mysql.schema.IndexSchema; import com.nearinfinity.honeycomb.mysql.schema.TableSchema; import com.nearinfinity.honeycomb.util.Verify; /** * Integration Test utility methods */ public class ITUtils { /** * Encodes the provided value as a {@link ByteBuffer} * * @param value The value to encode * @return The buffer representing the provided value */ public static ByteBuffer encodeValue(final long value) { return ByteBuffer.wrap(Longs.toByteArray(value)); } /** * Asserts that the rows returned from a table scan performed by the proxy are different * * @param proxy The proxy to use for invoking a scan, not null * @param rowCount The valid number of rows expected to be returned from the scan */ public static void assertReceivingDifferentRows(final HandlerProxy proxy, final int rowCount) { checkNotNull(proxy); verifyRowCount(rowCount); proxy.startTableScan(); assertDifferentRows(proxy, rowCount); proxy.endScan(); } /** * Asserts that the rows returned from an index scan performed by the proxy are different * * @param proxy The proxy to use for invoking a scan, not null * @param key The index used to run an index scan, not null * @param rowCount The valid number of rows expected to be returned from the scan */ public static void assertReceivingDifferentRows(final HandlerProxy proxy, final QueryKey key, final int rowCount) { checkNotNull(proxy); checkNotNull(key); verifyRowCount(rowCount); proxy.startIndexScan(key.serialize()); assertDifferentRows(proxy, rowCount); proxy.endScan(); } /** * Inserts the number of specified rows with a random {@link UUID} * * @param proxy * @param rowCount * @param keyColumnValue * @see #insertData(HandlerProxy, int, long, UUID) */ public static void insertData(final HandlerProxy proxy, final int rowCount, final long keyColumnValue) { insertData(proxy, rowCount, keyColumnValue, UUID.randomUUID()); } /** * Inserts the number of specified rows with each row containing a column, * {@link TestConstants#COLUMN1}, with the value provided and another column, * {@link TestConstants#COLUMN2}, with an arbitrary value * * @param proxy The proxy to use for invoking a scan, not null * @param rowCount The valid number of rows expected to be returned from the scan * @param keyColumnValue The value stored in the first column, which is intended to be indexed * @param uuid The unique identifier to associate with each row inserted, not null */ public static void insertData(final HandlerProxy proxy, final int rowCount, final long keyColumnValue, final UUID uuid) { checkNotNull(proxy); verifyRowCount(rowCount); checkNotNull(uuid); final Map<String, ByteBuffer> map = Maps.newHashMap(); map.put(TestConstants.COLUMN1, encodeValue(keyColumnValue)); for (int x = 0; x < rowCount; x++) { map.put(TestConstants.COLUMN2, encodeValue(x)); final Row row = new Row(map, uuid); proxy.insertRow(row.serialize()); } proxy.flush(); } /** * Inserts the number of specified rows with each row containing the column, * {@link TestConstants#COLUMN2}, with an arbitrary value and random {@link UUID}. * * @param proxy * @param rows * @see #insertNullData(HandlerProxy, int, String) */ public static void insertNullData(HandlerProxy proxy, int rows) { insertNullData(proxy, rows, TestConstants.COLUMN2); } /** * Inserts the number of specified rows with each row containing the provided * column name with an arbitrary value and random {@link UUID}. The rows created * do not use columns used for indexing such as {@link TestConstants#COLUMN1} and * {@link TestConstants#COLUMN2} * * @param proxy The proxy to use for invoking a scan, not null * @param rowCount The valid number of rows expected to be returned from the scan * @param columnName The column name to associated with the inserted row, not null or empty */ public static void insertNullData(final HandlerProxy proxy, final int rowCount, final String columnName) { checkNotNull(proxy); verifyRowCount(rowCount); Verify.isNotNullOrEmpty(columnName); final Map<String, ByteBuffer> map = Maps.newHashMap(); for (int x = 0; x < rowCount; x++) { map.put(columnName, encodeValue(x)); final Row row = new Row(map, UUID.randomUUID()); proxy.insertRow(row.serialize()); } proxy.flush(); } /** * Creates an index on {@link TestConstants#COLUMN1} for the provided value * with the index name of {@link TestConstants#INDEX1} * * @param keyValue The value stored in the indexed column * @param queryType The query type that this index will be used for * @return The constructed index with the provided details */ public static QueryKey createKey(final int keyValue, final QueryType queryType) { HashMap<String, ByteBuffer> keys = Maps.newHashMap(); keys.put(TestConstants.COLUMN1, encodeValue(keyValue)); return new QueryKey(TestConstants.INDEX1, queryType, keys); } public static Row createRow(final int columnValue) { final Map<String, ByteBuffer> map = Maps.newHashMap(); map.put(TestConstants.COLUMN1, encodeValue(columnValue)); return new Row(map, UUID.randomUUID()); } private static void assertDifferentRows(final HandlerProxy proxy, final int rowCount) { byte[] previous = null; for (int x = 0; x < rowCount; x++) { final byte[] current = proxy.getNextRow(); assertNotNull(current); assertThat(current, not(equalTo(previous))); previous = current; } assertNull(proxy.getNextRow()); } private static void verifyRowCount(final long rowCount) { checkArgument(rowCount >= 0, "The provided row count is invalid"); } /** * Check that the table open on the proxy has the expected number of data rows * and index rows on each index (checks both ascending and descending * directions). Note: this could be very slow for big tables. * @param proxy HandlerProxy with table already open * @param schema TableSchema of open table * @param expectedRowCount Expected number of rows */ public static void assertRowCount(final HandlerProxy proxy, final TableSchema schema, final long expectedRowCount) { checkState(proxy.getTableName() != null, "Proxy must have an open table."); checkNotNull(schema); verifyRowCount(expectedRowCount); proxy.startTableScan(); assertResultCount(proxy, expectedRowCount); proxy.endScan(); QueryKey queryKey; for (IndexSchema indexSchema : schema.getIndices()) { queryKey = new QueryKey(indexSchema.getIndexName(), QueryType.INDEX_FIRST, ImmutableMap.<String, ByteBuffer>of()); proxy.startIndexScan(queryKey.serialize()); assertResultCount(proxy, expectedRowCount); proxy.endScan(); queryKey = new QueryKey(indexSchema.getIndexName(), QueryType.INDEX_LAST, ImmutableMap.<String, ByteBuffer>of()); proxy.startIndexScan(queryKey.serialize()); assertResultCount(proxy, expectedRowCount); proxy.endScan(); } } /** * Checks that the open scan on the proxy has the expected number of results. * @param proxy * @param expectedResultCount */ private static void assertResultCount(final HandlerProxy proxy, final long expectedResultCount) { long actualResultCount = 0; while (proxy.getNextRow() != null) { actualResultCount++; } assertEquals(expectedResultCount, actualResultCount); } /** * Start concurrency number of actions concurrently and at the same time. This * method ensures that the actions are started at the same time, and they are * not being blocked in a thread pool. Obviously, this uses concurrency * number of threads simultaneously so use caution as thread starvation * deadlock can occur with high concurrency levels. * * @param concurrency Number of handler proxies to run the action against, concurrently * @param setup Action to be run on each proxy before starting concurrent actions * @param action Action to be run concurrently and in sync with other proxies * @param cleanup Action to be run to cleanup proxy * @param factory HandlerProxyFactory to supply proxies to be run concurrently * @throws InterruptedException */ public static void startProxyActionConcurrently(int concurrency, final ProxyRunnable setup, final ProxyRunnable action, final ProxyRunnable cleanup, final HandlerProxyFactory factory) throws InterruptedException { final ExecutorService executor = Executors.newCachedThreadPool(); final CountDownLatch ready = new CountDownLatch(concurrency); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch done = new CountDownLatch(concurrency); for (int i = 0; i < concurrency; i++) { executor.execute(new Runnable() { @Override public void run() { HandlerProxy proxy = factory.createHandlerProxy(); setup.run(proxy); ready.countDown(); try { start.await(); action.run(proxy); } catch (InterruptedException e) { System.out.println("Interuppted"); Thread.currentThread().interrupt(); } cleanup.run(proxy); done.countDown(); } }); } ready.await(); start.countDown(); done.await(); } public static ProxyRunnable openTable = new ITUtils.ProxyRunnable() { @Override public void run(HandlerProxy proxy) { proxy.openTable(TestConstants.TABLE_NAME); } }; public static ITUtils.ProxyRunnable closeTable = new ITUtils.ProxyRunnable() { @Override public void run(HandlerProxy proxy) { proxy.closeTable(); } }; /** * Basically Runnable, but run takes a HandlerProxy which the action * may use. */ public interface ProxyRunnable { public void run(HandlerProxy proxy); } }