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-10-19 10:20:56 -0700 (Fri, 19 Oct 2012) $'
8
 * '$Revision: 7409 $'
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.metacat.util.DocumentUtil;
48
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
49

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

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

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

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

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

    
444
            // Execute the query statement
445
            PreparedStatement stmt = dbConn.prepareStatement(query.toString());
446
            //set the param values
447
            int parameterIndex = 1;
448
            for (String val: paramValues) {
449
            	stmt.setString(parameterIndex++, val);
450
            }
451
            if (startDate != null) {
452
                stmt.setTimestamp(parameterIndex++, startDate); 
453
            }
454
            if (endDate != null) {
455
            	stmt.setTimestamp(parameterIndex++, endDate);
456
            }
457
            stmt.execute();
458
            ResultSet rs = stmt.getResultSet();
459
            //process the result and return it
460
            List<LogEntry> logs = new Vector<LogEntry>();
461
            
462
            while (rs.next()) {
463
            	LogEntry logEntry = new LogEntry();
464
            	logEntry.setEntryId(rs.getString(1));
465
            	
466
            	Identifier identifier = new Identifier();
467
            	identifier.setValue(rs.getString(2));
468
				logEntry.setIdentifier(identifier);
469

    
470
            	logEntry.setIpAddress(anonymous ? "N/A" : rs.getString(3));
471
            	String userAgent = "N/A";
472
            	if (rs.getString(4) != null) {
473
            		userAgent = rs.getString(4);
474
            	}
475
            	logEntry.setUserAgent(userAgent);
476
            	
477
            	Subject subject = new Subject();
478
            	subject.setValue(anonymous ? "N/A" : rs.getString(5));
479
				logEntry.setSubject(subject);
480
				
481
				
482
				String logEventString = rs.getString(6);
483
				Event logEvent = Event.convert(logEventString );
484
				if (logEvent == null) {
485
					logMetacat.info("Skipping uknown DataONE Event type: " + logEventString);
486
					continue;
487
				}
488
				logEntry.setEvent(logEvent);
489
				logEntry.setDateLogged(rs.getTimestamp(7));
490
				
491
				logEntry.setNodeIdentifier(memberNode);
492
				logs.add(logEntry);
493
            }
494
            
495
			log.setLogEntryList(logs);
496
			// d1 paging
497
		    int total = logs.size();
498
		    if (start != null && count != null) {
499
		    	int toIndex = start + count;
500
		    	// do not exceed total
501
		    	toIndex = Math.min(toIndex, total);
502
		    	// do not start greater than total
503
		    	start = Math.min(start, total);
504
		    	// sub set of the list
505
		    	logs = new ArrayList<LogEntry>(logs.subList(start, toIndex));
506
		    }
507

    
508
		    log.setLogEntryList(logs);
509
		    log.setStart(start);
510
		    log.setCount(logs.size());
511
		    log.setTotal(total);
512

    
513
            stmt.close();
514
        } catch (SQLException e) {
515
        	logMetacat.error("Error while getting log events: " + e.getMessage(), e);
516
        } finally {
517
            // Return database connection to the pool
518
            DBConnectionPool.returnDBConnection(dbConn, serialNumber);
519
        }
520
        return log;
521
    }
522
    
523
    /**
524
     * Format each returned log record as an XML structure.
525
     * 
526
     * @param entryId the identifier of the log entry
527
     * @param ipAddress the internet protocol address for the event
528
     * @param the agent making the request
529
	 * @param principal the principal for the event (a username, etc)
530
	 * @param docid the identifier of the document to which the event applies
531
	 * @param event the string code for the event
532
     * @param dateLogged the date on which the event occurred
533
     * @return String containing the formatted XML
534
     */
535
    private String generateXmlRecord(String entryId, String ipAddress, String userAgent,
536
            String principal, String docid, String event, Timestamp dateLogged)
537
    {
538
        StringBuffer rec = new StringBuffer();
539
        rec.append("<logEntry>");
540
        rec.append(generateXmlElement("entryid", entryId));
541
        rec.append(generateXmlElement("ipAddress", ipAddress));
542
        rec.append(generateXmlElement("userAgent", userAgent));
543
        rec.append(generateXmlElement("principal", principal));
544
        rec.append(generateXmlElement("docid", docid));
545
        rec.append(generateXmlElement("event", event));
546
        rec.append(generateXmlElement("dateLogged", DateTimeMarshaller.serializeDateToUTC(dateLogged)));
547
        rec.append("</logEntry>\n");
548

    
549
        return rec.toString();
550
    }
551
    
552
    /**
553
     * Return an XML formatted element for a given name/value pair.
554
     * 
555
     * @param name the name of the xml element
556
     * @param value the content of the xml element
557
     * @return the formatted XML element as a String
558
     */
559
    private String generateXmlElement(String name, String value)
560
    {
561
        return "<" + name + ">" + value + "</" + name + ">";
562
    }
563
}
(35-35/64)