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: 2012-07-02 16:50:01 -0700 (Mon, 02 Jul 2012) $'
8
 * '$Revision: 7287 $'
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
import java.util.Vector;
34

    
35
import org.apache.log4j.Logger;
36
import org.dataone.service.types.v1.Event;
37
import org.dataone.service.types.v1.Identifier;
38
import org.dataone.service.types.v1.Log;
39
import org.dataone.service.types.v1.LogEntry;
40
import org.dataone.service.types.v1.NodeReference;
41
import org.dataone.service.types.v1.Subject;
42
import org.dataone.service.util.DateTimeMarshaller;
43

    
44
import edu.ucsb.nceas.metacat.database.DBConnection;
45
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
46
import edu.ucsb.nceas.metacat.properties.PropertyService;
47
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
48

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

    
79
    /**
80
     * A private constructor that initializes the class when getInstance() is
81
     * called.
82
     */
83
    private EventLog()
84
    {
85
    }
86

    
87
    /**
88
     * Return the single instance of the event log after initializing it if it
89
     * wasn't previously initialized.
90
     * 
91
     * @return the single EventLog instance
92
     */
93
    public static EventLog getInstance()
94
    {
95
        if (self == null) {
96
            self = new EventLog();
97
        }
98
        return self;
99
    }
100

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

    
262
            // Execute the query statement
263
            PreparedStatement stmt = dbConn.prepareStatement(query.toString());
264
            //set the param values
265
            int parameterIndex = 1;
266
            for (String val: paramValues) {
267
            	stmt.setString(parameterIndex, val);
268
            }
269
            if (startDate != null) {
270
                stmt.setTimestamp(parameterIndex++, startDate); 
271
            }
272
            if (endDate != null) {
273
            	stmt.setTimestamp(parameterIndex++, endDate);
274
            }
275
            stmt.execute();
276
            ResultSet rs = stmt.getResultSet();
277
            //process the result and return it as an XML document
278
            resultDoc.append("<?xml version=\"1.0\"?>\n");
279
            resultDoc.append("<log>\n");
280
            while (rs.next()) {
281
                resultDoc.append(
282
                		generateXmlRecord(
283
                				rs.getString(1), //id
284
                				anonymous ? "" : rs.getString(2), //ip
285
                				rs.getString(3), //userAgent	
286
                				anonymous ? "" : rs.getString(4), //principal
287
                                rs.getString(5), 
288
                                rs.getString(6), 
289
                                rs.getTimestamp(7)));
290
            }
291
            resultDoc.append("</log>");
292
            stmt.close();
293
        } catch (SQLException e) {
294
        	logMetacat.info("Error while logging event to database: "
295
                            + e.getMessage());
296
        } finally {
297
            // Return database connection to the pool
298
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
299
        }
300
        return resultDoc.toString();
301
    }
302
    
303
    
304
    
305
    public Log getD1Report(String[] ipAddress, String[] principal, String[] docid,
306
            Event event, Timestamp startDate, Timestamp endDate, boolean anonymous, Integer start, Integer count)
