/* * 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.ignite.cache.store.cassandra.common; import com.datastax.driver.core.Cluster; import com.datastax.driver.core.DataType; import com.datastax.driver.core.Session; import com.datastax.driver.core.exceptions.DriverException; import com.datastax.driver.core.exceptions.InvalidQueryException; import com.datastax.driver.core.exceptions.NoHostAvailableException; import com.datastax.driver.core.exceptions.ReadTimeoutException; import java.net.InetSocketAddress; import java.util.Map; import java.util.regex.Pattern; import org.apache.ignite.internal.util.typedef.internal.U; /** * Helper class providing methods to work with Cassandra session and exceptions */ public class CassandraHelper { /** Cassandra error message if specified keyspace doesn't exist. */ private static final Pattern KEYSPACE_EXIST_ERROR1 = Pattern.compile("Keyspace [0-9a-zA-Z_]+ does not exist"); /** Cassandra error message if trying to create table inside nonexistent keyspace. */ private static final Pattern KEYSPACE_EXIST_ERROR2 = Pattern.compile("Cannot add table '[0-9a-zA-Z_]+' to non existing keyspace.*"); /** Cassandra error message if trying to create table inside nonexistent keyspace. */ private static final Pattern KEYSPACE_EXIST_ERROR3 = Pattern.compile("Error preparing query, got ERROR INVALID: " + "Keyspace [0-9a-zA-Z_]+ does not exist"); /** Cassandra error message if specified table doesn't exist. */ private static final Pattern TABLE_EXIST_ERROR1 = Pattern.compile("unconfigured table [0-9a-zA-Z_]+"); /** Cassandra error message if specified table doesn't exist. */ private static final String TABLE_EXIST_ERROR2 = "Error preparing query, got ERROR INVALID: unconfigured table"; /** Cassandra error message if trying to use prepared statement created from another session. */ private static final String PREP_STATEMENT_CLUSTER_INSTANCE_ERROR = "You may have used a PreparedStatement that " + "was created with another Cluster instance"; /** Closes Cassandra driver session. */ public static void closeSession(Session driverSes) { if (driverSes == null) return; Cluster cluster = driverSes.getCluster(); if (!driverSes.isClosed()) U.closeQuiet(driverSes); if (!cluster.isClosed()) U.closeQuiet(cluster); } /** * Checks if Cassandra keyspace absence error occur. * * @param e Exception to check. * @return {@code true} in case of keyspace absence error. */ public static boolean isKeyspaceAbsenceError(Throwable e) { while (e != null) { if (e instanceof InvalidQueryException && (KEYSPACE_EXIST_ERROR1.matcher(e.getMessage()).matches() || KEYSPACE_EXIST_ERROR2.matcher(e.getMessage()).matches())) return true; e = e.getCause(); } return false; } /** * Checks if Cassandra table absence error occur. * * @param e Exception to check. * @return {@code true} in case of table absence error. */ public static boolean isTableAbsenceError(Throwable e) { while (e != null) { if (e instanceof InvalidQueryException && (TABLE_EXIST_ERROR1.matcher(e.getMessage()).matches() || KEYSPACE_EXIST_ERROR1.matcher(e.getMessage()).matches() || KEYSPACE_EXIST_ERROR2.matcher(e.getMessage()).matches())) return true; if (e instanceof NoHostAvailableException && ((NoHostAvailableException) e).getErrors() != null) { NoHostAvailableException ex = (NoHostAvailableException)e; for (Map.Entry<InetSocketAddress, Throwable> entry : ex.getErrors().entrySet()) { //noinspection ThrowableResultOfMethodCallIgnored Throwable error = entry.getValue(); if (error instanceof DriverException && (error.getMessage().contains(TABLE_EXIST_ERROR2) || KEYSPACE_EXIST_ERROR3.matcher(error.getMessage()).matches())) return true; } } e = e.getCause(); } return false; } /** * Checks if Cassandra host availability error occur, thus host became unavailable. * * @param e Exception to check. * @return {@code true} in case of host not available error. */ public static boolean isHostsAvailabilityError(Throwable e) { while (e != null) { if (e instanceof NoHostAvailableException || e instanceof ReadTimeoutException) return true; e = e.getCause(); } return false; } /** * Checks if Cassandra error occur because of prepared statement created in one session was used in another session. * * @param e Exception to check. * @return {@code true} in case of invalid usage of prepared statement. */ public static boolean isPreparedStatementClusterError(Throwable e) { while (e != null) { if (e instanceof InvalidQueryException && e.getMessage().contains(PREP_STATEMENT_CLUSTER_INSTANCE_ERROR)) return true; e = e.getCause(); } return false; } /** * Checks if two Java classes are Cassandra compatible - mapped to the same Cassandra type. * * @param type1 First type. * @param type2 Second type. * @return {@code true} if classes are compatible and {@code false} if not. */ public static boolean isCassandraCompatibleTypes(Class type1, Class type2) { if (type1 == null || type2 == null) return false; DataType.Name t1 = PropertyMappingHelper.getCassandraType(type1); DataType.Name t2 = PropertyMappingHelper.getCassandraType(type2); return t1 != null && t2 != null && t1.equals(t2); } }