Solved

Get replication "last run" time through Java

Posted on 2009-07-14
16
808 Views
Last Modified: 2013-12-18
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).
0
Comment
Question by:stevenkbr
  • 6
  • 5
  • 2
  • +1
16 Comments
 
LVL 86

Expert Comment

by:CEHJ
ID: 24850719
Look for a command line solution in the docs and Runtime.exec a script that runs it
0
 

Author Comment

by:stevenkbr
ID: 24851698
Lotus Notes does not offer a CLI.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 24851741
It would probably need to be a tool *for* Lotus Notes
0
 
LVL 92

Expert Comment

by:objects
ID: 24854871
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.
0
 
LVL 22

Expert Comment

by:mbonaci
ID: 24857360
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

		sMsg$="" 

		For nCt%=0 To lEntries&-1

			sMsg$=sMsg$ & sList(nCt%) & Chr(13) & Chr(10) 

		Next
 

		' 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" 
 

	Else

		' 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 DIRECTION_NEVER=0

	Const DIRECTION_SEND=1

	Const DIRECTION_RECEIVE=2

	Const MAXPATH=256

	Const MAXALPHATIMEDATE=80

	Const ERR_SPECIAL_ID=578
 

	' custom type for storing individual replication history entries

	Type HIST_ENTRY

		RepTime As String

		ServerName As String

		FileName As String

		Direction As String

	End Type
 

	' Notes API types (based on C structs)

	Type TIMEDATE

		Innards1 As Long

		Innards2 As Long

	End Type
 

	Type REPLHIST_SUMMARY

		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 summary As REPLHIST_SUMMARY

	Dim entry() As HIST_ENTRY

	Dim nm As NotesName
 

	' what to return if no errors are found

	GetReplHistory=0 
 

	' prepare a string for API call

	sPath$=Space(MAXPATH)
 

	' 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%

	Else

		' get handle to replication history summary (sorted by server name) 

		nRetCode%=NSFDbGetReplHistorySummary(hDb&, 0, hSummary&, lEntries&) 
 

		If nRetCode% <> 0 Then

			GetReplHistory = nRetCode% 

		Else

	

			' 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

				hLock&=OSLockObject(hSummary&) 

	

				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

					Case DIRECTION_NEVER

						entry(nLoop%).Direction="Never Received" 

	

					Case DIRECTION_SEND

						entry(nLoop%).Direction="Send" 
 

					Case DIRECTION_RECEIVE

						entry(nLoop%).Direction="Receive" 
 

					End Select 
 

					' advance offset to next REPLHIST_SUMMARY struct

					hLock&=hLock&+Lenb(summary)

				Next
 

				' 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.

				sHold$=""

				sTemp$=String$(1, 0) 

				nLoop%=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)

						sHold$=""

						nLoop%=nLoop% + 1

					Else

						' build the string one char at a time

						sHold$=sHold$ & sTemp$

					End If
 

					' advance the offset

					hLock& = hLock& + 1

				Loop
 

				' release the lock on the history summary handle once we're done with it

				OSUnlockObject(hSummary&)
 

				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 & ")")

				Next
 

			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

0
 
LVL 86

Expert Comment

by:CEHJ
ID: 24857423
>>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:

http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
0
 
LVL 92

Expert Comment

by:objects
ID: 24857449
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.
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 22

Accepted Solution

by:
mbonaci earned 500 total points
ID: 24857456
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:
http://java.sun.com/docs/books/jni/html/jniTOC.html

Java programming with JNI on Developerworks:
http://www.ibm.com/developerworks/edu/j-dw-javajni-i.html

"Hello world" example:
http://www.codeproject.com/KB/java/jnibasics1.aspx


Hope this helps,
Mb¤
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 24857524
>>And which external program should he run using Runtime.exec() ?

The vb interpreter?
0
 
LVL 22

Expert Comment

by:mbonaci
ID: 24859429
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).
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 24860658
>>And which vb code he should run using vb interpreter?

The code you posted?
0
 
LVL 22

Expert Comment

by:mbonaci
ID: 24860807
That's LotusScript (although similar, it's not vb).
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 24861709
Yes it looked like vb to me although according to http://en.wikipedia.org/wiki/LotusScript

"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
0
 

Author Comment

by:stevenkbr
ID: 24955788
Thank you everyone for your comments.

I was hoping that someone would be able to point me to a solution using something like this:
   http://www-10.lotus.com/ldd/nd8forum.nsf/5049EE164C54799785256BFF00519260/A2CDCAD99B88050A8525729C0055EE63?OpenDocument

According to the article, there is a notes.jar that contains a com.ibm.notes.java.api 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.
0
 
LVL 22

Expert Comment

by:mbonaci
ID: 25114055
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.
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Java Flight Recorder and Java Mission Control together create a complete tool chain to continuously collect low level and detailed runtime information enabling after-the-fact incident analysis. Java Flight Recorder is a profiling and event collectio…
This article covers general Notes 8.5 troubleshooting information including recreating the Notes\Data folder.
Viewers will learn about the different types of variables in Java and how to declare them. Decide the type of variable desired: Put the keyword corresponding to the type of variable in front of the variable name: Use the equal sign to assign a v…
This tutorial explains how to use the VisualVM tool for the Java platform application. This video goes into detail on the Threads, Sampler, and Profiler tabs.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now