/* * Copyright (C) 2012 DataStax Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.netflix.staash.rest.util; import java.math.*; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.datastax.driver.core.*; /** * A number of static fields/methods handy for tests. */ public abstract class PaasUtils { private static final Logger logger = LoggerFactory.getLogger(PaasUtils.class); public static final String CREATE_KEYSPACE_SIMPLE_FORMAT = "CREATE KEYSPACE %s WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor' : %d }"; public static final String CREATE_KEYSPACE_GENERIC_FORMAT = "CREATE KEYSPACE %s WITH replication = { 'class' : '%s', %s }"; public static final String SIMPLE_KEYSPACE = "ks"; public static final String SIMPLE_TABLE = "test"; public static final String CREATE_TABLE_SIMPLE_FORMAT = "CREATE TABLE %s (k text PRIMARY KEY, t text, i int, f float)"; public static final String INSERT_FORMAT = "INSERT INTO %s (key, column1, value) VALUES ('%s', '%s', '%s')"; public static final String SELECT_ALL_FORMAT = "SELECT * FROM %s"; public static BoundStatement setBoundValue(BoundStatement bs, String name, DataType type, Object value) { switch (type.getName()) { case ASCII: bs.setString(name, (String)value); break; case BIGINT: bs.setLong(name, (Long)value); break; case BLOB: bs.setBytes(name, (ByteBuffer)value); break; case BOOLEAN: bs.setBool(name, (Boolean)value); break; case COUNTER: // Just a no-op, we shouldn't handle counters the same way than other types break; case DECIMAL: bs.setDecimal(name, (BigDecimal)value); break; case DOUBLE: bs.setDouble(name, (Double)value); break; case FLOAT: bs.setFloat(name, (Float)value); break; case INET: bs.setInet(name, (InetAddress)value); break; case INT: bs.setInt(name, (Integer)value); break; case TEXT: bs.setString(name, (String)value); break; case TIMESTAMP: bs.setDate(name, (Date)value); break; case UUID: bs.setUUID(name, (UUID)value); break; case VARCHAR: bs.setString(name, (String)value); break; case VARINT: bs.setVarint(name, (BigInteger)value); break; case TIMEUUID: bs.setUUID(name, (UUID)value); break; case LIST: bs.setList(name, (List)value); break; case SET: bs.setSet(name, (Set)value); break; case MAP: bs.setMap(name, (Map)value); break; default: throw new RuntimeException("Missing handling of " + type); } return bs; } public static Object getValue(Row row, String name, DataType type) { switch (type.getName()) { case ASCII: return row.getString(name); case BIGINT: return row.getLong(name); case BLOB: return row.getBytes(name); case BOOLEAN: return row.getBool(name); case COUNTER: return row.getLong(name); case DECIMAL: return row.getDecimal(name); case DOUBLE: return row.getDouble(name); case FLOAT: return row.getFloat(name); case INET: return row.getInet(name); case INT: return row.getInt(name); case TEXT: return row.getString(name); case TIMESTAMP: return row.getDate(name); case UUID: return row.getUUID(name); case VARCHAR: return row.getString(name); case VARINT: return row.getVarint(name); case TIMEUUID: return row.getUUID(name); case LIST: return row.getList(name, classOf(type.getTypeArguments().get(0))); case SET: return row.getSet(name, classOf(type.getTypeArguments().get(0))); case MAP: return row.getMap(name, classOf(type.getTypeArguments().get(0)), classOf(type.getTypeArguments().get(1))); } throw new RuntimeException("Missing handling of " + type); } private static Class classOf(DataType type) { assert !type.isCollection(); switch (type.getName()) { case ASCII: case TEXT: case VARCHAR: return String.class; case BIGINT: case COUNTER: return Long.class; case BLOB: return ByteBuffer.class; case BOOLEAN: return Boolean.class; case DECIMAL: return BigDecimal.class; case DOUBLE: return Double.class; case FLOAT: return Float.class; case INET: return InetAddress.class; case INT: return Integer.class; case TIMESTAMP: return Date.class; case UUID: case TIMEUUID: return UUID.class; case VARINT: return BigInteger.class; } throw new RuntimeException("Missing handling of " + type); } // Always return the "same" value for each type public static Object getFixedValue(final DataType type) { try { switch (type.getName()) { case ASCII: return "An ascii string"; case BIGINT: return 42L; case BLOB: return ByteBuffer.wrap(new byte[]{ (byte)4, (byte)12, (byte)1 }); case BOOLEAN: return true; case COUNTER: throw new UnsupportedOperationException("Cannot 'getSomeValue' for counters"); case DECIMAL: return new BigDecimal("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"); case DOUBLE: return 3.142519; case FLOAT: return 3.142519f; case INET: return InetAddress.getByAddress(new byte[]{(byte)127, (byte)0, (byte)0, (byte)1}); case INT: return 24; case TEXT: return "A text string"; case TIMESTAMP: return new Date(1352288289L); case UUID: return UUID.fromString("087E9967-CCDC-4A9B-9036-05930140A41B"); case VARCHAR: return "A varchar string"; case VARINT: return new BigInteger("123456789012345678901234567890"); case TIMEUUID: return UUID.fromString("FE2B4360-28C6-11E2-81C1-0800200C9A66"); case LIST: return new ArrayList(){{ add(getFixedValue(type.getTypeArguments().get(0))); }}; case SET: return new HashSet(){{ add(getFixedValue(type.getTypeArguments().get(0))); }}; case MAP: return new HashMap(){{ put(getFixedValue(type.getTypeArguments().get(0)), getFixedValue(type.getTypeArguments().get(1))); }}; } } catch (Exception e) { throw new RuntimeException(e); } throw new RuntimeException("Missing handling of " + type); } // Always return the "same" value for each type public static Object getFixedValue2(final DataType type) { try { switch (type.getName()) { case ASCII: return "A different ascii string"; case BIGINT: return Long.MAX_VALUE; case BLOB: ByteBuffer bb = ByteBuffer.allocate(64); bb.putInt(0xCAFE); bb.putShort((short) 3); bb.putShort((short) 45); return bb; case BOOLEAN: return false; case COUNTER: throw new UnsupportedOperationException("Cannot 'getSomeValue' for counters"); case DECIMAL: return new BigDecimal("12.3E+7"); case DOUBLE: return Double.POSITIVE_INFINITY; case FLOAT: return Float.POSITIVE_INFINITY; case INET: return InetAddress.getByName("123.123.123.123"); case INT: return Integer.MAX_VALUE; case TEXT: return "r??sum??"; case TIMESTAMP: return new Date(872835240000L); case UUID: return UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00"); case VARCHAR: return "A different varchar r??sum??"; case VARINT: return new BigInteger(Integer.toString(Integer.MAX_VALUE) + "000"); case TIMEUUID: return UUID.fromString("FE2B4360-28C6-11E2-81C1-0800200C9A66"); case LIST: return new ArrayList(){{ add(getFixedValue2(type.getTypeArguments().get(0))); }}; case SET: return new HashSet(){{ add(getFixedValue2(type.getTypeArguments().get(0))); }}; case MAP: return new HashMap(){{ put(getFixedValue2(type.getTypeArguments().get(0)), getFixedValue2(type.getTypeArguments().get(1))); }}; } } catch (Exception e) { throw new RuntimeException(e); } throw new RuntimeException("Missing handling of " + type); } // Wait for a node to be up and running // This is used because there is some delay between when a node has been // added through ccm and when it's actually available for querying public static void waitFor(String node, Cluster cluster) { waitFor(node, cluster, 20, false, false); } public static void waitFor(String node, Cluster cluster, int maxTry) { waitFor(node, cluster, maxTry, false, false); } public static void waitForDown(String node, Cluster cluster) { waitFor(node, cluster, 20, true, false); } public static void waitForDownWithWait(String node, Cluster cluster, int waitTime) { waitFor(node, cluster, 20, true, false); // FIXME: Once stop() works, remove this line try { Thread.sleep(waitTime * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } public static void waitForDown(String node, Cluster cluster, int maxTry) { waitFor(node, cluster, maxTry, true, false); } public static void waitForDecommission(String node, Cluster cluster) { waitFor(node, cluster, 20, true, true); } public static void waitForDecommission(String node, Cluster cluster, int maxTry) { waitFor(node, cluster, maxTry, true, true); } private static void waitFor(String node, Cluster cluster, int maxTry, boolean waitForDead, boolean waitForOut) { if (waitForDead || waitForOut) if (waitForDead) logger.info("Waiting for stopped node: " + node); else if (waitForOut) logger.info("Waiting for decommissioned node: " + node); else logger.info("Waiting for upcoming node: " + node); // In the case where the we've killed the last node in the cluster, if we haven't // tried doing an actual query, the driver won't realize that last node is dead until // keep alive kicks in, but that's a fairly long time. So we cheat and trigger a force // the detection by forcing a request. // if (waitForDead || waitForOut) // cluster.manager.submitSchemaRefresh(null, null); InetAddress address; try { address = InetAddress.getByName(node); } catch (Exception e) { // That's a problem but that's not *our* problem return; } Metadata metadata = cluster.getMetadata(); for (int i = 0; i < maxTry; ++i) { for (Host host : metadata.getAllHosts()) { if (host.getAddress().equals(address) && testHost(host, waitForDead)) return; } try { Thread.sleep(1000); } catch (Exception e) {} } for (Host host : metadata.getAllHosts()) { if (host.getAddress().equals(address)) { if (testHost(host, waitForDead)) { return; } else { // logging it because this give use the timestamp of when this happens logger.info(node + " is not " + (waitForDead ? "DOWN" : "UP") + " after " + maxTry + "s"); throw new IllegalStateException(node + " is not " + (waitForDead ? "DOWN" : "UP") + " after " + maxTry + "s"); } } } if (waitForOut){ return; } else { logger.info(node + " is not part of the cluster after " + maxTry + "s"); throw new IllegalStateException(node + " is not part of the cluster after " + maxTry + "s"); } } private static boolean testHost(Host host, boolean testForDown) { return testForDown ? !host.getMonitor().isUp() : host.getMonitor().isUp(); } }