| // 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 s_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) { |
| s_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.getClass(), 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.getClass().getAnnotation(Embeddable.class) != null) : "Class " + field.getClass().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) { |
| s_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 (s_logger.isDebugEnabled()) |
| s_logger.debug("GET_LOCK() timed out on lock : " + name); |
| } |
| } |
| } |
| } catch (SQLException e) { |
| s_logger.error("GET_LOCK() throws exception ", e); |
| } catch (Throwable e) { |
| s_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) { |
| s_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; |
| } |
| s_logger.error("releaseGlobalLock:RELEASE_LOCK() returns unexpected result"); |
| } |
| } |
| } catch (SQLException e) { |
| s_logger.error("RELEASE_LOCK() throws exception ", e); |
| } catch (Throwable e) { |
| s_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."); |
| } |
| |
| } |