307
    {
308
        
309
        Log log = new Log();
310
    	
311
    	NodeReference memberNode = new NodeReference();
312
        String nodeId = "localhost";
313
        try {
314
            nodeId = PropertyService.getProperty("dataone.nodeId");
315
        } catch (PropertyNotFoundException e1) {
316
            // TODO Auto-generated catch block
317
            e1.printStackTrace();
318
        }
319
        memberNode.setValue(nodeId);
320
        
321
        StringBuffer query = new StringBuffer();
322
        query.append(
323
        		"select " +
324
        		"entryid, " +
325
        		"id.guid as identifier, " +
326
        		"ip_address, " +
327
        		"user_agent, " +
328
        		"principal, " +
329
        		"case " +
330
        		"	when event = 'insert' then 'create' " +
331
        		"	else event " +
332
        		"end as event, " +
333
        		"date_logged " +
334
        		"from access_log al, identifier id " +
335
        		"where al.docid = id.docid||'.'||id.rev "
336
        );
337
        
338
        
339
        boolean clauseAdded = true;
340
        int startIndex = 0;
341
        int endIndex = 0;
342
        
343
        List<String> paramValues = new ArrayList<String>();
344
        if (ipAddress != null) {
345
        	query.append("ip_address in (");
346
        	for (int i = 0; i < ipAddress.length; i++) {
347
        		if (i > 0) {
348
            		query.append(", ");
349
        		}
350
        		query.append("?");
351
        		paramValues.add(ipAddress[i]);
352
        	}
353
        	query.append(") ");
354
            clauseAdded = true;
355
        }
356
        if (principal != null) {
357
        	query.append("principal in (");
358
        	for (int i = 0; i < principal.length; i++) {
359
        		if (i > 0) {
360
            		query.append(", ");
361
        		}
362
        		query.append("?");
363
        		paramValues.add(principal[i]);
364
        	}
365
        	query.append(") ");
366
            clauseAdded = true;
367
        }
368
        if (docid != null) {
369
        	query.append("al.docid in (");
370
        	for (int i = 0; i < docid.length; i++) {
371
        		if (i > 0) {
372
            		query.append(", ");
373
        		}
374
        		query.append("?");
375
        		paramValues.add(docid[i]);
376
        	}
377
        	query.append(") ");
378
            clauseAdded = true;
379
        }
380
        if (event != null) {
381
        	query.append("event in (");
382
    		query.append("?");
383
    		String eventString = event.xmlValue();
384
    		if (event.equals(Event.CREATE)) {
385
    			eventString = "insert";
386
    		}
387
    		paramValues.add(eventString);
388
        	query.append(") ");
389
            clauseAdded = true;
390
        }
391
        if (startDate != null) {
392
            if (clauseAdded) {
393
                query.append(" and ");
394
            }
395
            query.append("date_logged >= ?");
396
            clauseAdded = true;
397
            startIndex++;
398
        }
399
        if (endDate != null) {
400
            if (clauseAdded) {
401
                query.append(" and ");
402
            }
403
            query.append("date_logged < ?");
404
            clauseAdded = true;
405
            endIndex = startIndex + 1;
406
        }
407
        DBConnection dbConn = null;
408
        int serialNumber = -1;
409
        try {
410
            // Get a database connection from the pool
411
            dbConn = DBConnectionPool.getDBConnection("EventLog.getD1Report");
412
            serialNumber = dbConn.getCheckOutSerialNumber();
413

    
414
            // Execute the query statement
415
            PreparedStatement stmt = dbConn.prepareStatement(query.toString());
416
            //set the param values
417
            int parameterIndex = 1;
418
            for (String val: paramValues) {
419
            	stmt.setString(parameterIndex, val);
420
            }
421
            if (startDate != null) {
422
                stmt.setTimestamp(parameterIndex++, startDate); 
423
            }
424
            if (endDate != null) {
425
            	stmt.setTimestamp(parameterIndex++, endDate);
426
            }
427
            stmt.execute();
428
            ResultSet rs = stmt.getResultSet();
429
            //process the result and return it
430
            List<LogEntry> logs = new Vector<LogEntry>();
431
            
432
            while (rs.next()) {
433
            	LogEntry logEntry = new LogEntry();
434
            	logEntry.setEntryId(rs.getString(1));
435
            	
436
            	Identifier identifier = new Identifier();
437
            	identifier.setValue(rs.getString(2));
438
				logEntry.setIdentifier(identifier);
439

    
440
            	logEntry.setIpAddress(anonymous ? "N/A" : rs.getString(3));
441
            	String userAgent = "N/A";
442
            	if (rs.getString(4) != null) {
443
            		userAgent = rs.getString(4);
444
            	}
445
            	logEntry.setUserAgent(userAgent);
446
            	
447
            	Subject subject = new Subject();
448
            	subject.setValue(anonymous ? "N/A" : rs.getString(5));
449
				logEntry.setSubject(subject);
450
				
451
				
452
				String logEventString = rs.getString(6);
453
				Event logEvent = Event.convert(logEventString );
454
				if (logEvent == null) {
455
					logMetacat.info("Skipping uknown DataONE Event type: " + logEventString);
456
					continue;
457
				}
458
				logEntry.setEvent(logEvent);
459
				logEntry.setDateLogged(rs.getTimestamp(7));
460
				
461
				logEntry.setNodeIdentifier(memberNode);
462
				logs.add(logEntry);
463
            }
464
            
465
			log.setLogEntryList(logs);
466
			// d1 paging
467
		    int total = logs.size();
468
		    if (start != null && count != null) {
469
		    	int toIndex = start + count;
470
		    	// do not exceed total
471
		    	toIndex = Math.min(toIndex, total);
472
		    	// do not start greater than total
473
		    	start = Math.min(start, total);
474
		    	// sub set of the list
475
		    	logs = new ArrayList<LogEntry>(logs.subList(start, toIndex));
476
		    }
477

    
478
		    log.setLogEntryList(logs);
479
		    log.setStart(start);
480
		    log.setCount(logs.size());
481
		    log.setTotal(total);
482

    
483
            stmt.close();
484
        } catch (SQLException e) {
485
        	logMetacat.error("Error while getting log events: " + e.getMessage(), e);
486
        } finally {
487
            // Return database connection to the pool
488
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
489
        }
490
        return log;
491
    }
492
    
493
    /**
494
     * Format each returned log record as an XML structure.
495
     * 
496
     * @param entryId the identifier of the log entry
497
     * @param ipAddress the internet protocol address for the event
498
     * @param the agent making the request
499
	 * @param principal the principal for the event (a username, etc)
500
	 * @param docid the identifier of the document to which the event applies
501
	 * @param event the string code for the event
502
     * @param dateLogged the date on which the event occurred
503
     * @return String containing the formatted XML
504
     */
505
    private String generateXmlRecord(String entryId, String ipAddress, String userAgent,
506
            String principal, String docid, String event, Timestamp dateLogged)
507
    {
508
        StringBuffer rec = new StringBuffer();
509
        rec.append("<logEntry>");
510
        rec.append(generateXmlElement("entryid", entryId));
511
        rec.append(generateXmlElement("ipAddress", ipAddress));
512
        rec.append(generateXmlElement("userAgent", userAgent));
513
        rec.append(generateXmlElement("principal", principal));
514
        rec.append(generateXmlElement("docid", docid));
515
        rec.append(generateXmlElement("event", event));
516
        rec.append(generateXmlElement("dateLogged", DateTimeMarshaller.serializeDateToUTC(dateLogged)));
517
        rec.append("</logEntry>\n");
518

    
519
        return rec.toString();
520
    }
521
    
522
    /**
523
     * Return an XML formatted element for a given name/value pair.
524
     * 
525
     * @param name the name of the xml element
526
     * @param value the content of the xml element
527
     * @return the formatted XML element as a String
528
     */
529
    private String generateXmlElement(String name, String value)
530
    {
531
        return "<" + name + ">" + value + "</" + name + ">";
532
    }
533
}
(35-35/64)