Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2004 Regents of the University of California and the
4
 *             National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: jones $'
7
 *     '$Date: 2004-04-08 13:49:09 -0700 (Thu, 08 Apr 2004) $'
8
 * '$Revision: 2113 $'
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 */
24
package edu.ucsb.nceas.metacat;
25

    
26
import java.sql.PreparedStatement;
27
import java.sql.ResultSet;
28
import java.sql.SQLException;
29
import java.sql.Timestamp;
30
import java.util.Date;
31

    
32
/**
33
 * EventLog is used to intialize and store a log of events that occur in an
34
 * application. The events are registered with the logger as they occur, but
35
 * EventLog writes them to permenant storage when it is most convenient or
36
 * efficient. EventLog is a Singleton as there should always be only one object
37
 * for these logging events.
38
 * 
39
 * TODO: Logging to the database needn't be synchronous with the event.  
40
 * Instead, a separate thread can be launched that periodically sleeps and only
41
 * wakes periodically to see if metacat is idle.  The log event can be cached
42
 * and inserted later when the thread wakes and finds metacat idle.
43
 * 
44
 * TODO: Write a function that archives a part of the log table to an 
45
 * external text file so that the log table doesn't get to big.  This 
46
 * function should be able to be called manually or on a schedule. 
47
 * 
48
 * TODO: Write an access function that returns an XML report for a
49
 * specific subset of events.  Users should be able to query on
50
 * principal, docid/rev, date, event, and possibly other fields.
51
 * 
52
 * @author jones
53
 */
