Project

General

Profile

1 2096 jones
/**
2
 *  '$RCSfile$'
3
 *  Copyright: 2004 Regents of the University of California and the
4
 *             National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author$'
7
 *     '$Date$'
8
 * '$Revision$'
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 2104 jones
import java.sql.PreparedStatement;
27 2111 jones
import java.sql.ResultSet;
28 2096 jones
import java.sql.SQLException;
29 2104 jones
import java.sql.Timestamp;
30 6595 leinfelder
import java.util.ArrayList;
31 2096 jones
import java.util.Date;
32 6595 leinfelder
import java.util.List;
33 2096 jones
34 2663 sgarg
import org.apache.log4j.Logger;
35 6944 leinfelder
import org.dataone.service.util.DateTimeMarshaller;
36 2663 sgarg
37 5015 daigle
import edu.ucsb.nceas.metacat.database.DBConnection;
38
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
39
40 2096 jones
/**
41
 * EventLog is used to intialize and store a log of events that occur in an
42
 * application. The events are registered with the logger as they occur, but
43
 * EventLog writes them to permenant storage when it is most convenient or
44
 * efficient. EventLog is a Singleton as there should always be only one object
45
 * for these logging events.
46
 *
47
 * TODO: Logging to the database needn't be synchronous with the event.
48
 * Instead, a separate thread can be launched that periodically sleeps and only
49
 * wakes periodically to see if metacat is idle.  The log event can be cached
50
 * and inserted later when the thread wakes and finds metacat idle.
51
 *
52
 * TODO: Write a function that archives a part of the log table to an
53
 * external text file so that the log table doesn't get to big.  This
54
 * function should be able to be called manually or on a schedule.
55
 *
56
 * TODO: Write an access function that returns an XML report for a
57
 * specific subset of events.  Users should be able to query on
58
 * principal, docid/rev, date, event, and possibly other fields.
59
 *
60
 * @author jones
61
 */
