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-12 16:04:35 -0700 (Thu, 12 Jul 2012) $'
8
 * '$Revision: 7309 $'
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
        	if (clauseAdded) {
205
                query.append(" and ");
206
            }
207
        	query.append("principal in (");
208
        	for (int i = 0; i < principal.length; i++) {
209
        		if (i > 0) {
210
            		query.append(", ");
211
        		}
212
        		query.append("?");
213
        		paramValues.add(principal[i]);
214
        	}
215
        	query.append(") ");
216
            clauseAdded = true;
217
        }
218
        if (docid != null) {
219
        	if (clauseAdded) {
220
                query.append(" and ");
221
            }
222
        	query.append("docid in (");
223
        	for (int i = 0; i < docid.length; i++) {
224
        		if (i > 0) {
225
            		query.append(", ");
226
        		}
227
        		query.append("?");
228
        		paramValues.add(docid[i]);
229
        	}
230
        	query.append(") ");
231
            clauseAdded = true;
232
        }
233
        if (event != null) {
234
        	if (clauseAdded) {
235
                query.append(" and ");
236
            }
237
        	query.append("event in (");
238
        	for (int i = 0; i < event.length; i++) {
239
        		if (i > 0) {
240
            		query.append(", ");
241
        		}
242
        		query.append("?");
243
        		paramValues.add(event[i]);
244
        	}
245
        	query.append(") ");
246
            clauseAdded = true;
247
        }
248
        if (startDate != null) {
249
            if (clauseAdded) {
250
                query.append(" and ");
251
            }
252
            query.append("date_logged >= ?");
253
            clauseAdded = true;
254
            startIndex++;
255
        }
256
        if (endDate != null) {
257
            if (clauseAdded) {
258
                query.append(" and ");
259
            }
260
            query.append("date_logged < ?");
261
            clauseAdded = true;
262
            endIndex = startIndex + 1;
263
        }
264
        DBConnection dbConn = null;
265
        int serialNumber = -1;
266
        try {
267
            // Get a database connection from the pool
268
            dbConn = DBConnectionPool.getDBConnection("EventLog.getReport");
269
            serialNumber = dbConn.getCheckOutSerialNumber();
270

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

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

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

    
499
		    log.setLogEntryList(logs);
500
		    log.setStart(start);
501
		    log.setCount(logs.size());
502
		    log.setTotal(total);
503

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

    
540
        return rec.toString();
541
    }
542
    
543
    /**
544
     * Return an XML formatted element for a given name/value pair.
545
     * 
546
     * @param name the name of the xml element
547
     * @param value the content of the xml element
548
     * @return the formatted XML element as a String
549
     */
550
    private String generateXmlElement(String name, String value)
551
    {
552
        return "<" + name + ">" + value + "</" + name + ">";
553
    }
554
}
(35-35/64)