Get replication "last run" time through Java

I am looking for a way to programmatically (through Java) determine the date and time a Lotus Notes database was last replicated.

I have a Java program that uses NotesSQL to query a Notes database on my server, however I am running into performance problems (queries take a long time to come back). I would like to set it up such that my program can determine whether to query a local replica of the database if it has replicated recently or the server copy of the database otherwise.

There is no way to do this by looking at the actual replica nsf file on my local machine since the timestamp on the file seems to update whether or not I replicate.

Is there any way to do this in Java and, if so, how? Ideally, whatever solution I end up using would work on both Notes 7 and 8 (since I use both in my environment).
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Look for a command line solution in the docs and Runtime.exec a script that runs it
stevenkbrAuthor Commented:
Lotus Notes does not offer a CLI.
It would probably need to be a tool *for* Lotus Notes
CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

Mick BarryJava DeveloperCommented:
Not really a Java problem.  You need to first work out how to get the date, once you have that getting it from Java is the easy part.
I found this LotusScript code in my code library (you'll have to use those win API functions):
Sub Click(Source As Button)
	' This sample button calls the GetReplHistory function and displays a database's replication history
	' sorted by server name in the format <server> <filename> <date> <time> (<direction>). For example:
	' ServerA/CompanyX HR\hris.nsf 08/01/2000 08:01:54 PM (Receive)
	' ServerA/CompanyX HR\hris.nsf 08/01/2000 02:32:12 PM (Send)
	' If errors were encountered by the Notes API calls in GetReplHistory, the 
	' OSLoadString API is used to determine the error string returned.
	Dim session As New NotesSession
	Dim db As NotesDatabase
	Dim sList() As String
	Dim nCt%, nReturn%
	Dim lEntries&
	Dim sMsg$, sBuffer$
	Set db=session.GetDatabase("", "test.nsf")
	' retrieve the history sorted by server name
	nReturn%=GetReplHistory(db, sList, lEntries&)
	If nReturn% = 0 Then
		' no errors--build a string to show in a msgbox
		For nCt%=0 To lEntries&-1
			sMsg$=sMsg$ & sList(nCt%) & Chr(13) & Chr(10) 
		' show the string
		Msgbox sMsg$, 0, "Replication History for " & db.Title
	Elseif nReturn=ERR_SPECIAL_ID Then
		' no history found
		Msgbox "There is no replication history for this database.", 32, "Replication History" 
		' find out what the error string is from the API
		sBuffer$=String$(256, 0)
		OSLoadString 0, nReturn%, sBuffer$, 255
		' errors found
		Msgbox "Error found in retrieving history: " & sBuffer$, 48, "Replication History Error"
	End If
End Sub
	' Notes API constants
	Const MAXPATH=256
	' custom type for storing individual replication history entries
		RepTime As String
		ServerName As String
		FileName As String
		Direction As String
	End Type
	' Notes API types (based on C structs)
		Innards1 As Long
		Innards2 As Long
	End Type
		ReplicationTime As TIMEDATE
		AccessLevel As Integer
		AccessFlags As Integer
		Direction As Integer
		ServerNameOffset As Long
		ServerNameLength As Integer
		FileNameLength As Integer
		Spare1 As Long
		Spare2 As Long
	End Type
' Notes API declares
Declare Sub OSPathNetConstruct Lib "nnotes" (Byval portName$, Byval ServerName$, Byval FileName$, Byval retPathName$)
Declare Function NSFDbGetReplHistorySummary% Lib "nnotes" (Byval hDb&, Byval Flags&, rethSummary&, retNumEntries&)
Declare Function NSFDbOpen% Lib "nnotes" (Byval PathName$, hDB&)
Declare Function NSFDbClose% Lib "nnotes" (Byval hDB&)
Declare Function OSMemFree% Lib "nnotes" (Byval Handle&)
Declare Function OSLockObject& Lib "nnotes" (Byval nHandle&)
Declare Function OSUnlockObject% Lib "nnotes" (Byval nHandle&)
Declare Function ConvertTIMEDATEToText% Lib "nnotes" (Byval intFormat&, Byval TextFormat&, InputTime As TIMEDATE, Byval retTextBuffer$, Byval TextBufferLength%, retTextLength%)
Declare Function OSLoadString% Lib "nnotes" (Byval hModule&, Byval StringCode%, Byval retBuffer$, BufferLength%)
' Win32 API declares
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As REPLHIST_SUMMARY, Byval pSource&, Byval dwLength&)
Declare Sub CopyMemoryStr Lib "kernel32" Alias "RtlMoveMemory" (Byval pDest$, Byval pSource&, Byval dwLength&)
Function GetReplHistory(db As NotesDatabase, sList() As String, lEntries&) As Integer
	' This function returns the replication history for a database in the same format as you would see 
	' by selecting "File - Replication - History" (and clicking Server name radio button) in the Notes R5 client. 
	' The function has (3) parameters: 
	' 1. db (NotesDatabase) - [Input] Indicates the database you wish to open
	' 2. sList (String array) - [Output] Returns array of strings that will hold the formatted history
	' 3. lEntries (Long) - [Output] Returns the number of entries returned from the NSFDbGetReplHistorySummary call
	' The function itself returns an integer. If no errors are found, it returns a 0. Otherwise, it returns the error 
	' code returned by the Notes API.
	Dim hDb&, hLock&, hSummary&
	Dim nLoop%, cbReturn%, nPos%, nRetCode%
	Dim sPath$, sHold$, sRepTime$, sTemp$, sServer$, sFileName$
	Dim entry() As HIST_ENTRY
	Dim nm As NotesName
	' what to return if no errors are found
	' prepare a string for API call
	' create an API-friendly path to the db and open it
	OSPathNetConstruct "", db.Server, db.FilePath, sPath$
	nRetCode%=NSFDbOpen(sPath$, hDb&)
	If nRetCode% <> 0 Then
		GetReplHistory = nRetCode%
		' get handle to replication history summary (sorted by server name) 
		nRetCode%=NSFDbGetReplHistorySummary(hDb&, 0, hSummary&, lEntries&) 
		If nRetCode% <> 0 Then
			GetReplHistory = nRetCode% 
			' process only if there are entries in the history summary
			If lEntries& > 0 Then
				Redim entry(lEntries& - 1)
				sRepTime$=Space(MAXALPHATIMEDATE + 1) 
				' lock down the handle to the history summary so we can get at the data
				For nLoop%=0 To lEntries&-1
					' extract replication history by looping over the array of REPLHIST_SUMMARY structs
					CopyMemory summary, hLock&, Lenb(summary)
					' convert Notes TIMEDATE to a legible text string for replication time
					ConvertTIMEDATEToText 0, 0, summary.ReplicationTime, sRepTime$, MAXALPHATIMEDATE, cbReturn%
					entry(nLoop%).RepTime=Left$(sRepTime$, cbReturn%) 
					' get replication direction
					Select Case summary.Direction
						entry(nLoop%).Direction="Never Received" 
					End Select 
					' advance offset to next REPLHIST_SUMMARY struct
				' as server/filenames are not part of the REPLHIST_SUMMARY struct, but rather at the end of the
				' array of these structs, we'll need to grab this info one char at a time (for each entry we find) in the
				' format:
				' 	CN=ServerA/O=OrgA!!myfile.nsf/0CN=ServerB/O=OrgAA!!myfile.nsf/0, etc.
				sTemp$=String$(1, 0) 
				Do While nLoop% < lEntries&
					CopyMemoryStr sTemp$, hLock&, 1
					If sTemp$ = Chr$(0) Then
						' parse out server and filename
						nPos%=Instr(1, sHold$, "!!")
						entry(nLoop%).ServerName=Left$(sHold$, nPos%-1)
						entry(nLoop%).FileName=Right$(sHold$, Len(sHold$)-nPos%-1)
						nLoop%=nLoop% + 1
						' build the string one char at a time
						sHold$=sHold$ & sTemp$
					End If
					' advance the offset
					hLock& = hLock& + 1
				' release the lock on the history summary handle once we're done with it
				Redim sList(lEntries&-1)
				For nLoop%=0 To lEntries&-1
					' populate the array of entries to return to the caller
					Set nm=New NotesName(entry(nLoop%).ServerName)
					sList(nLoop%)=Trim$(nm.Abbreviated & " " & entry(nLoop%).FileName & " " & entry(nLoop%).RepTime & " (" & entry(nLoop%).Direction & ")")
			End If
		End If
		' free any open handles to the history summary and/or the db
		If hSummary& <> 0 Then OSMemFree hSummary& 
		If hDb& <> 0 Then NSFDbClose hDb&
	End If
