Is there a C# implementation of Redis-rdb-tools? - c#-4.0

Taking a look at Redis-RDB-Tools, it looks like there are some useful functions for monitoring the health of your Redis server.
ServiceStack.Redis seems to have a good set of client functions (but I'm using BookSleeve).
Are there any C# implementations that give me some basic health checks - consumed memory, disk usage, etc?
-- UPDATE --
Thanks to BookSleeve's GetInfo() command, the following is returned... however I should have been more specific: Is there a way of getting back the server info as parameters/object properties or a pre-packaged way of parsing the output values?
Here is the output from GetInfo():
"# Server\r\nredis_version:2.6.10\r\nredis_git_sha1:00000000\r\nredis_git_dirty:0\r\nredis_mode:standalone\r\nos:Linux 2.6.32-279.19.1.el6.x86_64 x86_64\r\narch_bits:64\r\nmultiplexing_api:epoll\r\ngcc_version:4.4.6\r\nprocess_id:2492\r\nrun_id:62402d583871f4b83f469917966aed8d163d02f3\r\ntcp_port:6379\r\nuptime_in_seconds:502354\r\nuptime_in_days:5\r\nlru_clock:1928056\r\n\r\n# Clients\r\nconnected_clients:7\r\nclient_longest_output_list:0\r\nclient_biggest_input_buf:175\r\nblocked_clients:0\r\n\r\n# Memory\r\nused_memory:1402576\r\nused_memory_human:1.34M\r\nused_memory_rss:9719808\r\nused_memory_peak:1675192\r\nused_memory_peak_human:1.60M\r\nused_memory_lua:31744\r\nmem_fragmentation_ratio:6.93\r\nmem_allocator:jemalloc-3.2.0\r\n\r\n# Persistence\r\nloading:0\r\nrdb_changes_since_last_save:3035\r\nrdb_bgsave_in_progress:0\r\nrdb_last_save_time:1360955487\r\nrdb_last_bgsave_status:ok\r\nrdb_last_bgsave_time_sec:-1\r\nrdb_current_bgsave_time_sec:-1\r\naof_enabled:0\r\naof_rewrite_in_progress:0\r\naof_rewrite_scheduled:0\r\naof_last_rewrite_time_sec:-1\r\naof_current_rewrite_time_sec:-1\r\naof_last_bgrewrite_status:ok\r\n\r\n# Stats\r\ntotal_connections_received:18822\r\ntotal_commands_processed:12547\r\ninstantaneous_ops_per_sec:3\r\nrejected_connections:0\r\nexpired_keys:0\r\nevicted_keys:0\r\nkeyspace_hits:374\r\nkeyspace_misses:39\r\npubsub_channels:1\r\npubsub_patterns:0\r\nlatest_fork_usec:0\r\n\r\n# Replication\r\nrole:master\r\nconnected_slaves:0\r\n\r\n# CPU\r\nused_cpu_sys:57.82\r\nused_cpu_user:208.63\r\nused_cpu_sys_children:0.00\r\nused_cpu_user_children:0.00\r\n\r\n# Keyspace\r\ndb0:keys=6,expires=0\r\n"

With regards to the updated question: the information there is not currently exposed as "parsed", but that sounds like a reasonable thing to add; I suspect I'll hide the GetInfo() method, move it to .Server.GetInfo(), and expose it in a parsed form. The code to split it already exists, though - but as a private method: RedisConnectionBase.ParseInfo:
static Dictionary<string, string> ParseInfo(string result)
{
string[] lines = result.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
var data = new Dictionary<string, string>();
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
if (string.IsNullOrEmpty(line) || line[0] == '#') continue; // 2.6+ can have empty lines, and comment lines
int idx = line.IndexOf(':');
if (idx > 0) // double check this line looks about right
{
data.Add(line.Substring(0, idx), line.Substring(idx + 1));
}
}
return data;
}

Call Redis's INFO command, it provides all the different server info stats inside a redis-server.
Here's a page dump of all the available stats on a 2.5.12 version of redis-server.

Related

create custom module for pdf manipulation