62
public class EventLog
63
{
64
    /**
65
     * The single instance of the event log that is always returned.
66
     */
67
    private static EventLog self = null;
68 2663 sgarg
    private Logger logMetacat = Logger.getLogger(EventLog.class);
69 2096 jones
70
    /**
71
     * A private constructor that initializes the class when getInstance() is
72
     * called.
73
     */
74
    private EventLog()
75
    {
76
    }
77
78
    /**
79
     * Return the single instance of the event log after initializing it if it
80
     * wasn't previously initialized.
81
     *
82
     * @return the single EventLog instance
83
     */
84
    public static EventLog getInstance()
85
    {
86
        if (self == null) {
87
            self = new EventLog();
88
        }
89
        return self;
90
    }
91
92
    /**
93
     * Log an event of interest to the application. The information logged can
94
     * include basic identification information about the principal or computer
95
     * that initiated the event.
96
     *
97 2099 jones
     * @param ipAddress the internet protocol address for the event
98 6542 leinfelder
     * @param userAgent the agent making the request
99 2099 jones
	 * @param principal the principal for the event (a username, etc)
100
	 * @param docid the identifier of the document to which the event applies
101
	 * @param event the string code for the event
102 2096 jones
     */
103 6542 leinfelder
    public void log(String ipAddress, String userAgent, String principal, String docid, String event) {
104
        EventLogData logData = new EventLogData(ipAddress, principal, docid, event);
105 2096 jones
        insertLogEntry(logData);
106
    }
107 2099 jones
108 2096 jones
    /**
109
     * Insert a single log event record to the database.
110
     *
111
     * @param logData the data to be logged when an event occurs
112
     */
113
    private void insertLogEntry(EventLogData logData)
114
    {
115
        String insertString = "insert into access_log"
116 6542 leinfelder
                + "(ip_address, user_agent, principal, docid, "
117 2096 jones
                + "event, date_logged) "
118 6595 leinfelder
                + "values ( ?, ?, ?, ?, ?, ? )";
119
120 2096 jones
        DBConnection dbConn = null;
121
        int serialNumber = -1;
122
        try {
123
            // Get a database connection from the pool
124 2111 jones
            dbConn = DBConnectionPool.getDBConnection("EventLog.insertLogEntry");
125 2096 jones
            serialNumber = dbConn.getCheckOutSerialNumber();
126
127
            // Execute the insert statement
128 2104 jones
            PreparedStatement stmt = dbConn.prepareStatement(insertString);
129 6595 leinfelder
130
            stmt.setString(1, logData.getIpAddress());
131
            stmt.setString(2, logData.getUserAgent());
132
            stmt.setString(3, logData.getPrincipal());
133
            stmt.setString(4, logData.getDocid());
134
            stmt.setString(5, logData.getEvent());
135
            stmt.setTimestamp(6, new Timestamp(new Date().getTime()));
136 2104 jones
            stmt.executeUpdate();
137 2096 jones
            stmt.close();
138
        } catch (SQLException e) {
139 2663 sgarg
        	logMetacat.error("Error while logging event to database: "
140
                    + e.getMessage());
141 2096 jones
        } finally {
142
            // Return database connection to the pool
143
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
144
        }
145
    }
146 2110 jones
147
    /**
148
     * Get a report of the log events that match a set of filters.  The
149 2111 jones
     * filter parameters can be null; log records are subset based on
150 2110 jones
     * non-null filter parameters.
151
     *
152
     * @param ipAddress the internet protocol address for the event
153
	 * @param principal the principal for the event (a username, etc)
154
	 * @param docid the identifier of the document to which the event applies
155
	 * @param event the string code for the event
156 2111 jones
	 * @param startDate beginning of date range for query
157
	 * @param endDate end of date range for query
158
	 * @return an XML-formatted report of the access log entries
159 2110 jones
     */
160 2111 jones
    public String getReport(String[] ipAddress, String[] principal, String[] docid,
161 5693 leinfelder
            String[] event, Timestamp startDate, Timestamp endDate, boolean anonymous)
162 2110 jones
    {
163 2111 jones
        StringBuffer resultDoc = new StringBuffer();
164
        StringBuffer query = new StringBuffer();
165 6542 leinfelder
        query.append("select entryid, ip_address, user_agent, principal, docid, "
166 2111 jones
            + "event, date_logged from access_log");
167
//                        + ""
168
//                        + "event, date_logged) " + "values (" + "'"
169
//                        + logData.getIpAddress() + "', " + "'"
170
//                        + logData.getPrincipal() + "', " + "'"
171
//                        + logData.getDocid() + "', " + "'" + logData.getEvent()
172
//                        + "', " + " ? " + ")";
173
        if (ipAddress != null || principal != null || docid != null
174
                        || event != null || startDate != null || endDate != null) {
175
            query.append(" where ");
176
        }
177
        boolean clauseAdded = false;
178
        int startIndex = 0;
179
        int endIndex = 0;
180
181 6595 leinfelder
        List<String> paramValues = new ArrayList<String>();
182 2111 jones
        if (ipAddress != null) {
183 6595 leinfelder
        	query.append("ip_address in (");
184
        	for (int i = 0; i < ipAddress.length; i++) {
185
        		if (i > 0) {
186
            		query.append(", ");
187
        		}
188
        		query.append("?");
189
        		paramValues.add(ipAddress[i]);
190
        	}
191
        	query.append(") ");
192 2111 jones
            clauseAdded = true;
193
        }
194
        if (principal != null) {
195 6595 leinfelder
        	query.append("principal in (");
196
        	for (int i = 0; i < principal.length; i++) {
197
        		if (i > 0) {
198
            		query.append(", ");
199
        		}
200
        		query.append("?");
201
        		paramValues.add(principal[i]);
202
        	}
203
        	query.append(") ");
204 2111 jones
            clauseAdded = true;
205
        }
206
        if (docid != null) {
207 6595 leinfelder
        	query.append("docid in (");
208
        	for (int i = 0; i < docid.length; i++) {
209
        		if (i > 0) {
210
            		query.append(", ");
211
        		}
212
        		query.append("?");
213
        		paramValues.add(docid[i]);
214
        	}
215
        	query.append(") ");
216 2111 jones
            clauseAdded = true;
217
        }
218
        if (event != null) {
219 6595 leinfelder
        	query.append("event in (");
220
        	for (int i = 0; i < event.length; i++) {
221
        		if (i > 0) {
222
            		query.append(", ");
223
        		}
224
        		query.append("?");
225
        		paramValues.add(event[i]);
226
        	}
227
        	query.append(") ");
228 2111 jones
            clauseAdded = true;
229
        }
230
        if (startDate != null) {
231
            if (clauseAdded) {
232
                query.append(" and ");
233
            }
234
            query.append("date_logged > ?");
235
            clauseAdded = true;
236
            startIndex++;
237
        }
238
        if (endDate != null) {
239
            if (clauseAdded) {
240
                query.append(" and ");
241
            }
242
            query.append("date_logged < ?");
243
            clauseAdded = true;
244
            endIndex = startIndex + 1;
245
        }
246
        DBConnection dbConn = null;
247
        int serialNumber = -1;
248
        try {
249
            // Get a database connection from the pool
250
            dbConn = DBConnectionPool.getDBConnection("EventLog.getReport");
251
            serialNumber = dbConn.getCheckOutSerialNumber();
252
253 2113 jones
            // Execute the query statement
254 2111 jones
            PreparedStatement stmt = dbConn.prepareStatement(query.toString());
255 6595 leinfelder
            //set the param values
256
            int parameterIndex = 1;
257
            for (String val: paramValues) {
258
            	stmt.setString(parameterIndex, val);
259
            }
260 6598 leinfelder
            if (startDate != null) {
261
                stmt.setTimestamp(parameterIndex++, startDate);
262 2111 jones
            }
263 6598 leinfelder
            if (endDate != null) {
264
            	stmt.setTimestamp(parameterIndex++, endDate);
265 2111 jones
            }
266
            stmt.execute();
267
            ResultSet rs = stmt.getResultSet();
268 2113 jones
            //process the result and return it as an XML document
269 2111 jones
            resultDoc.append("<?xml version=\"1.0\"?>\n");
270
            resultDoc.append("<log>\n");
271
            while (rs.next()) {
272 5693 leinfelder
                resultDoc.append(
273
                		generateXmlRecord(
274
                				rs.getString(1), //id
275
                				anonymous ? "" : rs.getString(2), //ip
276 6542 leinfelder
                				rs.getString(3), //userAgent
277
                				anonymous ? "" : rs.getString(4), //principal
278 5693 leinfelder
                                rs.getString(5),
279 6542 leinfelder
                                rs.getString(6),
280
                                rs.getTimestamp(7)));
281 2111 jones
            }
282
            resultDoc.append("</log>");
283
            stmt.close();
284
        } catch (SQLException e) {
285 2663 sgarg
        	logMetacat.info("Error while logging event to database: "
286
                            + e.getMessage());
287 2111 jones
        } finally {
288
            // Return database connection to the pool
289
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
290
        }
291
        return resultDoc.toString();
292 2110 jones
    }
293 2111 jones
294
    /**
295
     * Format each returned log record as an XML structure.
296
     *
297
     * @param entryId the identifier of the log entry
298
     * @param ipAddress the internet protocol address for the event
299 6542 leinfelder
     * @param the agent making the request
300 2111 jones
	 * @param principal the principal for the event (a username, etc)
301
	 * @param docid the identifier of the document to which the event applies
302
	 * @param event the string code for the event
303
     * @param dateLogged the date on which the event occurred
304
     * @return String containing the formatted XML
305
     */
306 6542 leinfelder
    private String generateXmlRecord(String entryId, String ipAddress, String userAgent,
307 2113 jones
            String principal, String docid, String event, Timestamp dateLogged)
308 2111 jones
    {
309
        StringBuffer rec = new StringBuffer();
310
        rec.append("<logEntry>");
311 2113 jones
        rec.append(generateXmlElement("entryid", entryId));
312
        rec.append(generateXmlElement("ipAddress", ipAddress));
313 6542 leinfelder
        rec.append(generateXmlElement("userAgent", userAgent));
314 2113 jones
        rec.append(generateXmlElement("principal", principal));
315
        rec.append(generateXmlElement("docid", docid));
316
        rec.append(generateXmlElement("event", event));
317 6944 leinfelder
        rec.append(generateXmlElement("dateLogged", DateTimeMarshaller.serializeDateToUTC(dateLogged)));
318 2111 jones
        rec.append("</logEntry>\n");
319
320
        return rec.toString();
321
    }
322
323
    /**
324
     * Return an XML formatted element for a given name/value pair.
325
     *
326
     * @param name the name of the xml element
327
     * @param value the content of the xml element
328
     * @return the formatted XML element as a String
329
     */
330 2113 jones
    private String generateXmlElement(String name, String value)
331 2111 jones
    {
332
        return "<" + name + ">" + value + "</" + name + ">";
333
    }
334 2096 jones
}