End Function

Open in new window

>>Look for a command line solution in the docs and Runtime.exec a script that runs it

There you are: you can Runtime.exec the script that's just been posted. Make sure you read and understand the following if you do so:
Mick BarryJava DeveloperCommented:
looks like you'd be better off changing the replication process to log a timestamp to a file and look that up.
Failing that you could use JNI to make the appropraite native calls.
And which external program should he run using Runtime.exec() ?
That's the wrong approach.

Use WIN/Lotus API functions (called native in Java) used in the LS code I posted.
You'll have to use JNI.

Tutorial on JNI:

Java programming with JNI on Developerworks:

"Hello world" example:

Hope this helps,

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
>>And which external program should he run using Runtime.exec() ?

The vb interpreter?
And which vb code he should run using vb interpreter?

Another approach:
If you have access to Lotus db design you could create LotusScript agent or web service and then call it from your Java code (using URL).
>>And which vb code he should run using vb interpreter?

The code you posted?
That's LotusScript (although similar, it's not vb).
Yes it looked like vb to me although according to

"Code can often be copied without modification from one to the other, and programmers familiar with one can easily understand the syntax and structure of code in the other."

Another option is to use the OLE (COM) interface that Notes exposes with a Java-COM bridge
stevenkbrAuthor Commented:
Thank you everyone for your comments.

I was hoping that someone would be able to point me to a solution using something like this:

According to the article, there is a notes.jar that contains a API.  However, I cannot find a lot more about it other than this one article...I especially cannot find any examples or pointers on getting replication times through this API.

If no one has any experience/tips for the Java-based solution, I will look into the native code solutions referenced above, however I really wanted to try to keep to one programming language since most team members primarily know Java.
Java support for Domino provide access to Domino objects and contains the same classes as LotusScript, so that would not help you.
There's no written Java solution for this, so use the API functions from my code and comment (24857456) to code it using Java.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Lotus IBM

From novice to tech pro — start learning today.