I want to create a custom Kofax module. When it comes to the batch processing the scanned documents get converted to PDF files. I want to fetch these PDF files, manipulate them (add a custom footer to the PDF document) and hand them back to Kofax.
So what I know so far:
create Kofax export scripts
add a custom module to Kofax
I have the APIRef.chm (Kofax.Capture.SDK.CustomModule) and the CMSplit as an example project. Unfortunately I struggle getting into it. Are there any resources out there showing step by step how to get into custom module development?
So I know that the IBatch interface represents one selected batch and the IBatchCollection represents the collection of all batches.
I would just like to know how to setup a "Hello World" example and could add my code to it and I think I don't even need a WinForms application because I only need to manipulate the PDF files and that's it...
Since I realized that your question was rather about how to create a custom module in general, allow me to add another answer. Start with a C# Console Application.
Add Required Assemblies
Below assemblies are required by a custom module. All of them reside in the KC's binaries folder (by default C:\Program Files (x86)\Kofax\CaptureSS\ServLib\Bin on a server).
Setup Part
Add a new User Control and Windows Form for setup. This is purely optional - a CM might not even have a setup form, but I'd recommend adding it regardless. The user control is the most important part, here - it will add the menu entry in KC Administration, and initialize the form itself:
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISetupForm
{
[DispId(1)]
AdminApplication Application { set; }
[DispId(2)]
void ActionEvent(int EventNumber, object Argument, out int Cancel);
}
[ClassInterface(ClassInterfaceType.None)]
[ProgId("Quipu.KC.CM.Setup")]
public class SetupUserControl : UserControl, ISetupForm
{
private AdminApplication adminApplication;
public AdminApplication Application
{
set
{
value.AddMenu("Quipu.KC.CM.Setup", "Quipu.KC.CM - Setup", "BatchClass");
adminApplication = value;
}
}
public void ActionEvent(int EventNumber, object Argument, out int Cancel)
{
Cancel = 0;
if ((KfxOcxEvent)EventNumber == KfxOcxEvent.KfxOcxEventMenuClicked && (string)Argument == "Quipu.KC.CM.Setup")
{
SetupForm form = new SetupForm();
form.ShowDialog(adminApplication.ActiveBatchClass);
}
}
}
Runtime Part
Since I started with a console application, I could go ahead and put all the logic into Program.cs. Note that is for demo-purposes only, and I would recommend adding specific classes and forms later on. The example below logs into Kofax Capture, grabs the next available batch, and just outputs its name.
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, eventArgs) => KcAssemblyResolver.Resolve(eventArgs);
Run(args);
return;
}
static void Run(string[] args)
{
// start processing here
// todo encapsulate this to a separate class!
// login to KC
var login = new Login();
login.EnableSecurityBoost = true;
login.Login();
login.ApplicationName = "Quipu.KC.CM";
login.Version = "1.0";
login.ValidateUser("Quipu.KC.CM.exe", false, "", "");
var session = login.RuntimeSession;
// todo add timer-based polling here (note: mutex!)
var activeBatch = session.NextBatchGet(login.ProcessID);
Console.WriteLine(activeBatch.Name);
activeBatch.BatchClose(
KfxDbState.KfxDbBatchReady,
KfxDbQueue.KfxDbQueueNext,
0,
"");
session.Dispose();
login.Logout();
}
}
Registering, COM-Visibility, and more
Registering a Custom Module is done via RegAsm.exe and ideally with the help of an AEX file. Here's an example - please refer to the documentation for more details and all available settings.
[Modules]
Minimal CM
[Minimal CM]
RuntimeProgram=Quipu/CM/Quipu.KC.CM/Quipu.KC.CM.exe
ModuleID=Quipu.KC.CM.exe
Description=Minimal Template for a Custom Module in C#
Version=1.0
SupportsTableFields=True
SupportsNonImageFiles=True
SetupProgram=Minimal CM Setup
[Setup Programs]
Minimal CM Setup
[Minimal CM Setup]
Visible=0
OCXFile=Quipu/CM/Quipu.KC.CM/Quipu.KC.CM.exe
ProgID=Quipu.KC.CM.Setup
Last but not least, make sure your assemblies are COM-visible:
I put up the entire code on GitHub, feel free to fork it. Hope it helps.
Kofax exposes a batch as an XML, and DBLite is basically a wrapper for said XML. The structure is explained in AcBatch.htm and AcDocs.htm (to be found under the CaptureSV directory). Here's the basic idea (just documents are shown):
AscentCaptureRuntime
Batch
Documents
Document
A single document has child elements itself such as pages, and multiple properties such as Confidence, FormTypeName, and PDFGenerationFileName. This is what you want. Here's how you would navigate down the document collection, storing the filename in a variable named pdfFileName:
IACDataElement runtime = activeBatch.ExtractRuntimeACDataElement(0);
IACDataElement batch = runtime.FindChildElementByName("Batch");
var documents = batch.FindChildElementByName("Documents").FindChildElementsByName("Document");
for (int i = 0; i < documents.Count; i++)
{
// 1-based index in kofax
var pdfFileName = documents[i + 1]["PDFGenerationFileName"];
}
Personally, I don't like this structure, so I created my own wrapper for their wrapper, but that's up to you.
With regard to the custom module itself, the sample shipped is already a decent start. Basically, you would have a basic form that shows up if the user launches the module manually - which is entirely optional if work happens in the back, preferably as Windows Service. I like to start with a console application, adding forms only when needed. Here, I would launch the form as follows, or start the service. Note that I have different branches in case the user wants to install my Custom Module as service:
else if (Environment.UserInteractive)
{
// run as module
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new RuntimeForm(args));
}
else
{
// run as service
ServiceBase.Run(new CustomModuleService());
}
}
The runtime for itself just logs you into Kofax Capture, registers event handlers, and processes batch by batch:
// login to KC
cm = new CustomModule();
cm.Login("", "");
// add progress event handlers
cm.BatchOpened += Cm_BatchOpened;
cm.BatchClosed += Cm_BatchClosed;
cm.DocumentOpened += Cm_DocumentOpened;
cm.DocumentClosed += Cm_DocumentClosed;
cm.ErrorOccured += Cm_ErrorOccured;
// process in background thread so that the form does not freeze
worker = new BackgroundWorker();
worker.DoWork += (s, a) => Process();
worker.RunWorkerAsync();
Then, your CM fetches the next batch. This can either make use of Kofax' Batch Notification Service, or be based on a timer. For the former, just handle the BatchAvailable event of the session object:
session.BatchAvailable += Session_BatchAvailable;
For the latter, define a timer - preferrably with a configurable polling interval:
pollTimer.Interval = pollIntervalSeconds * 1000;
pollTimer.Elapsed += PollTimer_Elapsed;
pollTimer.Enabled = true;
When the timer elapses, you could do the following:
private void PollTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
mutex.WaitOne();
ProcessBatches();
mutex.ReleaseMutex();
}