54
public class EventLog
55
{
56
    /**
57
     * The single instance of the event log that is always returned.
58
     */
59
    private static EventLog self = null;
60

    
61
    /**
62
     * A private constructor that initializes the class when getInstance() is
63
     * called.
64
     */
65
    private EventLog()
66
    {
67
    }
68

    
69
    /**
70
     * Return the single instance of the event log after initializing it if it
71
     * wasn't previously initialized.
72
     * 
73
     * @return the single EventLog instance
74
     */
75
    public static EventLog getInstance()
76
    {
77
        if (self == null) {
78
            self = new EventLog();
79
        }
80
        return self;
81
    }
82

    
83
    /**
84
     * Log an event of interest to the application. The information logged can
85
     * include basic identification information about the principal or computer
86
     * that initiated the event.
87
     * 
88
     * @param ipAddress the internet protocol address for the event
89
	 * @param principal the principal for the event (a username, etc)
90
	 * @param docid the identifier of the document to which the event applies
91
	 * @param event the string code for the event
92
     */
93
    public void log(String ipAddress, String principal, String docid,
94
			String event)
95
    {
96
        EventLogData logData = new EventLogData(ipAddress, principal, docid,
97
                event);
98
        insertLogEntry(logData);
99
    }
100
    
101
    /**
102
     * Insert a single log event record to the database.
103
     * 
104
     * @param logData the data to be logged when an event occurs
105
     */
106
    private void insertLogEntry(EventLogData logData)
107
    {
108
        String insertString = "insert into access_log"
109
                + "(ip_address, principal, docid, "
110
                + "event, date_logged) "
111
                + "values ("
112
                + "'" + logData.getIpAddress() + "', " 
113
                + "'" + logData.getPrincipal() + "', "
114
                + "'" + logData.getDocid() + "', "
115
                + "'" + logData.getEvent() + "', "
116
                + " ? " + ")"; 
117

    
118
        DBConnection dbConn = null;
119
        int serialNumber = -1;
120
        try {
121
            // Get a database connection from the pool
122
            dbConn = DBConnectionPool.getDBConnection("EventLog.insertLogEntry");
123
            serialNumber = dbConn.getCheckOutSerialNumber();
124
            
125
            // Execute the insert statement
126
            PreparedStatement stmt = dbConn.prepareStatement(insertString);
127
            stmt.setTimestamp(1, new Timestamp(new Date().getTime()));
128
            stmt.executeUpdate();
129
            stmt.close();
130
        } catch (SQLException e) {
131
            MetaCatUtil.debugMessage("Error while logging event to database: " 
132
                    + e.getMessage(), 5);
133
        } finally {
134
            // Return database connection to the pool
135
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
136
        }
137
    }
138
    
139
    /**
140
     * Get a report of the log events that match a set of filters.  The
141
     * filter parameters can be null; log records are subset based on
142
     * non-null filter parameters.
143
     * 
144
     * @param ipAddress the internet protocol address for the event
145
	 * @param principal the principal for the event (a username, etc)
146
	 * @param docid the identifier of the document to which the event applies
147
	 * @param event the string code for the event
148
	 * @param startDate beginning of date range for query
149
	 * @param endDate end of date range for query
150
	 * @return an XML-formatted report of the access log entries
151
     */
152
    public String getReport(String[] ipAddress, String[] principal, String[] docid,
153
            String[] event, Timestamp startDate, Timestamp endDate)
154
    {
155
        StringBuffer resultDoc = new StringBuffer();
156
        StringBuffer query = new StringBuffer();
157
        query.append("select entryid, ip_address, principal, docid, "
158
            + "event, date_logged from access_log");
159
//                        + ""
160
//                        + "event, date_logged) " + "values (" + "'"
161
//                        + logData.getIpAddress() + "', " + "'"
162
//                        + logData.getPrincipal() + "', " + "'"
163
//                        + logData.getDocid() + "', " + "'" + logData.getEvent()
164
//                        + "', " + " ? " + ")";
165
        if (ipAddress != null || principal != null || docid != null
166
                        || event != null || startDate != null || endDate != null) {
167
            query.append(" where ");
168
        }
169
        boolean clauseAdded = false;
170
        int startIndex = 0;
171
        int endIndex = 0;
172
        
173
        if (ipAddress != null) {
174
            query.append(generateSqlClause(clauseAdded, "ip_address", ipAddress));
175
            clauseAdded = true;
176
        }
177
        if (principal != null) {
178
            query.append(generateSqlClause(clauseAdded, "principal", principal));
179
            clauseAdded = true;
180
        }
181
        if (docid != null) {
182
            query.append(generateSqlClause(clauseAdded, "docid", docid));
183
            clauseAdded = true;
184
        }
185
        if (event != null) {
186
            query.append(generateSqlClause(clauseAdded, "event", event));
187
            clauseAdded = true;
188
        }
189
        if (startDate != null) {
190
            if (clauseAdded) {
191
                query.append(" and ");
192
            }
193
            query.append("date_logged > ?");
194
            clauseAdded = true;
195
            startIndex++;
196
        }
197
        if (endDate != null) {
198
            if (clauseAdded) {
199
                query.append(" and ");
200
            }
201
            query.append("date_logged < ?");
202
            clauseAdded = true;
203
            endIndex = startIndex + 1;
204
        }
205
        DBConnection dbConn = null;
206
        int serialNumber = -1;
207
        try {
208
            // Get a database connection from the pool
209
            dbConn = DBConnectionPool.getDBConnection("EventLog.getReport");
210
            serialNumber = dbConn.getCheckOutSerialNumber();
211

    
212
            // Execute the query statement
213
            PreparedStatement stmt = dbConn.prepareStatement(query.toString());
214
            if (startIndex > 0) {
215
                stmt.setTimestamp(startIndex, startDate); 
216
            }
217
            if (endIndex > 0) {
218
                stmt.setTimestamp(endIndex, endDate);
219
            }
220
            stmt.execute();
221
            ResultSet rs = stmt.getResultSet();
222
            //process the result and return it as an XML document
223
            resultDoc.append("<?xml version=\"1.0\"?>\n");
224
            resultDoc.append("<log>\n");
225
            while (rs.next()) {
226
                resultDoc.append(generateXmlRecord(rs.getString(1), rs.getString(2),
227
                                rs.getString(3), rs.getString(4), 
228
                                rs.getString(5), rs.getTimestamp(6)));
229
            }
230
            resultDoc.append("</log>");
231
            stmt.close();
232
        } catch (SQLException e) {
233
            MetaCatUtil.debugMessage("Error while logging event to database: "
234
                            + e.getMessage(), 5);
235
        } finally {
236
            // Return database connection to the pool
237
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
238
        }
239
        return resultDoc.toString();
240
    }
241
    
242
    /**
243
     * Utility method to help build a SQL query from an array of values.  For each
244
     * value in the array an 'OR' clause is constructed.
245
     * 
246
     * @param addOperator a flag indicating whether to add an 'AND' operator 
247
     *                    to the clause
248
     * @param column the name of the column to filter against
249
     * @param values the values to match in the SQL query
250
     * @return a String representation of the SQL query clause
251
     */
252
    private String generateSqlClause(boolean addOperator, String column, 
253
            String[] values)
254
    {
255
        StringBuffer clause = new StringBuffer();
256
        if (addOperator) {
257
            clause.append(" and ");
258
        }
259
        clause.append("(");
260
        for (int i = 0; i < values.length; i++) {
261
            if (i > 0) {
262
                clause.append(" or ");
263
            }
264
            clause.append(column);
265
            clause.append(" like '");
266
            clause.append(values[i]);
267
            clause.append("'");
268
        }
269
        clause.append(")");
270
        return clause.toString();
271
    }
272
    
273
    /**
274
     * Format each returned log record as an XML structure.
275
     * 
276
     * @param entryId the identifier of the log entry
277
     * @param ipAddress the internet protocol address for the event
278
	 * @param principal the principal for the event (a username, etc)
279
	 * @param docid the identifier of the document to which the event applies
280
	 * @param event the string code for the event
281
     * @param dateLogged the date on which the event occurred
282
     * @return String containing the formatted XML
283
     */
284
    private String generateXmlRecord(String entryId, String ipAddress, 
285
            String principal, String docid, String event, Timestamp dateLogged)
286
    {
287
        StringBuffer rec = new StringBuffer();
288
        rec.append("<logEntry>");
289
        rec.append(generateXmlElement("entryid", entryId));
290
        rec.append(generateXmlElement("ipAddress", ipAddress));
291
        rec.append(generateXmlElement("principal", principal));
292
        rec.append(generateXmlElement("docid", docid));
293
        rec.append(generateXmlElement("event", event));
294
        rec.append(generateXmlElement("dateLogged", dateLogged.toString()));
295
        rec.append("</logEntry>\n");
296

    
297
        return rec.toString();
298
    }
299
    
300
    /**
301
     * Return an XML formatted element for a given name/value pair.
302
     * 
303
     * @param name the name of the xml element
304
     * @param value the content of the xml element
305
     * @return the formatted XML element as a String
306
     */
307
    private String generateXmlElement(String name, String value)
308
    {
309
        return "<" + name + ">" + value + "</" + name + ">";
310
    }
311
}
(37-37/64)