/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2015, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.db; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import ch.qos.logback.core.UnsynchronizedAppenderBase; import ch.qos.logback.core.db.dialect.DBUtil; import ch.qos.logback.core.db.dialect.SQLDialect; import ch.qos.logback.core.db.dialect.SQLDialectCode; /** * @author Ceki Gülcü * @author Ray DeCampo * @author Sébastien Pennec */ public abstract class DBAppenderBase<E> extends UnsynchronizedAppenderBase<E> { protected ConnectionSource connectionSource; protected boolean cnxSupportsGetGeneratedKeys = false; protected boolean cnxSupportsBatchUpdates = false; protected SQLDialect sqlDialect; protected abstract Method getGeneratedKeysMethod(); protected abstract String getInsertSQL(); @Override public void start() { if (connectionSource == null) { throw new IllegalStateException("DBAppender cannot function without a connection source"); } sqlDialect = DBUtil.getDialectFromCode(connectionSource.getSQLDialectCode()); if (getGeneratedKeysMethod() != null) { cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys(); } else { cnxSupportsGetGeneratedKeys = false; } cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates(); if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) { throw new IllegalStateException( "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect"); } // all nice and dandy on the eastern front super.start(); } /** * @return Returns the connectionSource. */ public ConnectionSource getConnectionSource() { return connectionSource; } /** * @param connectionSource * The connectionSource to set. */ public void setConnectionSource(ConnectionSource connectionSource) { this.connectionSource = connectionSource; } @Override public void append(E eventObject) { Connection connection = null; PreparedStatement insertStatement = null; try { connection = connectionSource.getConnection(); connection.setAutoCommit(false); if (cnxSupportsGetGeneratedKeys) { String EVENT_ID_COL_NAME = "EVENT_ID"; // see if (connectionSource.getSQLDialectCode() == SQLDialectCode.POSTGRES_DIALECT) { EVENT_ID_COL_NAME = EVENT_ID_COL_NAME.toLowerCase(); } insertStatement = connection.prepareStatement(getInsertSQL(), new String[] { EVENT_ID_COL_NAME }); } else { insertStatement = connection.prepareStatement(getInsertSQL()); } long eventId; // inserting an event and getting the result must be exclusive synchronized (this) { subAppend(eventObject, connection, insertStatement); eventId = selectEventId(insertStatement, connection); } secondarySubAppend(eventObject, connection, eventId); connection.commit(); } catch (Throwable sqle) { addError("problem appending event", sqle); } finally { DBHelper.closeStatement(insertStatement); DBHelper.closeConnection(connection); } } protected abstract void subAppend(E eventObject, Connection connection, PreparedStatement statement) throws Throwable; protected abstract void secondarySubAppend(E eventObject, Connection connection, long eventId) throws Throwable; protected long selectEventId(PreparedStatement insertStatement, Connection connection) throws SQLException, InvocationTargetException { ResultSet rs = null; Statement idStatement = null; try { boolean gotGeneratedKeys = false; if (cnxSupportsGetGeneratedKeys) { try { rs = (ResultSet) getGeneratedKeysMethod().invoke(insertStatement, (Object[]) null); gotGeneratedKeys = true; } catch (InvocationTargetException ex) { Throwable target = ex.getTargetException(); if (target instanceof SQLException) { throw (SQLException) target; } throw ex; } catch (IllegalAccessException ex) { addWarn("IllegalAccessException invoking PreparedStatement.getGeneratedKeys", ex); } } if (!gotGeneratedKeys) { idStatement = connection.createStatement(); idStatement.setMaxRows(1); String selectInsertIdStr = sqlDialect.getSelectInsertId(); rs = idStatement.executeQuery(selectInsertIdStr); } // A ResultSet cursor is initially positioned before the first row; // the first call to the method next makes the first row the current row rs.next(); long eventId = rs.getLong(1); return eventId; } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { } } DBHelper.closeStatement(idStatement); } } @Override public void stop() { super.stop(); } }