How to get single values from am gremlinquery in C# code

I am using gremlin with Azure Cosmos DB. I'm using this code to get a list of files from a graph database.
public async Task<List<string>> GetFilesWithMoreThanOneFilename()
{
List<string> list = new List<string>();
using (var gremlinClient = new GremlinClient(gremlinServer, new GraphSON2Reader(), new GraphSON2Writer(), GremlinClient.GraphSON2MimeType))
{
var resultSet = await gremlinClient.SubmitAsync<dynamic>("g.V().hasLabel('file').where(out().count().is(gt(1)))");
if (resultSet.Count > 0)
{
foreach (var result in resultSet)
{
string output = JsonConvert.SerializeObject(result);
list.Add(output);
}
}
}
return list;
}
The output string looks like this:
{"id":"0a37e4896b6310b6d152f6cf89336173ffb89b819f7955494322e0f0bec017b4","label":"file","type":"vertex","properties":{"fileSize":[{"id":"456b087c-7cf3-43ea-a482-0f31219bc520","value":"41096"}],"mimeType":[{"id":"d849b065-16f8-465b-986c-f8e0fdda9ac7","value":"text/plain"}]}}
My Question is how I can get a single value from the result. For example just the ID or the mimeType or is the only possiblity to work with the output and string manipulation?
Due to your output data is in json format, so you could use Newtonsoft.Json to read the data.
I create a json file with your data, you could just parse the json data without file. And just read the id and properties.fileSize
static void Main(string[] args)
{
JObject jsonData = JObject.Parse(File.ReadAllText( "test.json"));
Console.WriteLine("id:"+jsonData["id"].ToString());
Console.WriteLine("properties:"+jsonData["properties"]["fileSize"].ToString());
Console.ReadLine();
}
And here is the result:
Hope this could help you, if yo ustill have other questions, please let me know.
Update: if you want to get the value in the array, you could use this to get the value:
Console.WriteLine("mimeType.value:" + jsonData["properties"]["mimeType"][0]["value"].ToString());
In general, you should only retrieve the values that you actually need and not vertices with all their properties and then filter locally. This is equivalent to relational databases where you wouldn't usually do SELECT * and instead do something like SELECT name.
Additionally, you should specify the return type you're expecting which allows Gremlin.NET to deserialize the result for you so you don't have to do that yourself.
These two suggestions together give you something like this:
var names = await client.SubmitAsync<string>(
"g.V().hasLabel('person').where(out().count().is(gt(1))).values('name')");
Console.WriteLine($"First name: {names.First()}");
names is then just a ResultSet<string> which implements IReadOnlyCollection<string>.

Arangov2.8 - Examples of register user functions in AQL

