My session slides from DNUG46
Here are the slides from my DNUG46 session about all things new in Notes / Domino Development in V10.x. FP2 fixes are included
Thanks to all who attended the session.
Here are the slides from my DNUG46 session about all things new in Notes / Domino Development in V10.x. FP2 fixes are included
Thanks to all who attended the session.
If you are already using the NotesHTTPRequest and / or NotesJSONNavigator classes in your code and you are experiencing one of the following issues, here is an important technote for you.
For high speed access to internal information about views and view columns, DQL processing uses design data extracted from view notes. Currently this information will be stored in a new database, named GQFdsgn.cat.
It is created using new updall flags. It does not replicate and is solely used as a fast-path tool to access design data at runtime.
Here is some LotusScript code to add / update the design of a named Notes application to the catalog. The NSFDesignHarvest call is currently undocumented. Use it at your own risk.
'DECLARATIONS
Public Const UPDATE_DESIGN_CATALOG = 0
Public Const ADD_TO_DESIGN_CATALOG = 1
Const NNOTES ="nnotes.dll"
Const LIBNOTES ="libnotes.so"
Declare Public Function WIN_NSFDbOpen Lib NNOTES Alias "NSFDbOpen" _
(ByVal dbName As String, hDb As Long) As Integer
Declare Public Function LIN_NSFDbOpen Lib LIBNOTES Alias "NSFDbOpen" _
(ByVal dbName As String, hDb As Long) As Integer
Declare Public Function WIN_NSFDbClose Lib NNOTES Alias "NSFDbClose" _
(ByVal hDb As Long) As Integer
Declare Public Function LIN_NSFDbClose Lib LIBNOTES Alias "NSFDbClose" _
(ByVal hDb As Long) As Integer
Declare Public Function WIN_NSFDesignHarvest Lib NNOTES Alias "NSFDesignHarvest" _
(ByVal hDb As Long, ByVal flag As Long) As Integer
Declare Public Function LIN_NSFDesignHarvest Lib LIBNOTES Alias "NSFDesignHarvest" _
(ByVal hDb As Long, ByVal flag As Long) As Integer
' API FUNCTIONS
Private Function NSFDbOpen( db As String, hDB As Long) As Integer
If isDefined("WINDOWS") Then
NSFDbOpen = WIN_NSFDbOpen(db,hDb)
ElseIf isDefined("LINUX") Then
NSFDbOpen = LIN_NSFDbOpen(db,hDb)
End If
End Function
Private Function NSFDbClose (hDb As Long)
If isDefined("WINDOWS") Then
NSFDbClose = WIN_NSFDbClose(hDb)
ElseIf isDefined("LINUX") Then
NSFDbClose = LIN_NSFDbClose(hDb)
End If
End Function
Private Function NSFDesignHarvest (hDb As Long, flag As long) As Integer
If isDefined("WINDOWS") Then
NSFDesignHarvest = WIN_NSFDesignHarvest(hDb, flag)
ElseIf isDefined("LINUX") Then
NSFDesignHarvest = LIN_NSFDesignHarvest(hDb, flag)
End If
End Function
Public Function catalogDesign(sDb As String, flag As Long) As Integer
Dim hDb As Long
Dim rc As Integer
If flag > 1 Then flag = 1
If flag < 0 Then flag = 0
rc = NSFDbOpen(sDb, hDb)
If rc = 0 Then
rc = NSFDesignHarvest(hDb, flag)
rc = NSFDbClose(hDb)
End If
catalogDesign = rc
End Function
To get some basic information about the installed Linux version you can use uname -a.
This will give you something like
Linux serv03.fritz.box 3.10.0-957.1.3.el7.x86_64 #1 SMP
Thu Nov 29 14:49:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
Well, we get the kernel version and the architecture.
rpm -qia ‘release‘ gives much more information
Name : nodesource-release
Version : el7
Release : 1
Architecture: noarch
Install Date: Thu 29 Nov 2018 09:01:53 AM CET
Group : System Environment/Base
Size : 3191
License : MIT
Signature : RSA/SHA256, Thu 04 Jan 2018 09:42:19 PM CET, Key ID 5ddbe8d434fa74dd
Source RPM : nodesource-release-el7-1.src.rpm
Build Date : Thu 04 Jan 2018 09:40:35 PM CET
Build Host : luthien
Relocations : (not relocatable)
URL : http://rpm.nodesource.com/pub_8.x/
Summary : N|Solid repository configuration
Description :
This package contains the NodeSource NodeJS repository
GPG key as well as configuration for yum.
Name : redhat-release-server
Version : 7.4
Release : 18.el7
Architecture: x86_64
Install Date: Tue 27 Nov 2018 01:50:07 PM CET
Group : System Environment/Base
Size : 43146
License : GPLv2
Signature : RSA/SHA256, Wed 28 Jun 2017 08:59:19 PM CEST, Key ID 199e2f91fd431d51
Source RPM : redhat-release-server-7.4-18.el7.src.rpm
Build Date : Wed 28 Jun 2017 08:36:32 PM CEST
Build Host : x86-037.build.eng.bos.redhat.com
Relocations : (not relocatable)
Packager : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
Vendor : Red Hat, Inc.
Summary : Red Hat Enterprise Linux Server release file
Description :
Red Hat Enterprise Linux Server release files
Name : epel-release
Version : 7
Release : 11
Architecture: noarch
Install Date: Wed 28 Nov 2018 07:54:48 AM CET
Group : System Environment/Base
Size : 24834
License : GPLv2
Signature : RSA/SHA256, Mon 02 Oct 2017 07:52:02 PM CEST, Key ID 6a2faea2352c64e5
Source RPM : epel-release-7-11.src.rpm
Build Date : Mon 02 Oct 2017 07:45:58 PM CEST
Build Host : buildvm-ppc64le-05.ppc.fedoraproject.org
Relocations : (not relocatable)
Packager : Fedora Project
Vendor : Fedora Project
URL : http://download.fedoraproject.org/pub/epel
Summary : Extra Packages for Enterprise Linux repository configuration
Description :
This package contains the Extra Packages for Enterprise Linux (EPEL) repository
GPG key as well as configuration for yum.
A couple of days ago, Thomas Adrian posted a new idea on aha.io Allow Enable/Disable/Sign agents from Domino Administrator without opening DDE.
This is a pretty cool idea, I think. Although there already is a great tool available from Ytria ( agentEZ ), handling of agents should be a part of the core features of Notes / Domino.
Notes / Domino contains some of the requested features and for example, you can enable an agent via adminp. You can simply create a new admin request in the admin4.nsf; if you know, which values to set. Unfortunately, this is not documented and you have to do a lot of try and error before the admin request is processed. The advantage of using adminp would be documentation of who did what.
As a downside, you cannot disable an agent; there is no adminp request for that.
Back in 2007, I already demonstrated, how you can add your own adminp requests to the adminp Delete Group Members Using The Administration Process
After reading Thomas’ idea, I decided to spend some hours on building a Domino server addin that would enable / disable or toggle the status of scheduled agents in an application. The application path, agent name and what to do should be passed as parameters to the addin.
Project “AMgr2” was born.
Over the years, I have created my own Notes cAPI CPP framework. This is still work in progress, as I add new methods and properties when I need them.
The framework is a great help when it comes to RAD in Notes / Domino using c/c++. I tried to name methods and properties as close as possible to what we have in LotusScript or Java to make the resulting source code readable and maintainable. Here is an example how I determine if an agent is of type “scheduled” within my framework
bool cNotesAgent::isScheduled() {
cNotesDocument doc(db_h, agnt_id, OPEN_NOVERIFYDEFAULT);
cNotesItemText trigger = doc.getItemText("$AssistTrigger");
if(!trigger.compare("1")) {return TRUE;} else {return FALSE;}
}
Most of the magic happens in the framework, so the source code for AMgr2 is pretty short.
// main.h
#ifndef _MAIN_H_
#define _MAIN_H_
#include <string>
#if defined (_MSC_VER) && !defined(ND64)
#pragma pack(push, 1)
#endif
#include <global.h>
#include <miscerr.h>
#include <addin.h>
#if defined (_MSC_VER) && !defined (ND64)
#pragma pack(pop)
#endif
#include "cNotesFramework.h"
#include "cNotesAgent.h"
#include "cmdline.h"
#if defined (W64)
#define HANDLE DHANDLE
#undef NOTEHANDLE
#define NOTEHANDLE DHANDLE
#else
#define DHANDLE HANDLE
#undef NOTEHANDLE
#define NOTEHANDLE HANDLE
#endif
#define ADDIN_STATUS_LINE "AMgr2"
#define APP_NAME "AMgr2: "
#undef MSG
#define MSG(fmt) APP_NAME fmt
#define ERROR -1
using namespace std;
#endif
// main.cpp
/*
* Amgr2
*
* eknori at eknori dot de www.eknori.de FEBRUARY 2019
*
* copyright (c) 2019 Ulrich Krause www.eknori.de
*/
#pragma warning(disable:4005)
#include "main.h"
STATUS LNPUBLIC AddInMain (HMODULE hModule, int argc, char *argv[]) {
STATUS error = NOERROR;
HANDLE hStatusLine;
HANDLE hStatusLineDesc;
HMODULE hMod;
AddInQueryDefaults (&hMod, &hStatusLine);
AddInDeleteStatusLine (hStatusLine);
hStatusLineDesc = AddInCreateStatusLine(ADDIN_STATUS_LINE);
AddInSetDefaults (hMod, hStatusLineDesc);
AddInSetStatusText("Initialising");
CmdLine *cmdline = new CmdLine();
cmdline->addUsage(" amgr2, V1.0.0.0, (c) 2019, Ulrich Krause\n");
cmdline->addUsage("Usage: lo amgr2 [options] [flags]\n");
cmdline->addUsage( "Options:\n" );
cmdline->addUsage( "-d --db\t\tdatabase path" );
cmdline->addUsage( "-a --agent\t\tagent name" );
cmdline->addUsage( "" );
cmdline->addUsage( "Flags:\n" );
cmdline->addUsage( "-h --help\t\tPrints this help" );
cmdline->addUsage( " --enable\tEnable agent" );
cmdline->addUsage( " --disable\tDisable agent" );
cmdline->addUsage( " --toggle\tToggle agent status" );
cmdline->setOption( "db", 'd' );
cmdline->setOption( "agent", 'a' );
cmdline->setFlag ( "help", 'h' );
cmdline->setFlag ( "enable");
cmdline->setFlag ( "disable");
cmdline->setFlag ( "toggle");
cmdline->processCommandArgs( argc, argv );
if( ! cmdline->hasOptions()) {
cmdline->printUsage2();
delete cmdline;
return ERROR;
}
if( cmdline->getFlag( "help" )
|| cmdline->getFlag( 'h' ) ) {
cmdline->printUsage2();
return NOERROR;
}
string file_path;
if( cmdline->getValue( 'd' ) != NULL
|| cmdline->getValue( "db" ) != NULL ){
file_path = cmdline->getValue( 'd' );
}
string agent_name;
if( cmdline->getValue( 'a' ) != NULL
|| cmdline->getValue( "agent" ) != NULL ){
agent_name = cmdline->getValue( 'a' );
}
cNotesDatabase _NotesDatabase;
try{
_NotesDatabase.open(
file_path.c_str());
cNotesAgent _NotesAgent(
_NotesDatabase.h, agent_name.c_str());
if(_NotesAgent.isScheduled()) {
if( cmdline->getFlag( "enable" )) {
_NotesAgent.enable();
AddInLogMessageText(
MSG("... agent '%s' in database '%s' has been enabled\n"),
NOERROR,
_NotesAgent.name.c_str(),
_NotesDatabase.filePath().c_str());
}
if( cmdline->getFlag( "disable" )) {
_NotesAgent.disable();
AddInLogMessageText(
MSG("... agent '%s' in database '%s' has been disabled\n"),
NOERROR,
_NotesAgent.name.c_str(),
_NotesDatabase.filePath().c_str());
}
if( cmdline->getFlag( "toggle" )) {
if(_NotesAgent.isEnabled()) {
_NotesAgent.disable();
AddInLogMessageText(
MSG("... agent '%s' in database '%s' has been disabled\n"),
NOERROR,
_NotesAgent.name.c_str(),
_NotesDatabase.filePath().c_str());
}
else {
_NotesAgent.enable();
AddInLogMessageText(
MSG("... agent '%s' in database '%s' has been enabled\n"),
NOERROR,
_NotesAgent.name.c_str(),
_NotesDatabase.filePath().c_str());
}
}
} else { // is_scheduled
AddInLogMessageText(
MSG("... agent '%s' in database '%s' is not a scheduled agent\n"),
NOERROR,
_NotesAgent.name.c_str(),
_NotesDatabase.filePath().c_str());
}
} catch (cNotesErr& err) {
delete cmdline;
_NotesDatabase.close();
AddInLogMessageText(
MSG("Notes Error: %s\n"),
NOERROR, err.what());
return ERROR;
} catch (...) {
delete cmdline;
_NotesDatabase.close();
AddInLogMessageText("Unexpected Error\n", NOERROR);
return ERROR;
}
delete cmdline;
_NotesDatabase.close();
return error;
}
To build the cmdline parser, I use another framework that I wrote a couple of years ago. I used the Boost.Program_options in a couple of other projects before, but it is a lot of overhead for such a small project like AMgr2.
CmdLine is much smaller. Despite of its simplicity, it is reliable and produces nice help screens.
I have not yet published CmdLine on Github. If you are interested in the source code, send me an email and I will send you the sources.
Putting it all together, we get a 64bit binary amgr2.exe Copy it to your Domino program directory and you are ready to go.
Open the Domino server console and type
lo amgr2 -h and you should get
amgr2, V1.0.0.0, (c) 2019, Ulrich Krause
Usage: lo amgr2 [options] [flags]
Options:
-d --db database path
-a --agent agent name
Flags:
-h --help Prints this help
--enable Enable agent
--disable Disable agent
--toggle Toggle agent status
To enable a scheduled agent in an application type
lo amgr2 -d names.nsf -a test --enable
[219C:0002-1CE8] 17.02.2019 08:39:56 AMgr2: ... agent 'test' in database 'names.nsf' has been enabled
Use –disable to disable an agent or –toggle to change the status of an agent accordingly.
AMgr2 only works for scheduled agents. If an agent does not match this criteria, you’ll get the following message on the server console
lo amgr2 -d names.nsf -a test2 --toggle
[1F94:0002-15D8] 17.02.2019 08:47:53 AMgr2: ... agent 'test2' in database 'names.nsf' is not a scheduled agent
You also can build a simple Notes application that scans all applications on a server for scheduled agents.
Next you can use NotesSession.SendConsoleCommand to enable / disable / toggle one or more agents.
Here is some sample code
Sub Click(Source As Button)
Dim session As New NotesSession
serverName$ = "serv01/singultus"
consoleCommand$ = Inputbox$("Type command:", _
"Send console command")
consoleReturn$ = session.SendConsoleCommand( _
serverName$, consoleCommand$)
Messagebox consoleReturn$,, consoleCommand$
End Sub
This might not be what Thomas asked for, but it is a good starting point. There is a lot room for improvements and enhancements. AMgr2 will sign the agent with the server id. This might not always be intended. It is not rocket science to implement code and add a couple of parameters to the cmdline parse to use a different id for signing. It is more work to make sure, this id is stored in a secure place and cnnot be accessed by any unauthorized person.
For now, this is it.
AMgr2 once again proves that you can do everything with Notes / Domino. The creators gave us tools that let us add functionallity that is not in the core code. OK, I admit that c/c++ is not the preferred programming language for most of the Notes / Domino developers.
Submitted another plugin to the WordPress plugin directory.
The plugin adds a new brush to the SyntaxHighlighter Evolved plugin to colorize @formula code.
Example:
_exclude:=
"$FILE":"$Fonts":"Form":"$UpdatedBy":"$Revisions":
"ID":"ModifiedBy":"AddressInvoiceAppartment";
_fld:=@Trim(@ReplaceSubstring(@DocFields; _exclude; @Nothing));
"{\"@unid\":\""
+@Text(@DocumentUniqueID)+"\","
+ @Implode ( @Transform (
_fld; "_fn" ; "\"" + _fn + "\":\"" +
@Text ( @GetField ( _fn) ) + "\"" ) ; "," ) +
"},"
UPDATE 05-FEB-2019: Issue is being tracked under SPR # VRARB94KAQ
When executing db.createDominoQuery(); in a Java agent on the server, I see the following error message on the Domino console:
te amgr run "ec11.nsf" 'dql.java'
[021963:000035-00007F2BE8DFD700] 02/03/2019 05:41:29 AM AMgr: Start executing agent 'dql.java' in 'ec11.nsf'
[021963:000037-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent printing: Version: Release 10.0.1 November 29, 2018
[021963:000037-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent printing: Db Title: singultus's Directory
[021963:000038-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent error: Exception in thread "AgentThread: JavaAgent"
[021963:000039-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent error: java.lang.UnsatisfiedLinkError: lotus/domino/local/Database.NcreateDQuery()J
[021963:000041-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent error: at lotus.domino.local.Database.createDominoQuery(Unknown Source)
[021963:000043-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent error: at JavaAgent.NotesMain(JavaAgent.java:19)
[021963:000045-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent error: at lotus.domino.AgentBase.runNotes(Unknown Source)
[021963:000047-00007F2BE816F700] 02/03/2019 05:41:29 AM Agent Manager: Agent error: at lotus.domino.NotesThread.run(Unknown Source)
[021963:000035-00007F2BE8DFD700] 02/03/2019 05:41:29 AM AMgr: Agent 'dql.java' in 'ec11.nsf' completed execution
According to John Curtis (HCL) “… We haven’t seen this to date.”.
I have tested on different OS.
serv02:
Red Hat Enterprise Linux Server release 7.4 (Maipo)
Linux serv02.fritz.box 3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux
IBM Domino (r) Server (64 Bit) (Release 10.0.1 for Linux/64) 02/03/2019 05:48:40 AM
serv02 has been upgraded from V10.0
serv03:
CentOS Linux release 7.6.1810 (Core)
Linux serv03.fritz.box 3.10.0-957.1.3.el7.x86_64 #1 SMP Thu Nov 29 14:49:43 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
IBM Domino (r) Server (64 Bit) (Release 10.0.1 for Linux/64) 02/03/2019 05:49:15 AM (Community server)
serv03 is "fresh" install and not upgraded from earlier Domino versions.
Here is the code I used
import lotus.domino.AgentBase;
import lotus.domino.Database;
import lotus.domino.DominoQuery;
import lotus.domino.Session;
public class JavaAgent extends AgentBase {
public void NotesMain() {
try {
Session session = getSession();
System.out.println("Version: " + session.getNotesVersion());
Database db = null;
db = session.getDatabase(null, "names.nsf");
System.out.println("Db Title: " + db.getTitle());
DominoQuery dql = null;
dql = db.createDominoQuery();
dql.recycle();
db.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
}
As it works on Domino 10.0.1 on WINDOWS, I investigated nlsxbe.dll and liblsxbe.so
nlsxbe.dll contains the function in question
Java_lotus_domino_local_Database_NcreateDQuery 0x0000000180017cd0 0x00017cd0 732 (0x2dc) nlsxbe.dll W:\Domino\nlsxbe.dll Exported Function
liblsxbe.so is missing the function. At least, it is not exported.
nm -D liblsxbe.so | grep createD
0000000000096a03 T Java_lotus_domino_local_Database_NcreateDocColl
0000000000095587 T Java_lotus_domino_local_Database_NcreateDocument
000000000012bf7d T Java_lotus_domino_local_DateRange_NrecreateDateRange
00000000000e33e1 T Java_lotus_domino_local_DateTime_NrecreateDateTime
0000000000110ad2 T Java_lotus_domino_local_DbDirectory_NcreateDatabase
00000000000d5d6d T Java_lotus_domino_local_Session_NcreateDateRange
00000000000da091 T Java_lotus_domino_local_Session_NcreateDateTime
00000000000d6bd1 T Java_lotus_domino_local_Session_NcreateDxlExporter
00000000000d6d71 T Java_lotus_domino_local_Session_NcreateDxlImporter
00000000000957a0 T Java_lotus_notes_Database_NcreateDocument
0000000000110d7e T Java_lotus_notes_DbDirectory_NcreateDatabase
00000000000d5f40 T Java_lotus_notes_Session_NcreateDateRange
00000000000da360 T Java_lotus_notes_Session_NcreateDateTime
U _ZN11XmlDocument14createDocumentEv
U _ZN11XmlDocument22createDocumentFragmentEv
The version and date seem to be OK.
ls -al liblsxbe.so
-rwxr-xr-x. 1 root root 14510400 Nov 29 07:48 liblsxbe.so
Perhaps someone else can confirm this behaviour. I have already created case #TS001863705 with HCL.
SyntaxHighlighter Evolved: LotusScript Brush is a small WordPress plugin that adds support for the LotusScript language to the great SyntaxHighlighter Evolved plugin by Alex Mills.
I have submitted a request to add the plugin to the official WordPress plugin directory. The review process will take some time.
In the meantime, you can start using SyntaxHighlighter Evolved: LotusScript Brush right now.
Download the plugin from here , unpack it and upload to your /wp-content/plugins directory.
Update 04-FEB-2019: The plugin is now available in the WordPress plugin directory.
Next activate the plugin
SyntaxHighlighter Evolved: LotusScript Brush will now be available as a new language in the Block Editor (“Code Languages”)
SyntaxHighlighter Evolved: LotusScript Brush highlights classes, method and properties, keywords, strings, comments and directives.
Think about a big database with lots of documents in it and you want to find only one particular document. You can do that with FTSearch, or you can use a db.search with some formula.
As of V10.0.x, you also have DQL and the new NotesDominoQuery class.
The class is available in Lotsscript and Java.
I want to demonstrate in this sample, how you can find the needle in the haystack with DQL in Lotusscript.
My database has about 12.500.000 Documents. It is one of our customers database at midpoints. The amount of documents was created by accident. Some call it a bug. Anyway, the database is a good playground.
The code is typical for a LS developer. It initiates objects and stuff, assigns variables like our dqlTerm (line 6 see the similarity to the @formula, you would probably use with db.search? ), does a check, if the target database is open (line 9 ) and also has some basic error handling (17, 20-21). We will come to that later.
I am running the sample on the client. As of today , you cannot run a query client / server. I will show in another post, how you can run the query on the server and work with the results on the client. But that is another story.
public Sub foo()
Dim session As New NotesSession
Dim db As NotesDatabase
Dim col As NotesDocumentCollection
Dim dqlTerm As String
dqlTerm = "form = 'frm.rules.device.rule' And rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"
Set db = session.Getdatabase("","trul-big.nsf", False)
If db.Isopen Then
Dim dql As NOTESDOMINOQUERY
Set dql = db.CreateDominoQuery()
Dim parse_result As String
parse_result = dql.parse(dqlTerm)
If LCase(parse_result) = "success" Then
Set col = dql.Execute(dqlTerm)
MsgBox dql.Explain(dqlTerm)
Else
MsgBox parse_result
End If
End If
End Sub
Before you run the code and try to search such a huge amount of data, you need to set some notes.ini variables.
QUERY_MAX_DOCS_SCANNED=13000000
QUERY_MAX_VIEW_ENTRIES_SCANNED=13000000
By now, there is no other way to increase the number of documents. There are setters in the NotesDominoQuery class, but those setters are broken. This is a known issue and HCL is working on a solution.
When we now run the code, we will get the following result
It took 51.3 secs to find 110642 documents that use the form “frm.rules.device.rule” and zero to none secs to find 1 document in that resultset.
Would it be faster or slower, if we modify our query to use only the rule_unid field and search over the entire set of documents?
dqlTerm = "rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"
Here is the result
32.2 secs to find the needle in the haystack. Use this kind of single field search, if you are sure that the field “rule_unid” is only used on one form.
But even in the case that the field is used on another form; to filter the resulting NotesDocument collection afterwards is much faster than the AND dqlTerm from the original code.
Maybe, you already have a view in your application where the rule_unid is in a sorted column.
Then we can use a modified dqlTerm to find the document in that view.
Again, let’s change our dqlTerm a little bit
dqlTerm = "'all'.rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"
And here is the result
THAT is pretty cool, isn’t it? 5.6 msecs to find the needle in the haystack.
Can it be even faster? I think no, but let us take another look at the code that does the query.
Dim parse_result As String
parse_result = dql.parse(dqlTerm)
If LCase(parse_result) = "success" Then
Set col = dql.Execute(dqlTerm)
MsgBox dql.Explain(dqlTerm)
Else
MsgBox parse_result
End If
We can safely remove all of our “Error handling”, that means lines 14-17 and 20-22. Why? Because dql.Excecute(dqlTerm) does the parse before executing the query.
So we end up with the following code
public Sub foo()
Dim session As New NotesSession
Dim db As NotesDatabase
Dim col As NotesDocumentCollection
Dim dqlTerm As String
dqlTerm = "'all'.rule_unid = '99A242AAB69B5BB9C1257FFC005DE6C4'"
Set db = session.Getdatabase("","trul-big.nsf", False)
If db.Isopen Then
Dim dql As NOTESDOMINOQUERY
Set dql = db.CreateDominoQuery()
Set col = dql.Execute(dqlTerm)
MsgBox dql.Explain(dqlTerm)
End If
End Sub
When we run the code, we get
Keep in mind that the numbers vary a litte bit from run to run.
If we now modify our dqlTerm and add an error, we are shown a nice dialog box explaining the error in detail.
That’s all for today, I hope you find this information useful.
I received the following mail from IBM announcing the End of Marketing for all Watson Workspace offerings and ending the Watson Workspace service on 2/28/19.
Watson Workspace Announcement
Tomorrow, Tuesday 15 Jan 2019, IBM will formally announce the End of Marketing for all Watson Workspace offerings and we anticipate ending the Watson Workspace service on 2/28/19. This includes Watson Workspace Essentials, Plus, and the free offering. It is our intent to ensure that you have a clear understanding of your options regarding the closure of Watson Workspace.
While there is no question that Watson Workspace is innovative and agile, it hasn’t resonated with clients or obtained the traction in the marketplace necessary for IBM to continue forward with the service. Despite our best efforts and enthusiasm for these offerings, our decision to withdraw them aligns to IBM’s investment strategy, focused on delivering solutions that deliver measurable value to our customers and business partners.
IBM has stopped accepting new orders for Watson Workspace products, and we will not be adding any new features to the offerings. We have been working with our licensed customers and business partners to provide options for handling subscriptions and contracts once the end of service announcement is published to provide a smooth transition.
Starting tomorrow there will be a banner placed into the UI of Watson Workspace informing users of the timeline for moving off the service. Mobile customers may find details in the release notes of the mobile app. We will also provide access to a tool that will allow you to download and save your conversations and content from Watson Workspace. Please plan accordingly to capture any content you’d like to retain as we work to sunset the service. This option will only be available for a limited time.
Thank you for your support of Watson Workspace and IBM Collaboration Solutions. Please find additional details and answers to commonly asked questions in the FAQ that will be posted in the Watson Workspace banner tomorrow.
IBM Collaboration Services, Offering Management
NotesHttpRequest is a new LotusScript class in Notes V10.0.1 used to make HTTP requests to web servers.
The example wraps the get(), post(), put() and deleteResource() methods in a LotusScript class.
I have also put together a small Node.js / Express.js project that can be used as test server for the NotesHttpRequest example.
%REM
Library 10010.http
Created Jan 8, 2019 by Ulrich Krause/singultus
%END REM
Option Public
Option Declare
Const BASE_URL_PORT = "http://192.168.178.35:3001/api/"
Const CLASS_CUSTOMER ="customers"
Const CUSTOMER_5 = |{
"firstname" : "Ulrich",
"lastname" : "Krause",
"age" : 59,
"id" : 5
}|
Const CUSTOMER_5_UPDATE = |{
"firstname" : "Heinz Ulrich",
"lastname" : "Krause",
"age" : 59,
"id" : 5
}|
%REM
Class HttpRequestWrapper
%END REM
Public Class HttpRequestWrapper
Private m_session As NotesSession
Private m_url As String
Private m_class As String
Private m_json As String
Private m_httpRequest As NOTESHTTPREQUEST
%REM
Sub New
%END REM
Public Sub New()
Set m_session = New NotesSession
Set m_httpRequest = me.m_session.CreateHttpRequest()
m_httpRequest.Preferstrings = True
m_url = BASE_URL_PORT
m_class = CLASS_CUSTOMER
End Sub
%REM
Function getApiVersion
%END REM
Public sub getApiVersion()
m_json = m_httpRequest.Get(BASE_URL_PORT)
MsgBox m_json
End Sub
%REM
Sub getAllObj
%END REM
Public Sub getAllObj()
m_json = m_httpRequest.Get(_
BASE_URL_PORT + CLASS_CUSTOMER)
MsgBox m_json
End Sub
%REM
Sub getObjById
%END REM
Public Sub getObjById(id As String)
m_json = m_httpRequest.Get(_
BASE_URL_PORT + CLASS_CUSTOMER + "/" + id)
MsgBox m_json
End Sub
%REM
Sub createObj
%END REM
Public Sub createObj()
m_json = m_httpRequest.Post(_
BASE_URL_PORT + CLASS_CUSTOMER,_
removeCRLF(CUSTOMER_5))
MsgBox m_json
End Sub
%REM
Sub updateObj
%END REM
Public Sub updateObj(id As String)
m_json = m_httpRequest.Put(_
BASE_URL_PORT + CLASS_CUSTOMER + "/" + id,_
removeCRLF(CUSTOMER_5_UPDATE))
MsgBox m_json
End Sub
%REM
Sub deleteObj
%END REM
Public Sub deleteObj(id As String)
m_json = m_httpRequest.DeleteResource(_
BASE_URL_PORT + CLASS_CUSTOMER + "/" + id)
MsgBox m_json
End Sub
End Class
Public Function removeCRLF(json As String) As String
removeCRLF = Replace(Replace(json, Chr(13), ""),Chr(10),"")
End Function
Use "10010.http"
Sub Click(Source As Button)
Dim httpRequestWrapper As New HttpRequestWrapper()
Call httpRequestWrapper.getApiVersion()
Call httpRequestWrapper.createObj()
Call httpRequestWrapper.deleteObj("3")
Call httpRequestWrapper.getAllObj()
Call httpRequestWrapper.updateObj("5")
Call httpRequestWrapper.getObjById("5")
End Sub
I recently ran into an issue with PM2. I have created a “helloworld” applications using Express.js.
[root@nodejs projects]# express helloworld
warning: the default view engine will not be jade in future releases
warning: use--view=jade' or
--help' for additional options
create : helloworld/
create : helloworld/public/
create : helloworld/public/javascripts/
create : helloworld/public/images/
create : helloworld/public/stylesheets/
create : helloworld/public/stylesheets/style.css
create : helloworld/routes/
create : helloworld/routes/index.js
create : helloworld/routes/users.js
create : helloworld/views/
create : helloworld/views/error.jade
create : helloworld/views/index.jade
create : helloworld/views/layout.jade
create : helloworld/app.js
create : helloworld/package.json
create : helloworld/bin/
create : helloworld/bin/www
change directory:
$ cd helloworld
install dependencies:
$ npm install
run the app:
$ DEBUG=helloworld:* npm start
The application started without issues with npm start.
Next I started the application with PM2
[root@nodejs helloworld]# pm2 start app.js
PM2 displayed the application as “online”, but it constantly restarted and was not accessible.
I found the solution here
[root@nodejs helloworld]# pm2 start bin/www
did the trick for me.
I have setup a new Node.js / Express development environment on a CentOS 7 VM. I ‘ll describe the details in another post later.
To test my setp, I created a new Express application “helloworld”. The application listens on port 3000 and I was able to connect to the application using a browser.
Next, I configured NGINX as reverse proxy to use port 80 to access the helloworld application.
But I got an error
I checked the logs
[root@nodejs ~]# cat /var/log/audit/audit.log | grep nginx | grep denied
and got
type=AVC msg=audit(1546783734.750:239): avc: denied { name_connect } for pid=11084 comm="nginx" dest=3000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:ntop_port_t:s0 tclass=tcp_socket permissive=0
My best guess was SELinux.I checked, if SELinux was enabled.
[root@nodejs ~]# sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 31
Next I checked the settings for httpd.
[root@nodejs ~]# getsebool -a | grep httpd
httpd_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_check_spam --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_connect_mythtv --> off
httpd_can_connect_zabbix --> off
httpd_can_network_connect --> off
httpd_can_network_connect_cobbler --> off
httpd_can_network_connect_db --> off
httpd_can_network_memcache --> off
httpd_can_network_relay --> off
httpd_can_sendmail --> off
So, httpd_can_network_connect was set to “Off”. This blocks the connection from the reverse proxy to the node.js application. As a result, you get the 502 Bad gateway error.
To enable the setting, execute the following command from the shell.
[root@nodejs ~]# setsebool -P httpd_can_network_connect on
You do not need to reboot the machine or SELinux.
I just tried to update my ESXi 6.7 host from ESXi-6.7.0-20181002001-standard (Build 10302608) to ESXi-6.7.0-20181104001-standard (Build 10764712).
Find a list for all available updates and patches here.
The update process in general is straight forward. SSH into your ESXi host and execute the following commands.
esxcli network firewall ruleset set -e true -r httpClient
esxcli software profile update -p ESXi-6.7.0-20181104001-standard -d https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml
esxcli network firewall ruleset set -e false -r httpClient
(replace the highlighted version with the version you want to upgrade to )
This time, the update failed.
[InstallationError]
Failed to setup upgrade using esx-update VIB: (None, "Failed to mount tardisk /tmp/esx-update-2123405/esxupdt-2123405 in ramdisk esx-update-2123405: [Errno 1] Operation not permitted: '/tardisks.noauto/esxupdt-2123405'")
vibs = ['VMware_bootbank_esx-update_6.7.0-1.31.10764712']
Please refer to the log file for more details.
[root@esxi:~]
Digging into the logs, I found the following clues
vmkernel.log:
cpu1:2099635)ALERT: VisorFSTar: 1655: Unauthorized attempt to mount a tardisk
cpu1:2099635)VisorFSTar: 2062: Access denied by vmkernel access control policy prevented creating tardisk
esxupdate.log:
esxupdate: 2099635: root: ERROR: File "/build/mts/release/bora-10302608/bora/build/esx/release/vmvisor/sys-boot/lib64/python3.5/shutil.py", line 544, in move
esxupdate: 2099635: root: ERROR: PermissionError: [Errno 1] Operation not permitted: '/tmp/esx-update-2123405 /esxupdt-2123405' -> '/tardisks.noauto/esxupdt-2123405 '
By the way, the esxupdate.log is in HEX format for some reason.
You can either use a HEX-Editor to decode the file, or open it in Visual Studio Code.
You’ll get a warning; just click on “Do you want to open it anyway?“
After a couple of try and error, I was able to get the updated VIBs using
[root@esxi:~] esxcli software vib update --depot=https://hostupdate.vmware.com/software/VUM/PRODUCTION/main/vmw-depot-index.xml
Installation Result
Message: The update completed successfully, but the system needs to be rebooted for the changes to be effective.
Reboot Required: true
VIBs Installed: VMware_bootbank_esx-base_6.7.0-1.31.10764712, VMware_bootbank_esx-ui_1.31.0-10201673, VMware_bootbank_esx-update_6.7.0-1.31.10764712, VMware_bootbank_sata-ahci_3.0-28vmw.600.3.107.10474991, VMware_bootbank_vsan_6.7.0-1.31.10720746, VMware_bootbank_vsanhealth_6.7.0-1.31.10720754
VIBs Removed: VMW_bootbank_sata-ahci_3.0-26vmw.670.0.0.8169922, VMware_bootbank_esx-base_6.7.0-1.28.10302608, VMware_bootbank_esx-ui_1.30.0-9946814, VMware_bootbank_esx-update_6.7.0-1.28.10302608, VMware_bootbank_vsan_6.7.0-1.28.10290435, VMware_bootbank_vsanhealth_6.7.0-1.28.10290721
VIBs Skipped: VMW_bootbank_ata-libata-92_3.00.9.2-16vmw.670.0.0.8169922, VMW_bootbank_ata-pata-amd_0.3.10-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-atiixp_0.4.6-4vmw.670.0.0.8169922, VMW_bootbank_ata-pata-cmd64x_0.2.5-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-hpt3x2n_0.3.4-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-pdc2027x_1.0-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-serverworks_0.4.3-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-sil680_0.4.8-3vmw.670.0.0.8169922, VMW_bootbank_ata-pata-via_0.3.3-2vmw.670.0.0.8169922, VMW_bootbank_block-cciss_3.6.14-10vmw.670.0.0.8169922, VMW_bootbank_bnxtnet_20.6.101.7-11vmw.670.0.0.8169922, VMW_bootbank_bnxtroce_20.6.101.0-20vmw.670.1.28.10302608, VMW_bootbank_brcmfcoe_11.4.1078.5-11vmw.670.1.28.10302608, VMW_bootbank_char-random_1.0-3vmw.670.0.0.8169922, VMW_bootbank_ehci-ehci-hcd_1.0-4vmw.670.0.0.8169922, VMW_bootbank_elxiscsi_11.4.1174.0-2vmw.670.0.0.8169922, VMW_bootbank_elxnet_11.4.1095.0-5vmw.670.1.28.10302608, VMW_bootbank_hid-hid_1.0-3vmw.670.0.0.8169922, VMW_bootbank_i40en_1.3.1-22vmw.670.1.28.10302608, VMW_bootbank_iavmd_1.2.0.1011-2vmw.670.0.0.8169922, VMW_bootbank_igbn_0.1.0.0-15vmw.670.0.0.8169922, VMW_bootbank_ima-qla4xxx_2.02.18-1vmw.670.0.0.8169922, VMW_bootbank_ipmi-ipmi-devintf_39.1-5vmw.670.1.28.10302608, VMW_bootbank_ipmi-ipmi-msghandler_39.1-5vmw.670.1.28.10302608, VMW_bootbank_ipmi-ipmi-si-drv_39.1-5vmw.670.1.28.10302608, VMW_bootbank_iser_1.0.0.0-1vmw.670.1.28.10302608, VMW_bootbank_ixgben_1.4.1-16vmw.670.1.28.10302608, VMW_bootbank_lpfc_11.4.33.3-11vmw.670.1.28.10302608, VMW_bootbank_lpnic_11.4.59.0-1vmw.670.0.0.8169922, VMW_bootbank_lsi-mr3_7.702.13.00-5vmw.670.1.28.10302608, VMW_bootbank_lsi-msgpt2_20.00.04.00-5vmw.670.1.28.10302608, VMW_bootbank_lsi-msgpt35_03.00.01.00-12vmw.670.1.28.10302608, VMW_bootbank_lsi-msgpt3_16.00.01.00-3vmw.670.1.28.10302608, VMW_bootbank_misc-cnic-register_1.78.75.v60.7-1vmw.670.0.0.8169922, VMW_bootbank_misc-drivers_6.7.0-0.0.8169922, VMW_bootbank_mtip32xx-native_3.9.8-1vmw.670.1.28.10302608, VMW_bootbank_ne1000_0.8.4-1vmw.670.1.28.10302608, VMW_bootbank_nenic_1.0.21.0-1vmw.670.1.28.10302608, VMW_bootbank_net-bnx2_2.2.4f.v60.10-2vmw.670.0.0.8169922, VMW_bootbank_net-bnx2x_1.78.80.v60.12-2vmw.670.0.0.8169922, VMW_bootbank_net-cdc-ether_1.0-3vmw.670.0.0.8169922, VMW_bootbank_net-cnic_1.78.76.v60.13-2vmw.670.0.0.8169922, VMW_bootbank_net-e1000_8.0.3.1-5vmw.670.0.0.8169922, VMW_bootbank_net-e1000e_3.2.2.1-2vmw.670.0.0.8169922, VMW_bootbank_net-enic_2.1.2.38-2vmw.670.0.0.8169922, VMW_bootbank_net-fcoe_1.0.29.9.3-7vmw.670.0.0.8169922, VMW_bootbank_net-forcedeth_0.61-2vmw.670.0.0.8169922, VMW_bootbank_net-igb_5.0.5.1.1-5vmw.670.0.0.8169922, VMW_bootbank_net-ixgbe_3.7.13.7.14iov-20vmw.670.0.0.8169922, VMW_bootbank_net-libfcoe-92_1.0.24.9.4-8vmw.670.0.0.8169922, VMW_bootbank_net-mlx4-core_1.9.7.0-1vmw.670.0.0.8169922, VMW_bootbank_net-mlx4-en_1.9.7.0-1vmw.670.0.0.8169922, VMW_bootbank_net-nx-nic_5.0.621-5vmw.670.0.0.8169922, VMW_bootbank_net-tg3_3.131d.v60.4-2vmw.670.0.0.8169922, VMW_bootbank_net-usbnet_1.0-3vmw.670.0.0.8169922, VMW_bootbank_net-vmxnet3_1.1.3.0-3vmw.670.0.0.8169922, VMW_bootbank_nfnic_4.0.0.14-0vmw.670.1.28.10302608, VMW_bootbank_nhpsa_2.0.22-3vmw.670.1.28.10302608, VMW_bootbank_nmlx4-core_3.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx4-en_3.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx4-rdma_3.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx5-core_4.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_nmlx5-rdma_4.17.9.12-1vmw.670.0.0.8169922, VMW_bootbank_ntg3_4.1.3.2-1vmw.670.1.28.10302608, VMW_bootbank_nvme_1.2.2.17-1vmw.670.1.28.10302608, VMW_bootbank_nvmxnet3-ens_2.0.0.21-1vmw.670.0.0.8169922, VMW_bootbank_nvmxnet3_2.0.0.29-1vmw.670.1.28.10302608, VMW_bootbank_ohci-usb-ohci_1.0-3vmw.670.0.0.8169922, VMW_bootbank_pvscsi_0.1-2vmw.670.0.0.8169922, VMW_bootbank_qcnic_1.0.2.0.4-1vmw.670.0.0.8169922, VMW_bootbank_qedentv_2.0.6.4-10vmw.670.1.28.10302608, VMW_bootbank_qfle3_1.0.50.11-9vmw.670.0.0.8169922, VMW_bootbank_qfle3f_1.0.25.0.2-14vmw.670.0.0.8169922, VMW_bootbank_qfle3i_1.0.2.3.9-3vmw.670.0.0.8169922, VMW_bootbank_qflge_1.1.0.11-1vmw.670.0.0.8169922, VMW_bootbank_sata-ata-piix_2.12-10vmw.670.0.0.8169922, VMW_bootbank_sata-sata-nv_3.5-4vmw.670.0.0.8169922, VMW_bootbank_sata-sata-promise_2.12-3vmw.670.0.0.8169922, VMW_bootbank_sata-sata-sil24_1.1-1vmw.670.0.0.8169922, VMW_bootbank_sata-sata-sil_2.3-4vmw.670.0.0.8169922, VMW_bootbank_sata-sata-svw_2.3-3vmw.670.0.0.8169922, VMW_bootbank_scsi-aacraid_1.1.5.1-9vmw.670.0.0.8169922, VMW_bootbank_scsi-adp94xx_1.0.8.12-6vmw.670.0.0.8169922, VMW_bootbank_scsi-aic79xx_3.1-6vmw.670.0.0.8169922, VMW_bootbank_scsi-bnx2fc_1.78.78.v60.8-1vmw.670.0.0.8169922, VMW_bootbank_scsi-bnx2i_2.78.76.v60.8-1vmw.670.0.0.8169922, VMW_bootbank_scsi-fnic_1.5.0.45-3vmw.670.0.0.8169922, VMW_bootbank_scsi-hpsa_6.0.0.84-3vmw.670.0.0.8169922, VMW_bootbank_scsi-ips_7.12.05-4vmw.670.0.0.8169922, VMW_bootbank_scsi-iscsi-linux-92_1.0.0.2-3vmw.670.0.0.8169922, VMW_bootbank_scsi-libfc-92_1.0.40.9.3-5vmw.670.0.0.8169922, VMW_bootbank_scsi-megaraid-mbox_2.20.5.1-6vmw.670.0.0.8169922, VMW_bootbank_scsi-megaraid-sas_6.603.55.00-2vmw.670.0.0.8169922, VMW_bootbank_scsi-megaraid2_2.00.4-9vmw.670.0.0.8169922, VMW_bootbank_scsi-mpt2sas_19.00.00.00-2vmw.670.0.0.8169922, VMW_bootbank_scsi-mptsas_4.23.01.00-10vmw.670.0.0.8169922, VMW_bootbank_scsi-mptspi_4.23.01.00-10vmw.670.0.0.8169922, VMW_bootbank_scsi-qla4xxx_5.01.03.2-7vmw.670.0.0.8169922, VMW_bootbank_shim-iscsi-linux-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-iscsi-linux-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libata-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libata-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfc-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfc-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfcoe-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-libfcoe-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-vmklinux-9-2-1-0_6.7.0-0.0.8169922, VMW_bootbank_shim-vmklinux-9-2-2-0_6.7.0-0.0.8169922, VMW_bootbank_shim-vmklinux-9-2-3-0_6.7.0-0.0.8169922, VMW_bootbank_smartpqi_1.0.1.553-12vmw.670.1.28.10302608, VMW_bootbank_uhci-usb-uhci_1.0-3vmw.670.0.0.8169922, VMW_bootbank_usb-storage-usb-storage_1.0-3vmw.670.0.0.8169922, VMW_bootbank_usbcore-usb_1.0-3vmw.670.0.0.8169922, VMW_bootbank_vmkata_0.1-1vmw.670.0.0.8169922, VMW_bootbank_vmkfcoe_1.0.0.1-1vmw.670.1.28.10302608, VMW_bootbank_vmkplexer-vmkplexer_6.7.0-0.0.8169922, VMW_bootbank_vmkusb_0.1-1vmw.670.1.28.10302608, VMW_bootbank_vmw-ahci_1.2.3-1vmw.670.1.28.10302608, VMW_bootbank_xhci-xhci_1.0-3vmw.670.0.0.8169922, VMware_bootbank_cpu-microcode_6.7.0-1.28.10302608, VMware_bootbank_elx-esx-libelxima.so_11.4.1184.0-0.0.8169922, VMware_bootbank_esx-dvfilter-generic-fastpath_6.7.0-0.0.8169922, VMware_bootbank_esx-xserver_6.7.0-0.0.8169922, VMware_bootbank_lsu-hp-hpsa-plugin_2.0.0-16vmw.670.1.28.10302608, VMware_bootbank_lsu-intel-vmd-plugin_1.0.0-2vmw.670.1.28.10302608, VMware_bootbank_lsu-lsi-lsi-mr3-plugin_1.0.0-13vmw.670.1.28.10302608, VMware_bootbank_lsu-lsi-lsi-msgpt3-plugin_1.0.0-8vmw.670.0.0.8169922, VMware_bootbank_lsu-lsi-megaraid-sas-plugin_1.0.0-9vmw.670.0.0.8169922, VMware_bootbank_lsu-lsi-mpt2sas-plugin_2.0.0-7vmw.670.0.0.8169922, VMware_bootbank_lsu-smartpqi-plugin_1.0.0-3vmw.670.1.28.10302608, VMware_bootbank_native-misc-drivers_6.7.0-0.0.8169922, VMware_bootbank_qlnativefc_3.0.1.0-5vmw.670.0.0.8169922, VMware_bootbank_rste_2.0.2.0088-7vmw.670.0.0.8169922, VMware_bootbank_vmware-esx-esxcli-nvme-plugin_1.2.0.34-1.28.10302608, VMware_locker_tools-light_10.3.2.9925305-10176879
[root@esxi:~]
I recently decided that it is time to setup a new homelab. The old server is about 10 yrs old. The hardware does not allow any upgrade in CPU and Ram. VMWare ESXi was version 6.5, but I could not upgrade to version 6.7 because the network card was not in the list of supported NICs and so the upgrade failed. Last, but not least, the power consumption was at 200W.
The new HomeLab has the following components
It took less than 30 minutes to assemble the NUC and install VMWare ESXi 6.7. ( + 15 minutes to drive to the local hardware store to grab an USB keyboard once I realized that I would need one for the setup )
Today, I migrated the existing VMs from the old host to the new one.
ESXi does not include VMotion. VMotion costs a lot of money.
I had read some articles which claimed to be best practice. But to be honest, using Veeam or SCP are not, what I consider “best” practice. I tried SCP, but it was so slooooow. Even a 50GB VM was estimated 11 hours to copy. And I have 30 VMs from just a couple of MB to 100GB.
I searched for a better solution. And I finally found it. VMware vCenter Converter Standalone Client.
You simply choose the “source” ESXi instance and select the VM to copy. Next you select the “target” ESXi. You can also choose, if the copy will be automatically updated to the target VM version.
It took only 30 minutes to copy a 50GB VM. Another 100GB VM was copied in 20 minutes.
The whole migration was done in only 5 hours. Not bad, isn’t it.
Sample code:
Const white = |{"color": "white", "category": "value","code": {"rgba": [0,0,0,1],"hex": "#FFF"}}|
Public Sub testJsonNavGetElementByPointer()
Dim session As New NotesSession
Dim jsnav As NotesJSONNavigator
Dim el As NOTESJSONELEMENT
Set jsnav = session.CreateJSONNavigator(removeCRLF(white))
'// returns "value"
Set el = jsnav.Getelementbypointer("/category")
MsgBox "category: " + el.Value
'// returns "#FFF"
Set el = jsnav.Getelementbypointer("/code/hex")
MsgBox "hex: " + el.Value
'// returns the 4th element in the rgba array "1"
Set el = jsnav.Getelementbypointer("/code/rgba/3")
MsgBox "code/rgba/3: " + el.Value
End Sub
General information about JSON pointer: https://tools.ietf.org/html/rfc6901
I stumbled upon an issue with NotesJsonNavigator getNthElement(index) method.
It looks like there is no boundry check, which leads to some inconsitent behaviour and unpredictable results.
Here is the code that I used in my test.
%REM
Sub testJsonNavGetNthElement
%END REM
Public Sub testJsonNavGetNthElement
Dim s As New NotesSession
Dim jsnav As NotesJSONNavigator
Dim el As NotesJSONElement
Set jsnav = s.CreateJSONNavigator(|{ "element1" : "value 1", "element2" : "value 2", "element3 ": "value 3" }|)
Set el = jsnav.GetNthElement(0)
Stop
Set el = jsnav.GetNthElement(1)
Stop
Set el = jsnav.GetNthElement(2)
Stop
Set el = jsnav.GetNthElement(3)
Stop
Set el = jsnav.GetNthElement(1000)
Stop
End Sub
The issue occurs with index < 1 and > upper bound of the array.
Index 0 AND index 1 both return the same value; “value 1“.
Index 1000 returns NULL or nothing.
This is not the expected behaviour. At least I would expect some “out of bounds” error.Also, it is not clear for me what the base for the index is. Do we start counting at 0 or do we start with 1 ?
According to the documentation, the index is 1-based.
NotesJsonNavigator, NotesJsonElement, NotesJsonArray, NotesJsonObject are new classes in Domino Designer as of Notes V10.0.1. They are not yet documented in the Domino Designer Help, but you can find online documentation following the above links.
The documentation also contains some basic samples.
In this article, I will demonstrate how to use the classes beyond the basic examples.
I’ll also show, how you can use the new NotesHttpRequest class to connect to a server and read and parse view data as JSON.
The first thing you need to know is that the NotesJsonNavigator class does not like CRLF.
When you try to create a new NotesJsonNavigator from the following JSON data
Const colors = |{
"colors": [
{
"color": "black",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,255,1],
"hex": "#000"
}
},
{
"color": "white",
"category": "value",
"code": {
"rgba": [0,0,0,1],
"hex": "#FFF"
}
},
{
"color": "red",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,0,0,1],
"hex": "#FF0"
}
},
{
"color": "blue",
"category": "hue",
"type": "primary",
"code": {
"rgba": [0,0,255,1],
"hex": "#00F"
}
},
{
"color": "yellow",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,0,1],
"hex": "#FF0"
}
},
{
"color": "green",
"category": "hue",
"type": "secondary",
"code": {
"rgba": [0,255,0,1],
"hex": "#0F0"
}
}
]
}|
you will get an error.
You can remove all CRLF from the data using this helper function.
Public Function removeCRLF(json As String) As String
removeCRLF = Replace(Replace(json, Chr(13), ""),Chr(10),"")
End Function
Let us create the NotesJsonNavigator from NotesSession first.
Dim session As New NotesSession
Dim jsnav As NotesJSONNavigator
Dim json As String
json = removeCRLF(colors)
Set jsnav = session.CreateJSONNavigator(json)
Now we can count how many different colors we would find in our JSON object
Dim el As NOTESJSONELEMENT
Dim arr As NOTESJSONARRAY
Set el = jsnav.GetFirstElement()
Set arr = el.value
MsgBox "Elements count: " + CStr(arr.size)
And finally, we want to get the value for the second color in the JSON object.
Although there is a GetNthElement (index) method, this method seems to be buggy or not yet fully implemented. GetNthElement(index) will always return the first element from the JSON object.
So we will use GetFirstElement and GetNextElement to navigate thru the object.
'Set el = arr.Getnthelement(2)
Set el = arr.GetFirstElement()
Set el = arr.GetNextElement()
Dim obj As NOTESJSONOBJECT
Set obj = el.Value
Set el = obj.Getelementbyname("color")
MsgBox "color: " + CStr(el.Value)
The next sample creates a NotesHttpRequest and gets JSON from a view in a Notes application. Then we retrieve the UNID and NoteId from the data returned using the new NotesJson… classes.
%REM
Library 10010.http
Created Jan 1, 2019 by Ulrich Krause/singultus
Description: Comments for Library
%END REM
Option Public
Option Declare
Public Sub httpGet
Dim Session As New NotesSession
Dim ret As String
Dim URL As String
Dim user As String
Dim password As String
Dim httpReq As NotesHTTPRequest
Set httpReq = session.CreateHttpRequest()
httpReq.Preferstrings = True
user = "firstname.lastname@tld.de"
password = "pAssw0rd"
URL = "https://yourserver/names.nsf/($certifiers)?readviewentries&outputformat=JSON"
Call httpReq.Setheaderfield("Authorization", "Basic " + EncodeBase64 (user + ":" + password))
Dim json As string
json = httpReq.Get(URL)
Dim jsnav As NotesJSONNavigator
Set jsnav = session.CreateJSONNavigator(removeCRLF(json))
Dim el As NOTESJSONELEMENT
Dim arr As NOTESJSONARRAY
Dim obj As NOTESJSONOBJECT
Set el = jsnav.GetElementByName("viewentry")
Set arr = el.value
Set el = arr.GetFirstElement()
Set el = arr.GetNextElement()
Set obj = el.Value
Set el = obj.Getelementbyname("@unid")
MsgBox "unid: " + CStr(el.Value)
Set el = obj.Getelementbyname("@noteid")
MsgBox "noteid: " + CStr(el.Value)
End Sub
Private Function removeCRLF(json As String) As String
removeCRLF = Replace(Replace(json, Chr(13), ""),Chr(10),"")
End Function
Private Function EncodeBase64 (StrIn As String) As String
Dim session As New NotesSession
Dim stream As NotesStream
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim body As NotesMIMEEntity
Set stream = session.CreateStream
Call stream.WriteText (StrIn)
Set db = session.CurrentDatabase
Set doc = db.CreateDocument
Set body = doc.CreateMIMEEntity
Call body.SetContentFromText (stream, "", ENC_NONE)
Call body.EncodeContent (ENC_BASE64)
EncodeBase64 = body.ContentAsText
Call stream.Close
Set doc = Nothing
End Function
Fixing memory leaks may not be not the shiniest skill on a CV, but when things go wrong on production, it’s better to be prepared!
After reading this article, you’ll be able to monitor, understand, and debug the memory consumption of a Node.js application.
Kévin Maschtaler
https://marmelab.com/blog/2018/04/03/how-to-track-and-fix-memory-leak-with-nodejs.html
In addition to this very good article , here are a couple of tipps how to enable remote debugging.
node –inspect=192.168.178.133:9229 yourapp.js
This will bind the debugger to a different IP address:port. Otherwise only LOCALHOST will be available
If you are using PM2
pm2 start –node-args=”–inspect=192.168.178.133:9229″ yourapp.js
Then open Chrome browser and navigate to chrome://inspect
Check “Discover network targets” and click “Configure”
Your application is ready for inspect