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: sgarg $'
7
 *     '$Date: 2005-10-10 11:06:55 -0700 (Mon, 10 Oct 2005) $'
8
 * '$Revision: 2663 $'
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
import org.apache.log4j.Logger;
33

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

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

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

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

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

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

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