From 5974d1b7b9ed013c0e917be7974f380599d0cb34 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Thu, 13 Mar 2014 12:04:51 +0200 Subject: [PATCH] Adds option to limit the number of history records stored, FIFO. Updates timestamp of records on record update. --- .../callhistory/CallHistoryServiceImpl.java | 3 +- .../impl/history/HistoryWriterImpl.java | 107 ++++++++++++++++-- .../service/history/HistoryWriter.java | 15 +++ .../slick/history/TestHistoryService.java | 62 ++++++++++ 4 files changed, 178 insertions(+), 9 deletions(-) diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java index 5995b4573..a4db17d10 100644 --- a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java +++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java @@ -1138,7 +1138,8 @@ public void updateCallRecordPeerSecondaryAddress(final Date date, } HistoryWriter historyWriter = history.getWriter(); - HistoryWriter.HistoryRecordUpdater updater = new HistoryWriter.HistoryRecordUpdater() + HistoryWriter.HistoryRecordUpdater updater + = new HistoryWriter.HistoryRecordUpdater() { private HistoryRecord record; diff --git a/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java b/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java index b36e791f4..882b435df 100644 --- a/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java +++ b/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java @@ -61,22 +61,44 @@ protected HistoryWriterImpl(HistoryImpl historyImpl) public void addRecord(HistoryRecord record) throws IOException { - this.addRecord(record.getPropertyNames(), record.getPropertyValues(), - record.getTimestamp()); + this.addRecord( + record.getPropertyNames(), + record.getPropertyValues(), + record.getTimestamp(), + -1); } public void addRecord(String[] propertyValues) throws IOException { - addRecord( - structPropertyNames, - propertyValues, - new Date()); + addRecord(structPropertyNames, propertyValues, new Date(), -1); } public void addRecord(String[] propertyValues, Date timestamp) throws IOException { - this.addRecord(structPropertyNames, propertyValues, timestamp); + this.addRecord(structPropertyNames, propertyValues, timestamp, -1); + } + + /** + * Stores the passed propertyValues complying with the + * historyRecordStructure. + * + * @param propertyValues + * The values of the record. + * @param maxNumberOfRecords the maximum number of records to keep or + * value of -1 to ignore this param. + * + * @throws IOException + */ + public void addRecord(String[] propertyValues, + int maxNumberOfRecords) + throws IOException + { + addRecord( + structPropertyNames, + propertyValues, + new Date(), + maxNumberOfRecords); } /** @@ -87,12 +109,15 @@ public void addRecord(String[] propertyValues, Date timestamp) * @param propertyNames String[] * @param propertyValues String[] * @param date Date + * @param maxNumberOfRecords the maximum number of records to keep or + * value of -1 to ignore this param. * @throws InvalidParameterException * @throws IOException */ private void addRecord(String[] propertyNames, String[] propertyValues, - Date date) + Date date, + int maxNumberOfRecords) throws InvalidParameterException, IOException { // Synchronized to assure that two concurrent threads can insert records @@ -111,6 +136,15 @@ private void addRecord(String[] propertyNames, Node root = this.currentDoc.getFirstChild(); synchronized (root) { + // if we have setting for max number of records, + // check the number and when exceed them, remove the first one + if( maxNumberOfRecords > -1 + && this.currentDocElements >= maxNumberOfRecords) + { + // lets remove the first one + removeFirstRecord(root); + } + Element elem = this.currentDoc.createElement("record"); SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); @@ -174,6 +208,51 @@ private void addRecord(String[] propertyNames, } } + /** + * Finds the oldest node by timestamp in current root and deletes it. + * @param root where to search for records + */ + private void removeFirstRecord(Node root) + { + SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); + + NodeList nodes = ((Element)root).getElementsByTagName("record"); + + Node oldestNode = null; + Date oldestTimeStamp = null; + + Node node; + for (int i = 0; i < nodes.getLength(); i++) + { + node = nodes.item(i); + + Date timestamp; + String ts + = node.getAttributes().getNamedItem("timestamp").getNodeValue(); + try + { + timestamp = sdf.parse(ts); + } + catch (ParseException e) + { + timestamp = new Date(Long.parseLong(ts)); + } + + if(oldestNode == null + || (oldestTimeStamp.after(timestamp))) + { + oldestNode = node; + oldestTimeStamp = timestamp; + continue; + } + + } + + if(oldestNode != null) + root.removeChild(oldestNode); + } + + /** * If no file is currently loaded loads the last opened file. If it does not * exists or if the current file was set - create a new file. @@ -293,6 +372,12 @@ public void updateRecord(String idProperty, String idValue, node.appendChild(propertyElement); } + // change the timestamp, to reflect there was a change + SimpleDateFormat sdf + = new SimpleDateFormat(DATE_FORMAT); + ((Element)node).setAttribute("timestamp", + sdf.format(new Date())); + changed = true; break; } @@ -349,6 +434,12 @@ public void updateRecord(HistoryRecordUpdater updater) throws IOException if(!updater.isMatching()) continue; + // change the timestamp, to reflect there was a change + SimpleDateFormat sdf + = new SimpleDateFormat(DATE_FORMAT); + ((Element)node).setAttribute("timestamp", + sdf.format(new Date())); + Map updates = updater.getUpdateChanges(); for(String nodeName : updates.keySet()) { diff --git a/src/net/java/sip/communicator/service/history/HistoryWriter.java b/src/net/java/sip/communicator/service/history/HistoryWriter.java index bb7ca3050..23844fb02 100644 --- a/src/net/java/sip/communicator/service/history/HistoryWriter.java +++ b/src/net/java/sip/communicator/service/history/HistoryWriter.java @@ -38,6 +38,21 @@ public interface HistoryWriter { */ void addRecord(String[] propertyValues) throws IOException; + /** + * Stores the passed propertyValues complying with the + * historyRecordStructure. + * + * @param propertyValues + * The values of the record. + * @param maxNumberOfRecords the maximum number of records to keep or + * value of -1 to ignore this param. + * + * @throws IOException + */ + public void addRecord(String[] propertyValues, + int maxNumberOfRecords) + throws IOException; + /** * Stores the passed propertyValues complying with the * historyRecordStructure. diff --git a/test/net/java/sip/communicator/slick/history/TestHistoryService.java b/test/net/java/sip/communicator/slick/history/TestHistoryService.java index 93fda1d7a..74ad5fbcd 100644 --- a/test/net/java/sip/communicator/slick/history/TestHistoryService.java +++ b/test/net/java/sip/communicator/slick/history/TestHistoryService.java @@ -44,6 +44,7 @@ public static Test suite() suite.addTest(new TestHistoryService("testReadRecords")); suite.addTest(new TestHistoryService("testPurgeLocallyStoredHistory")); suite.addTest(new TestHistoryService("testCreatingHistoryIDFromFS")); + suite.addTest(new TestHistoryService("testWriteRecordsWithMaxNumber")); return suite; } @@ -227,4 +228,65 @@ private void testHistoryIDCreate(String[] strArr) testNoSpecialCharsIDFSRead.getID()[i]); } } + + public void testWriteRecordsWithMaxNumber() + { + HistoryWriter writer = this.history.getWriter(); + HistoryReader reader = this.history.getReader(); + + try + { + + for (int i = 0; i < 20; i++) + { + writer.addRecord(new String[] { "" + i, + "name" + i, + i % 2 == 0 ? "m" : "f" }, 20); + synchronized(this) + { + try + { + wait(100); + } + catch(Throwable t){} + } + } + + QueryResultSet recs = reader.findLast(20); + int count = 0; + while(recs.hasNext()) + { + count++; + recs.next(); + } + + assertEquals( "Wrong count of messages", 20, count); + + writer.addRecord(new String[] { "" + 21, + "name" + 21, "f" }, 20); + + recs = reader.findLast(20); + count = 0; + boolean foundFirstMessage = false; + while(recs.hasNext()) + { + count++; + HistoryRecord hr = recs.next(); + + if(hr.getPropertyValues()[0].equals("0")) + foundFirstMessage = true; + } + + assertEquals( "Wrong count of messages", 20, count); + + assertFalse("Wrong message removed, must be the first one", + foundFirstMessage); + + } catch (Exception e) + { + e.printStackTrace(); + fail("Could not write records. Reason: " + e); + } + } + }