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: leinfelder $'
7
 *     '$Date: 2011-11-02 22:10:16 -0700 (Wed, 02 Nov 2011) $'
8
 * '$Revision: 6598 $'
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.ArrayList;
31
import java.util.Date;
32
import java.util.List;
33

    
34
import org.apache.log4j.Logger;
35

    
36
import edu.ucsb.nceas.metacat.database.DBConnection;
37
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
38

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

    
69
    /**
70
     * A private constructor that initializes the class when getInstance() is
71
     * called.
72
     */
73
    private EventLog()
74
    {
75
    }
76

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

    
91
    /**
92
     * Log an event of interest to the application. The information logged can
93
     * include basic identification information about the principal or computer
94
     * that initiated the event.
95
     * 
96
     * @param ipAddress the internet protocol address for the event
97
     * @param userAgent the agent making the request
98
	 * @param principal the principal for the event (a username, etc)
99
	 * @param docid the identifier of the document to which the event applies
100
	 * @param event the string code for the event
101
     */
102
    public void log(String ipAddress, String userAgent, String principal, String docid, String event) {
103
        EventLogData logData = new EventLogData(ipAddress, principal, docid, event);
104
        insertLogEntry(logData);
105
    }
106
    
107
    /**
108
     * Insert a single log event record to the database.
109
     * 
110
     * @param logData the data to be logged when an event occurs
111
     */
112
    private void insertLogEntry(EventLogData logData)
113
    {
114
        String insertString = "insert into access_log"
115
                + "(ip_address, user_agent, principal, docid, "
116
                + "event, date_logged) "
117
                + "values ( ?, ?, ?, ?, ?, ? )";
118
 
119
        DBConnection dbConn = null;
120
        int serialNumber = -1;
121
        try {
122
            // Get a database connection from the pool
123
            dbConn = DBConnectionPool.getDBConnection("EventLog.insertLogEntry");
124
            serialNumber = dbConn.getCheckOutSerialNumber();
125
            
126
            // Execute the insert statement
127
            PreparedStatement stmt = dbConn.prepareStatement(insertString);
128
            
129
            stmt.setString(1, logData.getIpAddress());
130
            stmt.setString(2, logData.getUserAgent());
131
            stmt.setString(3, logData.getPrincipal());
132
            stmt.setString(4, logData.getDocid());
133
            stmt.setString(5, logData.getEvent());
134
            stmt.setTimestamp(6, new Timestamp(new Date().getTime()));
135
            stmt.executeUpdate();
136
            stmt.close();
137
        } catch (SQLException e) {
138
        	logMetacat.error("Error while logging event to database: " 
139
                    + e.getMessage());
140
        } finally {
141
            // Return database connection to the pool
142
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
143
        }
144
    }
145
    
146
    /**
147
     * Get a report of the log events that match a set of filters.  The
148
     * filter parameters can be null; log records are subset based on
149
     * non-null filter parameters.
150
     * 
151
     * @param ipAddress the internet protocol address for the event
152
	 * @param principal the principal for the event (a username, etc)
153
	 * @param docid the identifier of the document to which the event applies
154
	 * @param event the string code for the event
155
	 * @param startDate beginning of date range for query
156
	 * @param endDate end of date range for query
157
	 * @return an XML-formatted report of the access log entries
158
     */
159
    public String getReport(String[] ipAddress, String[] principal, String[] docid,
160
            String[] event, Timestamp startDate, Timestamp endDate, boolean anonymous)