I need to filter some nodes by regex but since Arango v2.8 does not have this functionality I want to try registering user functions can anyone give me an example of how to register a simple function and use it in AQL?
I'm trying:
var myfunc = function (ideation_node) {
for (var i = 0; i < ideation_node.length; ++i) {
if (true) {
return ideation_node[i];
}
}
return null;
}
RETURN myfunctions::myfunc()
You use triagens/ArangoDb/AqlUserFunction to register a user function.
$funcHandler = new \triagens\ArangoDb\AqlUserFunction($arangoConnection);
$funcHandler->name = 'myfunctions::myFunc';
$funcHandler->code = 'function(ideation_node){...}';
$funcHandler->register();
In AQL-Statements registered functions are used like any other function, using the fully-qualified (i.e. with namespace-prefix) name of the function.
Make sure that the function is side-effect free and don't manipulate input-parameters, but use return-values to pass the calculation results to the outside world.

How to find out the number of entries or size of table in the Accumulo?

I have inserted data into accumulo. But how would i know the exact count of the table ?
Is there any method or API to read the accumulo table size of number of entries ?
First off, Accumulo doesn't know the exact size of a table during active ingest -- it will be an approximate. I don't think any public API methods exist to get this information, although there are some internal methods you could call. Something like the following should work:
ClientContext context = new ClientContext(instance, credentials, clientConfiguration);
MasterClientService.Iface client = null;
MasterMonitorInfo mmi = null;
while (null == mmi) {
try {
client = MasterClient.getConnection(context);
if (client != null) {
mmi = client.getMasterStats(Tracer.traceInfo(), context.rpcCreds())
}
} finally {
if (null != client) {
MasterClient.close(client);
}
}
}
for (Entry<String,TableInfo> table : mmi.getTableMap().entrySet()) {
System.out.println(table.getKey() + "=>" + (table.getValue().recs + table.getValue().recsInMemory));
}
This is similar to how the Accumulo monitor obtains these values. Because these are internal APIs, they're a little rough to use and may change across releases. If you'd like to see these APIs exposed through the normal Instance or Connector methods, please open an issue on the project's JIRA instance!
Do you need to do it programatically? If not, there are various ways that you can do this. The easiest is to go to the Accumulo monitor page on port 50095. If you don't have a ton of data, from the command line you can simply do
accumulo shell -u username -p password -e "scan -t foo -np" | wc -l

How do you deal with the fact, that URLs are case sensitive in xPages?

How do you deal with the fact, that URLs are case sensitive in xPages even for parameters? For example URL:
my_page.xsp?folderid=785478 ... is not the same as ...
my_page.xsp?FOLDERID=785478
How to make, for example, a proper check that params contain some key e.g.
param.containsKey("folderid") which desnt work when there is 'FOLDERID' in URL.
I'd suggest defining a couple convenience #Functions:
var #HasParam = function(parameter) {
var result:boolean = false;
for (var eachParam : param.keySet()) {
if (eachParam.toLowerCase() == parameter.toLowerCase()) {
result = true;
break;
}
}
return result;
};
var #GetParam = function(parameter) {
var result = "";
if (#HasParam(parameter)) {
for (var eachParam : param.keySet()) {
if (eachParam.toLowerCase() == parameter.toLowerCase()) {
result = param.get(eachParam);
break;
}
}
}
return result;
};
Then you can safely query the parameters without caring about case. For bonus points, you could add requestScope caching so that you can skip looping through the keySet if you're examining a parameter that you've previously looked at during the same request.
you may use this function:
context.getUrlParameter('param_name')
then test if it's null or not.
make sure to decide for one,so either upper or lowercase
other than that i'd suggest something like
KeyValuePair<string,string> kvp = null;
foreach(KeyValuePair<string,string> p in param)
{
if(UPPERCASE(p.Key) == UPPERCASE("folderid"))
{
kvp = p;
break;
}
}
syntax isn't correct and idk the uppercase method in c# right now,but you get the point
The easiest answer is ofcourse the obvious. Be sure that the parameters you are using througout your application are always the same on every url you are generating and know what to expect. A good approach to accomplish this is to create a ssjs function which generates url's for you according to the objects you submit.
In this function you could check which object you are receiving and with the use of keywords and so forth generate the correct url. This way generating twice a url with the same input parameters should always generate the exact same url.
another option would be just to double check with a bit of code like this
var key = "yourkey";
if(param.contains(#uppercase(key)) || param.contains(#lowercase(key)){
// do stuff
}
But should not be necesarry if the url you are parsing is generated by your own application
Edit after post of topic starter
Another option would be to grap the url directly from from the facescontext and to convert it to a string first. When it is a string you can parse the parameters yourself.
You can combine server side substitution/redirection to get around the issue that David mentioned. So a substitution rule will redirect incoming patern like this:
http://myhost/mypage/param (/mypage/* => which converts to - /dbpath/mypage.xsp?*) - substitution is tricky so please handle with care.
Also I believe I read somewhere that context.getUrlParameter is not case sensitive - can someone please confirm this.
Hope this helps.

Resources