How to convert raw String with a FIX message to QuickFIX/J Message object - quickfixj

What is the recommended way to convert a raw String, with one FIX message, to one QuickFIX/J Message object of the appropriate type, like one quickfix.fix44.ExecutionReport object?
I only have to deal with the FIX 4.4 version.
I found two options at this thread on Quickfixj-users mailing list.
I adapted these options to my situation and I was able to convert the message successfully in two different ways.
Example of a FIX message:
8=FIX.4.49=11035=834=749=EXEC52=20211014-19:39:25.75856=BANZAI6=011=114=10017=137=139=154=155=IBM150=2151=40010=237
I'd like to know which of the options below is better or if both are wrong and there's a better way.
Thanks in advance.
1 Option
quickfix.Message rawMessage = new quickfix.Message(rawMessageString);
// Example of MsgType = 8
MsgType msgType = new MsgType();
rawMessage.getHeader().getField(msgType);
MessageFactory messageFactory = new DefaultMessageFactory(ApplVerID.FIX44);
quickfix.Message message = messageFactory.create("FIX.4.4", msgType.getValue());
message.fromString(messageString, null, false, true);
// message.getName() = quickfix.fix44.ExecutionReport
System.out.println(message.getName() = " + message.getClass().getName());
2 Option
MessageFactory messageFactory = new DefaultMessageFactory(ApplVerID.FIX44);
quickfix.Message messageFromMessageUtils = quickfix.MessageUtils.parse(messageFactory, null, rawMessageString);
// message.getName() = quickfix.fix44.ExecutionReport
System.out.println(message.getName() = " + message.getClass().getName());
Data Dictionary
In the above tests, I didn't use a Data Dictionary, but in my real application I'll use one.

What I especially dislike about option 1 is that you need to construct two messages. One for the extraction of the MsgType and afterwards the actual message. Seems unnecessary to me.
If you are only using FIX4.4 I would go for the second option because it does all the magic for you, i.e. extracts the needed things like BeginString and MsgType from the raw String.
Downside: it will only work up to and including FIX4.4 since MessageUtils.parse() does only consider the BeginString (not the ApplVerID).
However, this should be no problem for your use case.
P.S.: If you ever need to parse messages for FIX5.0 and upwards, use the following (which is basically option 1 but without constructing a message to get the MsgType):
String msgType = MessageUtils.getMessageType(rawMessageString);
MessageFactory messageFactory = new DefaultMessageFactory(ApplVerID.FIX50); // FIX5.0 for example
quickfix.Message message = messageFactory.create("FIXT.1.1", ApplVerID.FIX50, msgType);
message.fromString(rawMessageString, dd, false, true);
P.P.S.: Not of interest for your use case but for the sake of completeness: you could also use one of the quickfix.Message(String) constructors but that would not return you a message of the correct type. Just a general quickfix.Message.

Related

How to remove non-ascii char from MQ messages with ESQL

CONCLUSION:
For some reason the flow wouldn't let me convert the incoming message to a BLOB by changing the Message Domain property of the Input Node so I added a Reset Content Descriptor node before the Compute Node with the code from the accepted answer. On the line that parses the XML and creates the XMLNSC Child for the message I was getting a 'CHARACTER:Invalid wire format received' error so I took that line out and added another Reset Content Descriptor node after the Compute Node instead. Now it parses and replaces the Unicode characters with spaces. So now it doesn't crash.
Here is the code for the added Compute Node:
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
DECLARE NonPrintable BLOB X'0001020304050607080B0C0E0F101112131415161718191A1B1C1D1E1F7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF1F2F3F4F5F6F7F8F9FAFBFCFDFEFF';
DECLARE Printable BLOB X'20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020';
DECLARE Fixed BLOB TRANSLATE(InputRoot.BLOB.BLOB, NonPrintable, Printable);
SET OutputRoot = InputRoot;
SET OutputRoot.BLOB.BLOB = Fixed;
RETURN TRUE;
END;
UPDATE:
The message is being parsed as XML using XMLNSC. Thought that would cause a problem, but it does not appear to be.
Now I'm using PHP. I've created a node to plug into the legacy flow. Here's the relevant code:
class fixIncompetence {
function evaluate ($output_assembly,$input_assembly) {
$output_assembly->MRM = $input_assembly->MRM;
$output_assembly->MQMD = $input_assembly->MQMD;
$tmp = htmlentities($input_assembly->MRM->VALUE_TO_FIX, ENT_HTML5|ENT_SUBSTITUTE,'UTF-8');
if (!empty($tmp)) {
$output_assembly->MRM->VALUE_TO_FIX = $tmp;
}
// Ensure there are no null MRM fields. MessageBroker is strict.
foreach ($output_assembly->MRM as $key => $val) {
if (empty($val)) {
$output_assembly->MRM->$key = '';
}
}
}
}
Right now I'm getting a vague error about read only messages, but before that it wasn't working either.
Original Question:
For some reason I am unable to impress upon the senders of our MQ
messages that smart quotes, endashes, emdashes, and such crash our XML
parser.
I managed to make a working solution with SQL queries, but it wasted
too many resources. Here's the last thing I tried, but it didn't work
either:
CREATE FUNCTION CLEAN(IN STR CHAR) RETURNS CHAR BEGIN
SET STR = REPLACE('–',STR,'–');
SET STR = REPLACE('—',STR,'—');
SET STR = REPLACE('·',STR,'·');
SET STR = REPLACE('“',STR,'“');
SET STR = REPLACE('”',STR,'”');
SET STR = REPLACE('‘',STR,'&lsqo;');
SET STR = REPLACE('’',STR,'’');
SET STR = REPLACE('•',STR,'•');
SET STR = REPLACE('°',STR,'°');
RETURN STR;
END;
As you can see I'm not very good at this. I have tried reading about
various ESQL string functions without much success.
So in ESQL you can use the TRANSLATE function.
The following is a snippet I use to clean up a BLOB containing non-ASCII low hex values so that it then be cast into a usable character string.
You should be able to modify it to change your undesired characters into something more benign. Basically each hex value in NonPrintable gets translated into its positional equivalent in Printable, in this case always a full-stop i.e. x'2E' in ASCII. You'll need to make your BLOB's long enough to cover the desired range of hex values.
DECLARE NonPrintable BLOB X'000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F';
DECLARE Printable BLOB X'2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E';
SET WorkBlob = TRANSLATE(WorkBlob, NonPrintable, Printable);
BTW if messages with invalid characters only come in every now and then I'd probably specify BLOB on the input node and then use something similar to the following to invoke the XMLNSC parser.
CREATE LASTCHILD OF OutputRoot DOMAIN 'XMLNSC'
PARSE(InputRoot.BLOB.BLOB CCSID InputRoot.Properties.CodedCharSetId ENCODING InputRoot.Properties.Encoding);
With the exception terminal wired up you can then correct the BLOB's of any messages containing parser breaking invalid characters before attempting to reparse.
Finally my best wishes as I've had a number of battles over the years with being forced to correct invalid message content in the "Integration Layer" after all that's what it's meant to do.

Open a libreoffice mail merged textdocument directly with swriter

I need help with opening the result of my mail merge operations directly in an new writer document.
Object mailMergeService = mcf.createInstanceWithContext(mailMergePackage, context);
XPropertySet mmProperties = UnoRuntime.queryInterface(XPropertySet.class, mailMergeService);
mmProperties.setPropertyValue("DocumentURL", templatePath);
mmProperties.setPropertyValue("DataSourceName", dbName);
mmProperties.setPropertyValue("CommandType", mmCommandType);
mmProperties.setPropertyValue("Command", mmCommand);
mmProperties.setPropertyValue("OutputType", mmOutputType);
// mmProperties.setPropertyValue("OutputURL", templateDirectory);
// mmProperties.setPropertyValue("FileNamePrefix", mmFileNamePrefix);
// mmProperties.setPropertyValue("SaveAsSingleFile", mmSaveAsSingleFile);
The mmOutputType is set as MailMergeType.SHELL
The LibreOffice API documentation says
"The output is a document shell.
The successful mail marge returns a XTextDocument based component."
So I've tried something like this
XJob job = UnoRuntime.queryInterface(XJob.class, mailMergeService);
Object mergedTextObject = job.execute(new NamedValue[0]);
String url = "private:factory/swriter";
loader.loadComponentFromURL(url, "_blank", 0, new PropertyValue[0]);
XTextDocument mergedText = UnoRuntime.queryInterface(XTextDocument.class, mergedTextObject);
XTextCursor cursor = mergedText.getText().createTextCursor();
cursor.setString(mergedText.getText().getString());
I guess I have to pass the XTextDocument component to the url-argument of the loadComponentFromURL method but I didnt find the right way to do that.
When I change the OutputType to MailMergeType.FILE the result is generated in a given directory and I can open the file and see that the mail merge succeeded. But this is not what my application should do.
Does someone know how I can open the result of the mail merge directly in an new writer document without saving the result to the hard drive?
Sincerly arthur
Hey guys I've found a simple way to open the result of my mail merge process directly.
The relevant snippets are these
XJob job = UnoRuntime.queryInterface(XJob.class, mailMergeService);
Object mergedTextObject = job.execute(new NamedValue[0]);
XTextDocument mergedText = UnoRuntime.queryInterface(XTextDocument.class, mergedTextObject);
mergedText.getCurrentController().getFrame().getContainerWindow().setVisible(true);
The last line of code made the window appear with the filled mail merge result.
I also don't need this line anymore
loader.loadComponentFromURL("private:factory/swriter", "_blank", 0, new PropertyValue[0]);
The document opens as a new instance of a swriter document. If you want to save the result as a file you can do this
mergedText.getCurrentController().getFrame().getContainerWindow().setVisible(true);
XStorable storeMM = UnoRuntime.queryInterface(XStorable.class, mergedText);
XModel modelMM = UnoRuntime.queryInterface(XModel.class, mergedText);
storeMM.storeAsURL(outputDirectory + outputFilename, modelMM.getArgs());
Sincerly
Arthur
What version of LO are you using? The SHELL constant has only been around since LO 4.4, and it is not supported by Apache OpenOffice yet, so it could be that it isn't fully implemented. However this code seems to show a working test.
If it is returning an XTextDocument, then normally I would assume the component is already open. However it sounds like you are not seeing a Writer window appear. Did you start LO in headless mode? If not, then maybe the process needs a few seconds before it can display.
Object mergedTextObject = job.execute(new NamedValue[0]);
Thread.sleep(10000);
Anyway to me it looks like your code has a mistake in it. These two lines would simply insert the text onto itself:
XTextCursor cursor = mergedText.getText().createTextCursor();
cursor.setString(mergedText.getText().getString());
Probably you intended to write something like this instead:
XTextDocument mergedText = UnoRuntime.queryInterface(XTextDocument.class, mergedTextObject);
String url = "private:factory/swriter";
XComponent xComponent = loader.loadComponentFromURL(url, "_blank", 0, new PropertyValue[0]);
XTextDocument xTextDocument = (XTextDocument)UnoRuntime.queryInterface(XTextDocument.class, xComponent);
XText xText = (XText)xTextDocument.getText();
XTextRange xTextRange = xText.getEnd();
xTextRange.setString(mergedText.getText().getString());
One more thought: getString() might just return an empty string if the entire document is in a table. If that is the case then you could use the view cursor or enumerate text content.
EDIT:
To preserve formatting including tables, you can do something like this (adapted from https://blog.oio.de/2010/10/27/copy-and-paste-without-clipboard-using-openoffice-org-api/):
// Select all.
XController xMergedTextController = mergedText.getCurrentController();
XTextViewCursorSupplier supTextViewCursor =
(XTextViewCursorSupplier) UnoRuntime.queryInterface(
XTextViewCursorSupplier.class, xMergedTextController);
XTextViewCursor oVC = supTextViewCursor.getViewCursor();
oVC.gotoStart(False) // This would not work if your document began with a table.
oVC.gotoEnd(True)
// Copy and paste.
XTransferableSupplier xTransferableSupplier = UnoRuntime.queryInterface(XTransferableSupplier.class, xMergedTextController);
XTransferable transferable = xTransferableSupplier.getTransferable();
XController xController = xComponent.getCurrentController();
XTransferableSupplier xTransferableSupplier_newDoc = UnoRuntime.queryInterface(XTransferableSupplier.class, xController);
xTransferableSupplier_newDoc.insertTransferable(transferable);

F# non-literal printf format strings - how to make them passable as parameters?

I would like to use non-literal strings for the "format" parameter of a logging type function, as shown here:
// You need to make c:\testDir or something similar to run this.....
//
let csvFile = #"c:\testDir\foo.csv"
open System.IO
let writer file (s:string) =
use streamWriter = new StreamWriter(file, true)
streamWriter.WriteLine(s)
// s
let log format = Printf.ksprintf (writer csvFile) format
let oneString format = (Printf.StringFormat<string->string> format)
let format = oneString "(this does not %s)"
//log format "important string"
log "this works %s" "important string"
My first attempt used a literal string, and the above fragment should work fine for you if you create the directory it needs or similar.
After discovering that you can't just "let bind" a format string, I then learned about Printf.StringFormatand more details about Printf.ksprintf, but I am obviously missing something, because I can't get them to work together with my small example.
If you comment out the last line and reinstate its predecessor, you will see a compiler error.
Making the function writer return a string almost helped (uncomment its last line), but that then makes log return a string (which means every call now needs an ignore).
I would like to know how to have my format strings dynamically settable within the type checked F# printf world!
Update
I added the parameter format to log to avoid a value restriction error that happens if log is not later called as it is in my example. I also change fmt to format in oneString.
Update
This is a different question from this one. That question does not show a function argument being passed to Printf.StringFormat (a minor difference), and it does not have the part about Printf.ksprintf not taking a continuation function that returns unit.
I thought I had found a solution with:
let oneString format = (Printf.StringFormat<string->string,unit> format)
this compiles, but there is a runtime error. (The change is the ,unit)

How to validate the connection string comming from a text file

there is a way by which we parse any time input given by user...
here i am taking the input of a connection string from a text file
i am supposed to assume that the user who keeps this string in the text file can be trusted.
but what if he makes a typo a mistake unknowingly, that will cause the coming code to cause exception,
i would like a way to check the string for the correct format, like parse it some way to see if it is the way an connection tring should be, and then possibly use that parsed result.
edit
as requested the sample code i using, and the connection the sqladaptor using
//Connect to a remote instance of SQL Server.
Server srv;
ServerConnection conContext = new ServerConnection();
conContext.ServerInstance = #"A-63A9D4D7E7934\SECOND";
conContext.LoginSecure = false;
conContext.Login = "sa";
conContext.Password = "two";
srv = new Server(conContext);
Response.Write(srv.Information.Version);
//Data Source=A-63A9D4D7E7834\SECOND;Initial Catalog=replicate;User ID=sa;Password=***********
A regular expresion can't validate an incorrect password, user name, or host name, so it is possible for your user to generate a valid string that will still cause an exception.
Also, you can't build a regex that will cover every possible combination of connection arguments.
The only robust solution, would be to wrap the connection code in a try/catch block and provide an appropriate error message.

When parsing XML, the character é is missing

I have an XML as input to a Java function that parses it and produces an output. Somewhere in the XML there is the word "stratégie". The output is "stratgie". How should I parse the XML as to get the "é" character as well?
The XML is not produced by myself, I get it as a response from a web service and I am positive that "stratégie" is included in it as "stratégie".
In the parser, I have:
public List<Item> GetItems(InputStream stream) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeLst = doc.getElementsByTagName("item");
List<Item> items = new ArrayList<Item>();
Item currentItem = new Item();
Node node = nodeLst.item(0);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element item = (Element) node;
if(node.getChildNodes().getLength()==0){
return null;
}
NodeList title = item.getElementsByTagName("title");
Element titleElmnt = (Element) title.item(0);
if (null != titleElmnt)
currentItem.setTitle(titleElmnt.getChildNodes().item(0).getNodeValue());
....
Using the debugger, I can see that titleElmnt.getChildNodes().item(0).getNodeValue() is "stratgie" (without the é).
Thank you for your help.
I strongly suspect that either you're parsing it incorrectly or (rather more likely) it's just not being displayed properly. You haven't really told us anything about the code or how you're using the result, which makes it hard to give very concrete advice.
As ever with encoding issues, the first thing to do is work out exactly where data is getting lost. Lots of logging tends to be the way forward: create a small test case that demonstrates the problem (as small as you can get away with) and log everything about the data. Don't just try to log it as raw text: log the Unicode value of each character. That way your log will have all the information even if there are problems with the font or encoding you use to view the log.
The answer was here: http://www.yagudaev.com/programming/java/7-jsp-escaping-html
You can either use utf-8 and have the 'é' char in your document instead of é, or you need to have a parser that understand this entity which exists in HTML and XHTML and maybe other XML dialects but not in pure XML : in pure XML there's "only" ", <, > and maybe &apos; I don't remember.
Maybe you can need to specify those special-char entities in your DTD or XML Schema (I don't know which one you use) and tell your parser about it.

Resources