/* * Copyright (c) 2010-2016 Evolveum * * 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.evolveum.midpoint.task.quartzimpl.handlers; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.repo.sql.SqlRepositoryConfiguration; import com.evolveum.midpoint.repo.sql.SqlRepositoryFactory; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.*; import com.evolveum.midpoint.task.api.TaskRunResult.TaskRunResultStatus; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.xml.namespace.QName; import java.sql.*; import java.util.List; /** * @author Pavol Mederly */ @Component public class JdbcPingTaskHandler implements TaskHandler { private static final transient Trace LOGGER = TraceManager.getTrace(JdbcPingTaskHandler.class); public static final String HANDLER_URI = TaskConstants.JDBC_PING_HANDLER_URI; @Autowired private TaskManager taskManager; @Autowired(required = false) // during some tests the repo is not available private SqlRepositoryFactory sqlRepositoryFactory; @PostConstruct public void initialize() { taskManager.registerHandler(HANDLER_URI, this); } private class Statistics { Integer min = null; Integer max = null; int total = 0; int okCount = 0; int failCount = 0; void record(int time) { total += time; okCount++; if (min == null || time < min) { min = time; } if (max == null || time > max) { max = time; } } void recordFailure() { failCount++; } @Override public String toString() { float avg = okCount > 0 ? (float) total / (float) okCount : 0; return "OK: " + okCount + ", avg: " + String.format("%.2f", avg) + " ms, min: " + min + " ms, max: " + max + " ms, failures: " + failCount; } } @Override public TaskRunResult run(Task task) { long progress = task.getProgress(); OperationResult opResult = new OperationResult(JdbcPingTaskHandler.class.getName()+".run"); int tests = get(task, SchemaConstants.JDBC_PING_TESTS_QNAME, 0); int interval = get(task, SchemaConstants.JDBC_PING_INTERVAL_QNAME, 10); String testQuery = get(task, SchemaConstants.JDBC_PING_TEST_QUERY_QNAME, "select 1"); SqlRepositoryConfiguration config = sqlRepositoryFactory != null ? sqlRepositoryFactory.getSqlConfiguration() : null; String jdbcDriver = get(task, SchemaConstants.JDBC_PING_DRIVER_CLASS_NAME_QNAME, config != null ? config.getDriverClassName() : ""); String jdbcUrl = get(task, SchemaConstants.JDBC_PING_JDBC_URL_QNAME, config != null ? config.getJdbcUrl() : ""); String jdbcUsername = get(task, SchemaConstants.JDBC_PING_JDBC_USERNAME_QNAME, config != null ? config.getJdbcUsername() : ""); String jdbcPassword = get(task, SchemaConstants.JDBC_PING_JDBC_PASSWORD_QNAME, config != null ? config.getJdbcPassword() : ""); boolean logOnInfoLevel = get(task, SchemaConstants.JDBC_PING_LOG_ON_INFO_LEVEL_QNAME, true); LOGGER.info("JdbcPingTaskHandler run starting; with progress = {}", progress); LOGGER.info("Tests to be executed: {}", tests > 0 ? tests : "(unlimited)"); LOGGER.info("Interval between tests: {} seconds", interval); LOGGER.info("SQL query to be used: {}", testQuery); LOGGER.info("JDBC:"); LOGGER.info(" - driver: {}", jdbcDriver); LOGGER.info(" - URL: {}", jdbcUrl); LOGGER.info(" - username: {}", jdbcUsername); LOGGER.info("Log on info level: {}", logOnInfoLevel); Statistics connectionStatistics = new Statistics(); Statistics queryStatistics = new Statistics(); for (int i = 0; task.canRun() && (tests == 0 || i < tests); i++) { Connection connection = null; try { Class.forName(jdbcDriver); long connStart = System.currentTimeMillis(); connection = DriverManager.getConnection(jdbcUrl, jdbcUsername, jdbcPassword); long connTime = System.currentTimeMillis() - connStart; connectionStatistics.record((int) connTime); if (logOnInfoLevel) { LOGGER.info("Successfully connected to database in {} milliseconds", connTime); } else { LOGGER.debug("Successfully connected to database in {} milliseconds", connTime); } try { long queryStart = System.currentTimeMillis(); Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery(testQuery); int rowCount = 0; while (rs.next()) { rowCount++; } long queryTime = System.currentTimeMillis() - queryStart; queryStatistics.record((int) queryTime); if (logOnInfoLevel) { LOGGER.info("Test query executed successfully in {} milliseconds, returned {} rows", queryTime, rowCount); } else { LOGGER.debug("Test query executed successfully in {} milliseconds, returned {} rows", queryTime, rowCount); } } catch (Throwable t) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't execute test query '" + testQuery + "'", t); queryStatistics.recordFailure(); } } catch (Throwable t) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't connect to '" + jdbcUrl + "'", t); connectionStatistics.recordFailure(); } if (logOnInfoLevel) { LOGGER.info("Connection statistics: {}", connectionStatistics); LOGGER.info("Query statistics: {}", queryStatistics); } else { LOGGER.debug("Connection statistics: {}", connectionStatistics); LOGGER.debug("Query statistics: {}", queryStatistics); } if (connection != null) { try { connection.close(); } catch (Throwable t) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't close DB connection", t); } } progress++; try { Thread.sleep(1000L * interval); } catch (InterruptedException e) { break; } } opResult.computeStatusIfUnknown(); TaskRunResult runResult = new TaskRunResult(); runResult.setOperationResult(opResult); runResult.setRunResultStatus(TaskRunResultStatus.FINISHED); // would be overwritten when problem is encountered runResult.setProgress(progress); LOGGER.info("JdbcPingTaskHandler run finishing; progress = " + progress + " in task " + task.getName()); LOGGER.info("Connection statistics: {}", connectionStatistics); LOGGER.info("Query statistics: {}", queryStatistics); return runResult; } private <T> T get(Task task, QName propertyName, T defaultValue) { PrismProperty<T> property = task.getExtensionProperty(propertyName); if (property == null) { return defaultValue; } else { return property.getRealValue(); } } @Override public Long heartbeat(Task task) { return null; // not to overwrite progress information! } @Override public void refreshStatus(Task task) { } @Override public String getCategoryName(Task task) { return TaskCategory.UTIL; } @Override public List<String> getCategoryNames() { return null; } }