161
    {
162
        StringBuffer resultDoc = new StringBuffer();
163
        StringBuffer query = new StringBuffer();
164
        query.append("select entryid, ip_address, user_agent, principal, docid, "
165
            + "event, date_logged from access_log");
166
//                        + ""
167
//                        + "event, date_logged) " + "values (" + "'"
168
//                        + logData.getIpAddress() + "', " + "'"
169
//                        + logData.getPrincipal() + "', " + "'"
170
//                        + logData.getDocid() + "', " + "'" + logData.getEvent()
171
//                        + "', " + " ? " + ")";
172
        if (ipAddress != null || principal != null || docid != null
173
                        || event != null || startDate != null || endDate != null) {
174
            query.append(" where ");
175
        }
176
        boolean clauseAdded = false;
177
        int startIndex = 0;
178
        int endIndex = 0;
179
        
180
        List<String> paramValues = new ArrayList<String>();
181
        if (ipAddress != null) {
182
        	query.append("ip_address in (");
183
        	for (int i = 0; i < ipAddress.length; i++) {
184
        		if (i > 0) {
185
            		query.append(", ");
186
        		}
187
        		query.append("?");
188
        		paramValues.add(ipAddress[i]);
189
        	}
190
        	query.append(") ");
191
            clauseAdded = true;
192
        }
193
        if (principal != null) {
194
        	query.append("principal in (");
195
        	for (int i = 0; i < principal.length; i++) {
196
        		if (i > 0) {
197
            		query.append(", ");
198
        		}
199
        		query.append("?");
200
        		paramValues.add(principal[i]);
201
        	}
202
        	query.append(") ");
203
            clauseAdded = true;
204
        }
205
        if (docid != null) {
206
        	query.append("docid in (");
207
        	for (int i = 0; i < docid.length; i++) {
208
        		if (i > 0) {
209
            		query.append(", ");
210
        		}
211
        		query.append("?");
212
        		paramValues.add(docid[i]);
213
        	}
214
        	query.append(") ");
215
            clauseAdded = true;
216
        }
217
        if (event != null) {
218
        	query.append("event in (");
219
        	for (int i = 0; i < event.length; i++) {
220
        		if (i > 0) {
221
            		query.append(", ");
222
        		}
223
        		query.append("?");
224
        		paramValues.add(event[i]);
225
        	}
226
        	query.append(") ");
227
            clauseAdded = true;
228
        }
229
        if (startDate != null) {
230
            if (clauseAdded) {
231
                query.append(" and ");
232
            }
233
            query.append("date_logged > ?");
234
            clauseAdded = true;
235
            startIndex++;
236
        }
237
        if (endDate != null) {
238
            if (clauseAdded) {
239
                query.append(" and ");
240
            }
241
            query.append("date_logged < ?");
242
            clauseAdded = true;
243
            endIndex = startIndex + 1;
244
        }
245
        DBConnection dbConn = null;
246
        int serialNumber = -1;
247
        try {
248
            // Get a database connection from the pool
249
            dbConn = DBConnectionPool.getDBConnection("EventLog.getReport");
250
            serialNumber = dbConn.getCheckOutSerialNumber();
251

    
252
            // Execute the query statement
253
            PreparedStatement stmt = dbConn.prepareStatement(query.toString());
254
            //set the param values
255
            int parameterIndex = 1;
256
            for (String val: paramValues) {
257
            	stmt.setString(parameterIndex, val);
258
            }
259
            if (startDate != null) {
260
                stmt.setTimestamp(parameterIndex++, startDate); 
261
            }
262
            if (endDate != null) {
263
            	stmt.setTimestamp(parameterIndex++, endDate);
264
            }
265
            stmt.execute();
266
            ResultSet rs = stmt.getResultSet();
267
            //process the result and return it as an XML document
268
            resultDoc.append("<?xml version=\"1.0\"?>\n");
269
            resultDoc.append("<log>\n");
270
            while (rs.next()) {
271
                resultDoc.append(
272
                		generateXmlRecord(
273
                				rs.getString(1), //id
274
                				anonymous ? "" : rs.getString(2), //ip
275
                				rs.getString(3), //userAgent	
276
                				anonymous ? "" : rs.getString(4), //principal
277
                                rs.getString(5), 
278
                                rs.getString(6), 
279
                                rs.getTimestamp(7)));
280
            }
281
            resultDoc.append("</log>");
282
            stmt.close();
283
        } catch (SQLException e) {
284
        	logMetacat.info("Error while logging event to database: "
285
                            + e.getMessage());
286
        } finally {
287
            // Return database connection to the pool
288
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
289
        }
290
        return resultDoc.toString();
291
    }
292
    
293
    /**
294
     * Format each returned log record as an XML structure.
295
     * 
296
     * @param entryId the identifier of the log entry
297
     * @param ipAddress the internet protocol address for the event
298
     * @param the agent making the request
299
	 * @param principal the principal for the event (a username, etc)
300
	 * @param docid the identifier of the document to which the event applies
301
	 * @param event the string code for the event
302
     * @param dateLogged the date on which the event occurred
303
     * @return String containing the formatted XML
304
     */
305
    private String generateXmlRecord(String entryId, String ipAddress, String userAgent,
306
            String principal, String docid, String event, Timestamp dateLogged)
307
    {
308
        StringBuffer rec = new StringBuffer();
309
        rec.append("<logEntry>");
310
        rec.append(generateXmlElement("entryid", entryId));
311
        rec.append(generateXmlElement("ipAddress", ipAddress));
312
        rec.append(generateXmlElement("userAgent", userAgent));
313
        rec.append(generateXmlElement("principal", principal));
314
        rec.append(generateXmlElement("docid", docid));
315
        rec.append(generateXmlElement("event", event));
316
        rec.append(generateXmlElement("dateLogged", dateLogged.toString()));
317
        rec.append("</logEntry>\n");
318

    
319
        return rec.toString();
320
    }
321
    
322
    /**
323
     * Return an XML formatted element for a given name/value pair.
324
     * 
325
     * @param name the name of the xml element
326
     * @param value the content of the xml element
327
     * @return the formatted XML element as a String
328
     */
329
    private String generateXmlElement(String name, String value)
330
    {
331
        return "<" + name + ">" + value + "</" + name + ">";
332
    }
333
}
(35-35/64)