View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.db;
15  
16  import java.lang.reflect.InvocationTargetException;
17  import java.lang.reflect.Method;
18  import java.sql.Connection;
19  import java.sql.PreparedStatement;
20  import java.sql.ResultSet;
21  import java.sql.SQLException;
22  import java.sql.Statement;
23  
24  import ch.qos.logback.core.UnsynchronizedAppenderBase;
25  import ch.qos.logback.core.db.dialect.DBUtil;
26  import ch.qos.logback.core.db.dialect.SQLDialect;
27  import ch.qos.logback.core.db.dialect.SQLDialectCode;
28  
29  /**
30   * @author Ceki Gülcü
31   * @author Ray DeCampo
32   * @author Sébastien Pennec
33   */
34  public abstract class DBAppenderBase<E> extends UnsynchronizedAppenderBase<E> {
35  
36    protected ConnectionSource connectionSource;
37    protected boolean cnxSupportsGetGeneratedKeys = false;
38    protected boolean cnxSupportsBatchUpdates = false;
39    protected SQLDialect sqlDialect;
40  
41    protected abstract Method getGeneratedKeysMethod();
42  
43    protected abstract String getInsertSQL();
44  
45    @Override
46    public void start() {
47  
48      if (connectionSource == null) {
49        throw new IllegalStateException(
50            "DBAppender cannot function without a connection source");
51      }
52  
53      sqlDialect = DBUtil
54          .getDialectFromCode(connectionSource.getSQLDialectCode());
55      if (getGeneratedKeysMethod() != null) {
56        cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys();
57      } else {
58        cnxSupportsGetGeneratedKeys = false;
59      }
60      cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates();
61      if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) {
62        throw new IllegalStateException(
63            "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect");
64      }
65  
66      // all nice and dandy on the eastern front
67      super.start();
68    }
69  
70    /**
71     * @return Returns the connectionSource.
72     */
73    public ConnectionSource getConnectionSource() {
74      return connectionSource;
75    }
76  
77    /**
78     * @param connectionSource
79     *          The connectionSource to set.
80     */
81    public void setConnectionSource(ConnectionSource connectionSource) {
82      this.connectionSource = connectionSource;
83    }
84  
85    @Override
86    public void append(E eventObject) {
87      Connection connection = null;
88      try {
89        connection = connectionSource.getConnection();
90        connection.setAutoCommit(false);
91        PreparedStatement insertStatement;
92  
93        if (cnxSupportsGetGeneratedKeys) {
94          String EVENT_ID_COL_NAME = "EVENT_ID";
95          // see
96          if (connectionSource.getSQLDialectCode() == SQLDialectCode.POSTGRES_DIALECT) {
97            EVENT_ID_COL_NAME = EVENT_ID_COL_NAME.toLowerCase();
98          }
99          insertStatement = connection.prepareStatement(getInsertSQL(),
100             new String[] { EVENT_ID_COL_NAME });
101       } else {
102         insertStatement = connection.prepareStatement(getInsertSQL());
103       }
104 
105       long eventId;
106       // inserting an event and getting the result must be exclusive
107       synchronized (this) {
108         subAppend(eventObject, connection, insertStatement);
109         eventId = selectEventId(insertStatement, connection);
110       }
111       secondarySubAppend(eventObject, connection, eventId);
112 
113       // we no longer need the insertStatement
114       close(insertStatement);
115       insertStatement = null;
116 
117       connection.commit();
118     } catch (Throwable sqle) {
119       addError("problem appending event", sqle);
120     } finally {
121       DBHelper.closeConnection(connection);
122     }
123   }
124 
125   protected abstract void subAppend(E eventObject, Connection connection,
126       PreparedStatement statement) throws Throwable;
127 
128   protected abstract void secondarySubAppend(E eventObject, Connection connection,
129       long eventId) throws Throwable;
130 
131   protected long selectEventId(PreparedStatement insertStatement,
132       Connection connection) throws SQLException, InvocationTargetException {
133     ResultSet rs = null;
134     Statement idStatement = null;
135     boolean gotGeneratedKeys = false;
136     if (cnxSupportsGetGeneratedKeys) {
137       try {
138         rs = (ResultSet) getGeneratedKeysMethod().invoke(insertStatement,
139             (Object[]) null);
140         gotGeneratedKeys = true;
141       } catch (InvocationTargetException ex) {
142         Throwable target = ex.getTargetException();
143         if (target instanceof SQLException) {
144           throw (SQLException) target;
145         }
146         throw ex;
147       } catch (IllegalAccessException ex) {
148         addWarn(
149             "IllegalAccessException invoking PreparedStatement.getGeneratedKeys",
150             ex);
151       }
152     }
153 
154     if (!gotGeneratedKeys) {
155       idStatement = connection.createStatement();
156       idStatement.setMaxRows(1);
157       String selectInsertIdStr = sqlDialect.getSelectInsertId();
158       rs = idStatement.executeQuery(selectInsertIdStr);
159     }
160 
161     // A ResultSet cursor is initially positioned before the first row;
162     // the first call to the method next makes the first row the current row
163     rs.next();
164     long eventId = rs.getLong(1);
165 
166     rs.close();
167 
168     close(idStatement);
169     idStatement = null;
170 
171     return eventId;
172   }
173 
174   void close(Statement statement) throws SQLException {
175     if (statement != null) {
176       statement.close();
177     }
178   }
179 
180   @Override
181   public void stop() {
182     super.stop();
183   }
184 }