@Timothy Leavitt 2014.1.1
- Log in to post comments
@Timothy Leavitt 2014.1.1
it in MSSQL
@Eduard Lebedyuk was wondering if you can spot what the problem is thanks in advance
@Eduard Lebedyuk Looked at the class this classEnsLib.RecordMap.Service.BatchFileService and the EnsLib.RecordMap.Service.BatchStandard class apparently these are fine but for some reason the custom class acts as if it goes into a loop.I have put some traces in my code and tried to capture the status and change it to fail if a certain validation fails but that makes the whole service to log an error after successfully delivering the first message .
here are the changes made thanks for your help
ClassMethod GetBatchHeader(pIOStream As %IO.DeviceStream, pTimeout As %Numeric = -1, Output pBatch As EnsLib.RecordMap.SimpleBatch, ByRef pLookAhead As %String) As %Status
{
$$$TRACE("Begin")
Try {
#dim cpStatus as %Status=$$$OK
Set tStatus = $$$OK
Set pBatch = ""
Set tTerm = ..GetHeaderTerm()
//Set tFullHeader = 63 _ tTerm
Set tHeaderLen =63+$length(tTerm)
set stage=""
If tHeaderLen
{
Set tFound = 0
$$$TRACE("0")
Set tLeadingJunk = ""
Set pLookAhead = $get(pLookAhead)
Set tTimeout = pTimeout
Set tEndTime = $zhorolog + pTimeout
// While ('tFound) && ('pIOStream.AtEnd)
// {
Set tReadLen = tHeaderLen - $length(pLookAhead)
If tReadLen > 0
{
Set tData = pLookAhead _ pIOStream.Read(tReadLen, .tTimeout, .tStatus)
$$$TRACE("1")
If $$$ISERR(tStatus) Quit
If tTimeout
{
Set tStatus = $$$ERROR($$$EnsErrTCPReadTimeoutExpired, pTimeout, tReadLen)
Quit
}
Set pLookAhead = ""
}
Else
{
$$$TRACE("Else treadlen less than 0-[2]")
Set tData = $extract(pLookAhead, 1, tHeaderLen)
Set pLookAhead = $extract(pLookAhead, tHeaderLen + 1, *)
}
If ($extract(tData,1,3) = "001" )
{
Set pBatch = ..%New()
set pBatch.BatchHeader = tData
Set tFound = 1
$$$TRACE("Foundlee[3]"_tFound)
Quit
}
Else
{
$$$TRACE("else $extract(tData1,3)Failed[3]")
Set pLookAhead = pLookAhead _ tData
#; Check if we should start discarding leading data
If ($length(pLookAhead) >= tHeaderLen)
{
If ($length(tLeadingJunk) < 400)
{
Set tLeadingJunk = tLeadingJunk _ $extract(pLookAhead,1)
}
Set pLookAhead = $extract(pLookAhead,2,*)
}
set cpStatus=0
set stage="Extractfailed" //check here
// Quit
//Continue
}
If (pTimeout = -1)
{
Set tTimeout = -1
}
Else
{
$$$TRACE("time out ok [4]")
Set tCurrTime = $zhorolog
If (tCurrTime > tEndTime)
{
Set tStatus = $$$ERROR($$$EnsErrTCPReadTimeoutExpired, pTimeout, tReadLen)
Quit
}
Set tTimeout = tEndTime - tCurrTime
}
If $$$ISERR(tStatus) Quit
// } //while
$$$TRACE("while end"_tFound)
If $$$ISERR(tStatus) Quit
#; Clear the lookahead buffer if we didn't find the batch header
$$$TRACE("###Status###[5]"_tStatus)
If (('tFound) && ($length(tLeadingJunk) < 400)&&('pLookAhead="")&&($$$ISERR(cpStatus)))
{
Set tLeadingJunk = tLeadingJunk _ $get(pLookAhead)
Set pLookAhead = ""
$$$TRACE("###Not Found###[6]"_tFound)
}
If (tLeadingJunk '= "") && ('..#IgnoreLeadingData)
{
#; Use JS escaping to handle control characters
Set tLoggedJunk = $zconvert($extract(tLeadingJunk,1,400),"O","JS") _ $select($length(tLeadingJunk) > 400: "...", 1: "")
$$$LOGWARNING($$$FormatText($$$Text("[]Discarding unexpected leading data: '%1'","Ensemble"),tLoggedJunk))
}
If (('tFound)&&($$$ISERR(cpStatus)))
{
Set pBatch = ""
$$$TRACE("Not Found[8]")
//Set tStatus = $$$ERROR($$$EnsRecordMapErrBatchHeaderNotFound,$classname($this))
if (stage="Extractfailed" &&($$$ISERR(cpStatus)))
{
set tStatus=cpStatus
$$$LOGWARNING($classname($this)_":: Empty Batch Discarded")
set tStatus=$$$OK
}
else
{
// Set tStatus = $$$EnsSystemError
Set tStatus = $$$ERROR($$$EnsRecordMapErrBatchHeaderNotFound,$classname($this))
}
Quit
}
}
Else
{
Set pBatch = ..%New()
Set pLookAhead = $get(pLookAhead)
Quit
}
}
Catch ex
{
$$$TRACE("in catch]")
Set tStatus = $$$EnsSystemError
quit
}
Quit tStatus
}Since this is made up of three classes how do I control the status to send the call with within the block have a look at where I have newStatus and I would like to quit and pass new status
thanks that seems to help
@Eduard Lebedyuk I have tried your suggestions and still get the error when opening the file with adobe reader. If I try on a simple pass through operation like this below everything works fine.
ERROR: Adobe acrobat reader could not open file.pdf because it is either not a supported file type or because the file has been damaged.
the service
Method OnProcessInput(pInput As %Stream.Object, Output pOutput As %RegisteredObject) As %Status
{
#dim pt as TestingEnvironment.ECGTrace.TEST.FSMREQ=##class(TestingEnvironment.ECGTrace.TEST.FSMREQ).%New()
Set tSource=pInput.Attributes("Filename"), pInput=$zobjclassmethod(..#CONTAINERCLASS,"%New",pInput)
Set tSC=..resolveAndIndex(pInput) Quit:$$$ISERR(tSC) tSC
set pt.filestream=pInput
set pt.path=tSource
Set tWorkArchive=(""'=..Adapter.ArchivePath)&&(..Adapter.ArchivePath=..Adapter.WorkPath || (""=..Adapter.WorkPath && (..Adapter.ArchivePath=..Adapter.FilePath)))
$$$SyncCommitSet(tSyncCommit)
For iTarget=1:1:$L(..TargetConfigNames, ",") { Set tOneTarget=$ZStrip($P(..TargetConfigNames,",",iTarget),"<>W") Continue:""=tOneTarget
$$$sysTRACE("Sending input Stream "_pInput.Stream_"("_pInput.Stream.Size_")"_$S(tWorkArchive:" Async",1:" Sync")_" from '"_tSource_"' to '"_tOneTarget_"'")
If tWorkArchive {
Set tSC1=..SendRequestAsync(tOneTarget,pt) Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
//Set tSC1=..SendRequestAsync(tOneTarget,pInput) Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
} Else {
#; If not archiving send Sync to avoid Adapter deleting file before Operation gets it
//Set tSC1=..SendRequestSync(tOneTarget,pInput) Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
Set tSC1=..SendRequestSync(tOneTarget,pt) Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
}
}
$$$SyncCommitClear(tSyncCommit)
Quit tSC
}the operation
Method OnMessage(pREs As TestingEnvironment.ECGTrace.TEST.FSMREQ, pRequest As Ens.StreamContainer, Output pResponse As %Persistent) As %Status
{
set pRequest=pREs.filestream
Quit:'$IsObject(pRequest.Stream) $$$ERROR($$$EnsErrGeneral,"No Stream contained in StreamContainer Request")
Set tFilename=..Adapter.CreateTimestamp(##class(%File).GetFilename(pRequest.OriginalFilename),..Filename)
Set tSC=..Adapter.PutStream(tFilename, pRequest.Stream)
Do pRequest.%Save() ; re-save in case PutStream() optimization changed the Stream filename
Quit tSC
}my code the service
Method OnProcessInput(pInput As %Stream.Object, Output pOutput As %RegisteredObject) As %Status
{
#dim meta as DocumentUpload.GenericUploadMREQ=##class(DocumentUpload.GenericUploadMREQ).%New()
;get the filepath from the request
;;wrap the stream object into container for easy transpotation
Set tFileName=pInput.Attributes("Filename") , pInput=$zobjclassmethod(..#CONTAINERCLASS,"%New",pInput)
$$$TRACE(tFileName)
;get the file name
set dataPiece=##class(%File).GetFilename(tFileName)
$$$TRACE(dataPiece)
Set tSC=..resolveAndIndex(pInput) Quit:$$$ISERR(tSC) tSC
;check if the file path data is populated
if (dataPiece'="")
{
;build the ECG Message
set meta.ClientID =$Piece(dataPiece,"_",1)
set meta.LastName=$Piece(dataPiece,"_",2)
set meta.FirstName=$Piece(dataPiece,"_",3)
set meta.DateOfBirth=$Piece(dataPiece,"_",4)
set meta.Directory =$Piece(tFileName,"\",*-1)
set meta.OGFileName =dataPiece
set meta.Fullpath =tFileName
;get the date to testing
set DateOfTest=$Piece(dataPiece,"_",5)
;get the time of testing
set mtim=$Piece(dataPiece,"_",6)
;separate the extension of the file path and the time
set TimeOfTest=$Piece(mtim,".",1)
set meta.FileTimeStamp =DateOfTest_""_TimeOfTest
set meta.recordAdded =$ZDT($ZTIMESTAMP,3,1,3)
set meta.TargetConfig ="RIO.DocumentUpload.RiOFileOPRN"
set meta.payLoad=pInput
set meta.DocumentType=..DocumentType
set meta.Description=..Description
set meta.Title=..Title
set meta.FinalRevision=..Revesion
set meta.Author=..Author_""_$Piece(tFileName,"\",*-1)
set messagetype=$PIECE(..Author," ",1)
set meta.UserId=..UserID
set meta.sourceConfig =tFileName
set meta.TypeMes=messagetype
Set tWorkArchive=(""'=..Adapter.ArchivePath)&&(..Adapter.ArchivePath=..Adapter.WorkPath || (""=..Adapter.WorkPath && (..Adapter.ArchivePath=..Adapter.FilePath)))
$$$SyncCommitSet(tSyncCommit)
For iTarget=1:1:$L(..TargetConfigNames, ",") { Set tOneTarget=$ZStrip($P(..TargetConfigNames,",",iTarget),"<>W") Continue:""=tOneTarget
$$$sysTRACE("Sending input Stream "_pInput.Stream_"("_pInput.Stream.Size_")"_$S(tWorkArchive:" Async",1:" Sync")_" from '"_tFileName_"' to '"_tOneTarget_"'")
If tWorkArchive {
Set tSC1=..SendRequestAsync(tOneTarget,meta) Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
} Else {
#; If not archiving send Sync to avoid Adapter deleting file before Operation gets it
Set tSC1=..SendRequestSync(tOneTarget,meta) Set:$$$ISERR(tSC1) tSC=$$$ADDSC(tSC,tSC1)
}
}
$$$SyncCommitClear(tSyncCommit)
}
quit tSC
}the operation please note I have a Route in between which simple transformers the message to the message expected by the operation its just a simple mapping scenario
Method WriteOutFiles(pRequest As DocumentUpload.FileMREQ, pInput As Ens.StreamContainer, Output pResponse As DocumentUpload.GenericRESP) As %Status
{
set pInput=pRequest.FileStream
;the variable to hold the status for the method
#dim status as %Status=$$$OK
;clear the pResponse
kill pResponse
set pResponse=$$$NULLOREF
;set the file name to the sequence number
set ..Filename=pRequest.NewFileName
;the filepath set on the settings of this OPERATION
set origDirectory = ..Adapter.FilePath
;the file directory to drop the file
set ..Adapter.FilePath = ..Adapter.FilePath_"\"_..StubDirectory
;start writing out file data to the stub file
set:$$$ISOK(status) status= ..Adapter.PutLine(..Filename_"."_..stubExtension,
$CHAR(34)_ pRequest.ClientID_$CHAR(34)_$CHAR(44)_$CHAR(34)_pRequest.UserId_$CHAR(34)_$CHAR(44)_$CHAR(34)_
pRequest.DocumentType _$CHAR(34)_$CHAR(44)_$CHAR(34)_pRequest.Title_$CHAR(34)_$CHAR(44)_$CHAR(34)_pRequest.Description_$CHAR(34)_$CHAR(44)_$CHAR(34)_pRequest.Author _$CHAR(34)_$CHAR(44)_$CHAR(34)_pRequest.DocumentDate_$CHAR(34)_$CHAR(44)_$CHAR(34)_pRequest.FinalRevision_$CHAR(34))
if ($$$ISOK(status))
{
;set back to the operation settings
set ..Adapter.FilePath = origDirectory
;set the file name to write out to
set ..Filename=pRequest.NewFileName_"."_..DocExtension
;set the filepath on the production settings to this variable
set origDirectory = ..Adapter.FilePath
;set the new filepath
set ..Adapter.FilePath = ..Adapter.FilePath_"\"_..DocumentDirectory
// set:$$$ISOK(status) status= ..Adapter.PutStream(..Filename, pInput.Stream) //error here to file
set:$$$ISOK(status) status=..OriginalFileOut(pInput,pResponse,..Filename)
;set adapter to its original file path
set ..Adapter.FilePath = origDirectory
;check writing out file worked
if ($$$ISOK(status))
{
set pResponse=##class(DocumentUpload.GenericRESP).%New()
set pResponse.Process="FileOPRN_files Written to their respective directories"
set pResponse.Status=status
set status=pResponse.%Save()
}
}
;return status
return status
}// passthrough original method to write out file
Method OriginalFileOut(pRequest As Ens.StreamContainer, Output pResponse As %Persistent, filenamess) As %Status
{
Quit:'$IsObject(pRequest.Stream) $$$ERROR($$$EnsErrGeneral,"No Stream contained in StreamContainer Request")
Set tFilename=..Adapter.CreateTimestamp(##class(%File).GetFilename(pRequest.OriginalFilename),filenamess)
Set tSC=..Adapter.PutStream(tFilename, pRequest.Stream)
Do pRequest.%Save() ; re-save in case PutStream() optimization changed the Stream filename
Quit tSC
}@Eduard Lebedyuk I have tried changing the file to a txt file and the file is being created with nothing in it
@Eduard Lebedyuk its not readable I cannot open it
thanks the problem was the pdf is more like an image file so using a container to pass the stream around is a good idea only if the message leaves the service to the operation but in my case I had a process which was going to forward that message to another process so the first process had all the contents of a stream but when it forwards it to the next process the contents are empty so in my case I changed the stream object to a %Streambinary and I am not using the container anymore everything is working fine.
I might be connecting to a wrong server here is how I am doing my configuration for atelier:
@Robert Cemper Is it possible to have an example as I do not know where to start so far using the %Net.HttpRequest I have been able to navigate to the Log in page and would like to locate the user inputs and insert some credentials
set oReq = ##class(%Net.HttpRequest).%New()
set oReq.Server =..Server
set oReq.Location =..location
set oReq.SSLConfiguration =..SSlConfig
set oReq.Https=1
@Robert Cemper thank you for the guidance
$LENGTH(pRequest.GetValueAt("PID:3") please note this does not give me list length rather it take the PID as a string and give me the length of the string I need to able to extract the individual values in the segment that's the length I need
@Anastasia Dyubaylo why are you editing all my questions
Thanks this is what I am trying to do
Method StringToDate(pd As %String) As %Date
{
#dim theDate as %Date
SET theDate =$zdateh(pd,4)
w $ZDate(theDate,3)
QUIT
}@sjbttt sjbttt did you final get the solution to this if you did would you kindly share the solution please
@Jon Willeke I mean the above characters I would like to have them on an xml in their ASCII format(HTML Format)eg bullet point in •
@Eduard Lebedyuk No it is not a known node what I am trying to do is pull those characters and pass them to $TRANSLATE($SYSTEM.Encryption.Base64Encode(streamString),$C(10,13))' as part of a document I need to convert to a pdf but have tried all the encodings I could use from utf -8 to the windows 1252 and I get an error like so
ERROR <Ens>ErrException: <ILLEGAL VALUE>zEncodeStream+18 @' set encString = $TRANSLATE($SYSTEM.Encryption.Base64Encode(streamString),$C(10,13))' any ways to get around the base 64 encoding@Eduard Lebedyuk all ready tried that but it refreshes the whole page
After some investigation on the above problem I realised that when purging record map data the data integrity check is the problem as I am using Ensemble 2014.1 the relationships on those is a one to many so when deleting these objects there is an %ondelete method which invokes a delete record method which tries to delete a record and then delete the batch but it does not check if that batch still have records associated with it. Now a new problem arises where I have tried to delete individuals records in a batch successfully but when trying to delete the batch its self it errors with a 5002 ROLLback error now the question is how to get around this problem
@Attila Toth I am using your FOWrapper to pass a stream of XLFO data in my production to generate a pdf and nothing is happening I am not getting any errors or anything would you please advice on where I am going wrong I have altered it a bit to use what I need please advice here is my code thanks in advance
Include Ensemble
IncludeGenerator (%occInclude, EnsUtil)
Class ZEN.Report.XLFOToPDFWrapper Extends %ZEN.Report.reportPage
{
Parameter DEFAULTMODE As STRING = "xml";
/// Default value for the CallbackClass property. This can be set in subclasses!
Parameter DEFAULTCALLBACKCLASS;
/// Default value for the CallbackMethod property. This can be set in subclasses!
Parameter DEFAULTCALLBACKMETHOD;
Parameter ENCODING = "Windows-1252";
Property FOStreamString As %Stream.FileCharacter(ZENURL = "FOSTREAMSTRING");
/// If this and the CallbackMethod are not empty and referring a valid class and method name,
/// then the return value of the corresponding classmethod (which should be a character stream) is used as the XSL-FO file.
Property CallbackClass As %String(ZENURL = "CLASS") [ InitialExpression = {..#DEFAULTCALLBACKCLASS} ];
/// If this and the CallbackClass are not empty and referring a valid class and method name,
/// then the return value of the corresponding classmethod (which should be a character stream) is used as the XSL-FO file.
Property CallbackMethod As %String(ZENURL = "METHOD") [ InitialExpression = {..#DEFAULTCALLBACKMETHOD} ];
/// This callback is invoked after this report is instantiated
/// and before it is run.
Method %OnBeforeReport() As %Status [ Internal ]
{
Set tSC = $$$OK
If (..CallbackClass '= "") && (..CallbackMethod '= "") {
TRY {
Set tSC = $CLASSMETHOD(..CallbackClass, ..CallbackMethod, .fostream)
}
CATCH ex {
Set tSC = ex.AsStatus()
}
}
Else {
Set fostream = ##class(%Stream.FileCharacter).%New()
While '..FOStreamString.AtEnd {
Set tSC = fostream.Write(..FOStreamString.Read(32000))
}
$$$TRACE(fostream)
$$$LOGWARNING(fostream)
}
If $$$ISOK(tSC) {
// TODO: automatically append XSLT frame around the XSL-FO file.
Set ..toxslfostream = ..TransformFO2XSL(.fostream)
Do ..toxslfostream.Rewind()
}
Quit tSC
}
/// This report has a "fake" XML definition. Basically any XML would do, because the XSL-FO file is created outside of the ZEN Report class.
XData ReportDefinition [ XMLNamespace = "http://www.intersystems.com/zen/report/definition" ]
{
}
/// Method, which generates the "fake" XML content of the report.
/// At the moment this contains a single element (), which reflects the parameters of the report.'
ClassMethod FakeXML() [ Internal ]
{
Write ""
}
ClassMethod ProducePDFFile(pOutputFile As %String, pXLFOStream As %String, pDisplayLog As %Boolean = 0, pRenderServer As %String = "") As %Status
{
Set tSC = $$$OK
Set report = ..%New()
Set report.FOStreamString = pXLFOStream
Set tSC = report.GenerateReport(pOutputFile, 2, pDisplayLog, pRenderServer)
Quit tSC
}
/// Appends the XSLT frame around the original XSL-FO stream, to make it usable with this report class.
ClassMethod TransformFO2XSL(ByRef pFOStream As %Stream.Object) As %Stream.Object [ Internal ]
{
Set tSC = $$$OK
Set xslStream = ##class(%Stream.TmpCharacter).%New()
Do pFOStream.Rewind()
Set first100 = pFOStream.Read(100)
If $Extract(first100, 1, 2) = "", 1) _ ">"
Set first100 = $Piece(first100, ">", 2, *)
}
Else {
Set xmlHeader = ""
}
Do xslStream.WriteLine($Select(
xmlHeader '= "": xmlHeader,
1: ""))
Do xslStream.WriteLine("")
Do xslStream.WriteLine("")
Do xslStream.Write(first100)
While 'pFOStream.AtEnd {
Do xslStream.Write(pFOStream.Read(32000))
}
Do xslStream.WriteLine("")
Do xslStream.WriteLine(" ")
Quit xslStream
}
} and on my operation I call it like so
Class FopPDFRenderingEngineOPRN Extends Ens.BusinessOperation
{
Parameter ADAPTER = "EnsLib.File.OutboundAdapter";
Property Adapter As EnsLib.File.OutboundAdapter;
Parameter INVOCATION = "Queue";
Method Render(pRequest As RenderRequestMREQ, Output pResponse As RenderRequestMRES) As %Status
{
set tSC = $$$OK
set pResponse = ##class(Ecrion.PublishingEngine.RenderRequestMRES).%New()
set pResponse.FileName = pRequest.FileName
set pResponse.Status = 0
#Dim oHttpReq3 AS ZEN.Report.XLFOToPDFWrapper= ##class(ZEN.Report.XLFOToPDFWrapper).%New()
// if the input format is xslfo Base64Encode the stream
if (pRequest.InputFormat = "xslfo")
{
// write the request object
set pResponse.Document = ..getStreamData(pRequest.Source)
set p=oHttpReq3.ProducePDFFile(..Adapter.FilePath_pRequest.FileName,..getStreamData(pRequest.Source))
If ($$$ISOK(p))
{
set pResponse.Message="all good"
}else
{
set pResponse.Message=$$$ISERR(tSC)
}
kill oStream
}
kill oStream
quit tSC
}
Method getStreamData(foo As %Stream.TmpCharacter) As %Stream.TmpCharacter
{
set oStream = ##class(%Stream.TmpCharacter).%New()
while ('foo.AtEnd) { do oStream.Write(foo.Read(1200)) }
do oStream.Rewind()
quit oStream
}
XData MessageMap
{
Render
}
}@Attila Toth thanks for getting back at me I have tried with log errors and I get a pdf generated which I can not open as if I have a loop running or the file being corrupt your solution works fine with the reading of files and using the call back method with embedded XLFO is it possible to supply the call back method with the XLFO stream and use that instead and if possible please could you give us an example if you have one thanks
I am using the supplied FakeXML as below
ClassMethod FakeXML() [ Internal ]
{
Write "<fofile" _
$Case(%report.FOFilename, "": "", : " name=""" _ ##class(%File).NormalizeFilename(%report.FOFilename) _ """") _
$Case(%report.FOStreamString, "": "", : " FOStreamString=""" _ %report.FOStreamString _ """") _
$Case(%report.CallbackClass, "": "", : " callbackClass=""" _ %report.CallbackClass _ """") _
$Case(%report.CallbackMethod, "": "", : " callbackMethod=""" _ %report.CallbackMethod _ """") _
"></fofile>"
}
@Attila Toth thank you but I managed to crack it and I have sent you the code to update if you like
@Warlin Garcia Thank you just what I needed $LISTGET(CList,i)
@Robert C.Cemper C.Cemper
I am using 2014.1 version of Ensemble
@Hansel Rudy Stange Gaete
Was wondering did you get this problem solved I also have an object with a stream property that I am trying to return in a soap response and is blank but in the trace on the production is populated any help appreciated
@Hansel Rudy Stange Gaete
I am trying to send a pdf document encoded to base64 which means the size could vary depending on the document
@Tomas Vaverka
Yes that right but I know that the question may be was not clear I have an object class which has two properties one a string and a stream and was trying to pass that object ,all I could do was pass a string and an empty stream but I have read around the stream and found out that I can pass a stream as a stream but not inside an object thanks again
@Suman Samanta
Please have a look at this question I posted earlier if you still need more code let me know here