| /* |
| * 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 java.net.InetSocketAddress; |
| import java.util.Map; |
| import java.util.regex.Pattern; |
| 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 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 specified table doesn't exist. */ |
| private static final Pattern TABLE_EXIST_ERROR3 = Pattern.compile("unconfigured columnfamily [0-9a-zA-Z_]+"); |
| |
| /** 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() || |
| TABLE_EXIST_ERROR3.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()) { |
| 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); |
| } |
| } |
| |