blob: 68424bc5dd9e405ad16b88e342c3849947727d2f [file] [log] [blame]
// 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
// 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.cloud.utils.db;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.PrimaryKeyJoinColumns;
import javax.persistence.SecondaryTable;
import javax.persistence.SecondaryTables;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.log4j.Logger;
import static com.cloud.utils.AutoCloseableUtil.closeAutoCloseable;
public class DbUtil {
protected final static Logger LOGGER = Logger.getLogger(DbUtil.class);
private static Map<String, Connection> s_connectionForGlobalLocks = new HashMap<String, Connection>();
public static Connection getConnectionForGlobalLocks(String name, boolean forLock) {
synchronized (s_connectionForGlobalLocks) {
if (forLock) {
if (s_connectionForGlobalLocks.get(name) != null) {
LOGGER.error("Sanity check failed, global lock name " + name + " is already in use");
assert (false);
}
Connection connection = TransactionLegacy.getStandaloneConnection();
if (connection != null) {
try {
connection.setAutoCommit(true);
} catch (SQLException e) {
closeAutoCloseable(connection, "error closing connection for global locks");
return null;
}
s_connectionForGlobalLocks.put(name, connection);
return connection;
}
return null;
} else {
Connection connection = s_connectionForGlobalLocks.get(name);
s_connectionForGlobalLocks.remove(name);
return connection;
}
}
}
public static void removeConnectionForGlobalLocks(String name) {
synchronized (s_connectionForGlobalLocks) {
s_connectionForGlobalLocks.remove(name);
}
}
public static String getColumnName(Field field, AttributeOverride[] overrides) {
if (overrides != null) {
for (AttributeOverride override : overrides) {
if (override.name().equals(field.getName())) {
return override.column().name();
}
}
}
assert (field.getAnnotation(Embedded.class) == null) : "Cannot get column name from embedded field: " + field.getName();
Column column = field.getAnnotation(Column.class);
return column != null ? column.name() : field.getName();
}
public static String getColumnName(Field field) {
return getColumnName(field, null);
}
public static String getReferenceColumn(PrimaryKeyJoinColumn pkjc) {
return pkjc.referencedColumnName().length() != 0 ? pkjc.referencedColumnName() : pkjc.name();
}
public static PrimaryKeyJoinColumn[] getPrimaryKeyJoinColumns(Class<?> clazz) {
PrimaryKeyJoinColumn pkjc = clazz.getAnnotation(PrimaryKeyJoinColumn.class);
if (pkjc != null) {
return new PrimaryKeyJoinColumn[] {pkjc};
}
PrimaryKeyJoinColumns pkjcs = clazz.getAnnotation(PrimaryKeyJoinColumns.class);
if (pkjcs != null) {
return pkjcs.value();
}
return null;
}
public static Field findField(Class<?> clazz, String columnName) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getAnnotation(Embedded.class) != null || field.getAnnotation(EmbeddedId.class) != null) {
findField(field.getType(), columnName);
} else {
if (columnName.equals(DbUtil.getColumnName(field))) {
return field;
}
}
}
return null;
}
public static final AttributeOverride[] getAttributeOverrides(AnnotatedElement ae) {
AttributeOverride[] overrides = null;
AttributeOverrides aos = ae.getAnnotation(AttributeOverrides.class);
if (aos != null) {
overrides = aos.value();
}
if (overrides == null || overrides.length == 0) {
AttributeOverride override = ae.getAnnotation(AttributeOverride.class);
if (override != null) {
overrides = new AttributeOverride[1];
overrides[0] = override;
} else {
overrides = new AttributeOverride[0];
}
}
return overrides;
}
public static final boolean isPersistable(Field field) {
if (field.getAnnotation(Transient.class) != null) {
return false;
}
int modifiers = field.getModifiers();
return !(Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers));
}
public static final boolean isIdField(Field field) {
if (field.getAnnotation(Id.class) != null) {
return true;
}
if (field.getAnnotation(EmbeddedId.class) != null) {
assert (field.getType().getAnnotation(Embeddable.class) != null) : "Class " + field.getType().getName() + " must be Embeddable to be used as Embedded Id";
return true;
}
return false;
}
public static final SecondaryTable[] getSecondaryTables(AnnotatedElement clazz) {
SecondaryTable[] sts = null;
SecondaryTable stAnnotation = clazz.getAnnotation(SecondaryTable.class);
if (stAnnotation == null) {
SecondaryTables stsAnnotation = clazz.getAnnotation(SecondaryTables.class);
sts = stsAnnotation != null ? stsAnnotation.value() : new SecondaryTable[0];
} else {
sts = new SecondaryTable[] {stAnnotation};
}
return sts;
}
public static final String getTableName(Class<?> clazz) {
Table table = clazz.getAnnotation(Table.class);
return table != null ? table.name() : clazz.getSimpleName();
}
public static boolean getGlobalLock(String name, int timeoutSeconds) {
Connection conn = getConnectionForGlobalLocks(name, true);
if (conn == null) {
LOGGER.error("Unable to acquire DB connection for global lock system");
return false;
}
try (PreparedStatement pstmt = conn.prepareStatement("SELECT COALESCE(GET_LOCK(?, ?),0)");) {
pstmt.setString(1, name);
pstmt.setInt(2, timeoutSeconds);
try (ResultSet rs = pstmt.executeQuery();) {
if (rs != null && rs.first()) {
if (rs.getInt(1) > 0) {
return true;
} else {
if (LOGGER.isDebugEnabled())
LOGGER.debug("GET_LOCK() timed out on lock : " + name);
}
}
}
} catch (SQLException e) {
LOGGER.error("GET_LOCK() throws exception ", e);
} catch (Throwable e) {
LOGGER.error("GET_LOCK() throws exception ", e);
}
removeConnectionForGlobalLocks(name);
closeAutoCloseable(conn, "connection for global lock");
return false;
}
public static Class<?> getEntityBeanType(GenericDao<?, Long> dao) {
return dao.getEntityBeanType();
}
public static boolean releaseGlobalLock(String name) {
try (Connection conn = getConnectionForGlobalLocks(name, false);) {
if (conn == null) {
LOGGER.error("Unable to acquire DB connection for global lock system");
assert (false);
return false;
}
try (PreparedStatement pstmt = conn.prepareStatement("SELECT COALESCE(RELEASE_LOCK(?), 0)");) {
pstmt.setString(1, name);
try (ResultSet rs = pstmt.executeQuery();) {
if (rs != null && rs.first()) {
return rs.getInt(1) > 0;
}
LOGGER.error("releaseGlobalLock:RELEASE_LOCK() returns unexpected result");
}
}
} catch (SQLException e) {
LOGGER.error("RELEASE_LOCK() throws exception ", e);
} catch (Throwable e) {
LOGGER.error("RELEASE_LOCK() throws exception ", e);
}
return false;
}
public static void closeResources(final Connection connection, final Statement statement, final ResultSet resultSet) {
closeResultSet(resultSet);
closeStatement(statement);
closeConnection(connection);
}
public static void closeResources(final Statement statement, final ResultSet resultSet) {
closeResources(null, statement, resultSet);
}
public static void closeResultSet(final ResultSet resultSet) {
closeAutoCloseable(resultSet, "exception while closing result set.");
}
public static void closeStatement(final Statement statement) {
closeAutoCloseable(statement, "exception while closing statement.");
}
public static void closeConnection(final Connection connection) {
closeAutoCloseable(connection, "exception while close connection.");
}
public static Map<String, String> getDbInfo(String type, String ... var) {
String vars = String.join(",", var);
Map<String, String> result = new HashMap<>();
String sql = String.format("SHOW %s WHERE FIND_IN_SET(Variable_name,?)",type);
try (TransactionLegacy txn = TransactionLegacy.open("metrics")) {
PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql);
pstmt.setString(1, vars);
final ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
String variableName = rs.getString("Variable_name");
String value = rs.getString("value");
result.put(variableName, value);
}
} catch (SQLException e) {
LOGGER.error("failed to get the database status: " + e.getLocalizedMessage());
LOGGER.debug("failed to get the database status", e);
}
return result;
}
}