Message details and AuditLog from Java Stack using Custom WebService
Overview
Recently I started to wonder how to get message content and audit logs for message after processing in Adater Engine. If Message comes to PI, usually is processed in Abap stuck and there everything seems to be rather clear. We have some function modules like
SXMB_GET_MESSAGE_DATA
Or we can check main tables like
SXMSPMAST XI Messages – Master
SXMSPVERS Integration Engine: Message Version
SXMSCLUP Cluster – Compressed Message Payload Property
As a last resort we can debug sxi_monitor transaction to receive message data, current status or payload. But how can we get those values from Java stuck. Of course we can read table data
XI_AF_MSG AF XI Message
XI_AF_MSG_AUDIT Audit Log Entries
But it is not the solution and sometimes data aren’t kept in human readable format like payload is kept in blob format.
In PI 7.1 we have standard WebService like described in Michael’s blog. Disadvantage of this solution is that we do not receive Audit Log for processed message and this WebService in not available for PI 7.0.
Purpose
Let suppose I would like to build some external tool to trace selected message’s flow. I will receive information about errors in Abap stuck through CCMS and in Java stuck with Alert mechanism. But it’s not enough for me. I would like to have full information about message and Audit Log in java based on message id.
Development
First approach – Java DB tables
Read Java tables – not acceptable
Second approach – Jar files
So I started to check what kind of possibilities I have. First of all I downloaded some jar files from PI server and checked what kind of class and methods they offer.
To receive information about Message I discovered in aii_af_ms_impl.jar file, class MessageStore and method getMemoryMessageBeans(MessageBeanFilter filter) where MessageBeanFilter is filter similar to this with we set in http://<server>:<port>/mdt/index.jsp .
In aii_af_svc.jar file I have class AuditLogManger. So at the beginning I started to use method getAuditEntryList(AuditMessageKey msgKey) but I receive error message. After struggling with AuditLogManager class I receive in my WebService correct result and the problem was with Connection class that provide access to Java Database. But this method seems to be very raw and unreliable but at least it was working. I started to search more and discovered some very nice Enterprise Java Beans offered by our J2EE server with is SAP Netweaver.
Third approach – Enterprise JavaBeans
In NWA i have list of all available modules and assigned to it Enterprise JavaBeans. I’m interested in module aii_af_ms_app_ejb.
Inside this module i will find EJB with I’m particularly interested in and there name is messaging.system.MonitorBean and messaging.system.AuditLogBean
Let’s now focus on MonitorBean with I will use to fetch message detail from PI. As each bean this one has JNDI used for search it in lookup and interface MonitorHI that allow WebService to obtain reference to specific bean.
EJB functionality is accessed by means of remote interface, which define the business methods visible to, and called by WebService. MonitorRI interface contains lot of very use full methods and is implemented by MonitorBeanImpl. Some use full methods:
MessageBean getMessageBean(MessageKey paramMessageKey, int paramInt) – return message details for message key ( consists of message id and message direction)
byte[] getMessageBytes(MessageKey paramMessageKey, int paramInt) – return whole XI Message with payload.
MonitorBean getMonitorBean(MessageBeanFilter paramMessageBeanFilter) – return all messages that fulfil requirements set in MessageBeanFilter, that contains additional criteria for filter like interface name, sender service etc.
Similar for AuditLogBean I will use interface AuditLogBI and method
ArrayList getLogEntries(AuditMessageKey paramAuditMessageKey, int paramInt)
-return all AuditLogEntry for selected auditMessageKey( consist of message id and message key)
After download jar files that contains those class from J2EE server i can start building WebService. There are plenty of tutorials on SDN how to build java WebService from EJB.
Code
In my bean i had to add some references etc.
Method code to receive Message details
MonitorHI _mhi;
MonitorBI _monb;
try
{
InitialContext ctx = new InitialContext();
Object obj = ctx.lookup(“messaging.system.MonitorBean”);
if( obj != null)
{
_mhi = (MonitorHI) obj;
_monb= (MonitorBI) _mhi.create();
}
}
catch(Exception ex)
{
}
Home interface MonitorHI can be used to execute method create() and receive Remote interface MonitorRI. This interface type extends MonitorBI interface so we can cast it to MonitorBI or use MonitorRI
Next step it to call requested method like
MessageKey _mk = null;
MessageDirection _mess_dir = null;
_mess_dir = MessageDirection.INBOUND;
_mk = new MessageKey(“YOUR MESSAGE ID”,_mess_dir);
MessageBean _result = null;
try
{
_result = _monb.getMessageBean(_mk,i_cluster);
}
catch(Exception el)
{
}
Where _mk is MessageKey and i_cluster is int variable that contain node/cluster id and can be omitted with value 0.
Similar approach can be used to receive array of AuditLogEntry
Test
Log on to Wsnavigator and test WebServices
GetMessage
GetAuditLogs
Could you include the references that you had to had in your project. This will help a lot anybody that wants to use the MonitorBean
Thanks
Wonderful! Great! I will use it immediately!
Seems really useful, I just need something like this.
Could you enclose whole source of java classes, please?
I've failed with getting APIAccess class 🙁 But, at your possible UDF or Http Servlet you can bind to JNDI
com.sap.engine.interfaces.messaging.api.public
and you'll get public interface PublicAPIAccess.I've managed with getting access to APIAccess from my own servlet so now try to write UDF with same idea.
Just a note for success story 7.31. Two ways (at least) of using that code are possible.
First way: UDF. It's quite easy:
1. Locate com.sap.aii.af.ms.ifc_api.jar and load it as imported archive IA_IFC.
2. Create FL or MM
3. Make dependencies as follow (beside default):
a. To imported archive IA_IFC
b. import javax.naming.*;
import com.sap.engine.interfaces.messaging.api.*;
4. Append simple function ApiAccessTest execution type SingleValues with the only parameter msgid
5. The proof-of-concept code is follow:
Object o;
InitialContext ctx;
APIAccess a;
AbstractTrace tr = container.getTrace();
try {
ctx = new InitialContext();
o = ctx.lookup("com.sap.engine.interfaces.messaging.api");
if (o==null) throw new StreamTransformationException("APIAccess isn't got");
a = (APIAccess)o;
tr.addInfo("APIAccess got well: " + a);
} catch (NamingException e) {
throw new StreamTransformationException("Can't detect JNDI. Error: " + e.getExplanation());
}
return "" + msgid + ";test";
6. Check compilation and test mapping result. It's the base for follow steps. Good luck!
I have problem in "a = (APIAccess)o;" row.
If I put to row before this code "trace.addInfo(obj.toString());" I received in trace this message: com.sap.engine.messaging.impl.api.APIAccessImpl@6255b154
and than is exception raised:
Exception:[java.lang.ClassCastException: class com.sap.engine.messaging.impl.api.APIAccessImpl:service:com.sap.aii.af.ms.svc@com.sap.engine.boot.loader.ResourceMultiParentClassLoader@2e3ed883@alive incompatible with interface com.sap.engine.interfaces.messaging.api.APIAccess:com.sap.aii.ibrep.server.mapping.rt.ArchiveClassLoader@15a8e5c4]
Seems that I'm receiving little bit different type of object than I expect, right?
I'm running and testing it in Builder, not running whole message including transformation.
And I still don't understand how can I find out old message through MessageID and check it's status (successfully processed, failed in transformation, in channel etc.) from another transaction.
Maybe I put here wrong comment without question...
Code, mapping objects and description is put on pinternals_tuttifrutti/mapping_APIAccess at master · chumpa/pinternals_tuttifrutti · GitHub
JDBC_http://sap.com/xi/XI/System
<local>
null
null
6533
XXX|XXXXX_D
content-type=multipart/related; boundary=SAP_1c92b0e8-4567-11e4-afd4-000007a372ba_END; type="text/xml"; start="<soap-1c92b0e7456711e49591000007a372ba@sap.com>"
http=POST
content-length=6533
S 2014-09-26 14:23:09 JDBC_SUC_004 CC_BankPair_JDBC_Send,null,null,null,null,
S 2014-09-26 14:23:09 JDBC_SUC_005 CC_BankPair_JDBC_Send,1,null,null,null,
S 2014-09-26 14:23:09 JDBC_SUC_009 3478,null,null,null,null,
S 2014-09-26 14:23:09 MP: processing local module {0} localejbs/CallSapAdapter,null,null,null,null,
S 2014-09-26 14:23:09 SEND_TRYING JDBC_http://sap.com/xi/XI/System,XI,null,null,null,
S 2014-09-26 14:23:09 SEND_QUEUE_PUT_TRYING null,null,null,null,null,
S 2014-09-26 14:23:09 QUEUE_PUT_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 SEND_SUCCESS JDBC_http://sap.com/xi/XI/System,null,null,null,null,
S 2014-09-26 14:23:09 SEND_QUEUE_GET_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 STATUS_SET_SUCCESS DLNG,null,null,null,null,
S 2014-09-26 14:23:09 SEND_QUEUE_PUT_TRYING null,null,null,null,null,
S 2014-09-26 14:23:09 QUEUE_PUT_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 SEND_QUEUE_GET_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 STATUS_SET_SUCCESS DLNG,null,null,null,null,
S 2014-09-26 14:23:09 SEND_QUEUE_PUT_TRYING null,null,null,null,null,
S 2014-09-26 14:23:09 QUEUE_PUT_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 SEND_QUEUE_GET_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 STATUS_SET_SUCCESS DLNG,null,null,null,null,
S 2014-09-26 14:23:09 AAE_REQUEST_MAPPING urn:mapping:001BankImport:SVNSI,BankPairNotification__BankProxyNotification,cbf5bad0b0be11e29710cb3b0a0000fe,null,null,
S 2014-09-26 14:23:09 AFW_DELIVER CC_JPR_Recv,null,null,null,null,
S 2014-09-26 14:23:09 MP: processing local module {0} localejbs/sap.com/com.sap.aii.af.soapadapter/XISOAPAdapterBean,null,null,null,null,
S 2014-09-26 14:23:09 XI packaging (bulk mode) is not enabled. Switching to normal processing.... null,null,null,null,null,
S 2014-09-26 14:23:09 XISOAP: XI message received for processing null,null,null,null,null,
S 2014-09-26 14:23:09 SOAP: Request message entering the adapter processing with user j2ee_gst_jxd null,null,null,null,null,
S 2014-09-26 14:23:09 REQUEST XI,http://localhost:51200/MessagingSystem/receive/JPR/XI,User,PIAPPLJXD2,null,
S 2014-09-26 14:23:09 RECEIVE_QUEUE_PUT_TRYING JPR,null,null,null,null,
S 2014-09-26 14:23:09 QUEUE_PUT_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 RECEIVE_QUEUE_GET_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:09 SOAP: Processing completed null,null,null,null,null,
S 2014-09-26 14:23:09 STATUS_SET_SUCCESS DLNG,null,null,null,null,
S 2014-09-26 14:23:09 SEND_TRANSMIT_SUCCESS JDBC_http://sap.com/xi/XI/System,<local>,null,null,null,
S 2014-09-26 14:23:09 JPR_IN_ACCEPTED null,null,null,null,null,
S 2014-09-26 14:23:09 STATUS_SET_SUCCESS DLVD,null,null,null,null,
S 2014-09-26 14:23:09 Calling Invoke Method null,null,null,null,null,
S 2014-09-26 14:23:11 Back after executing Invoke Method null,null,null,null,null,
S 2014-09-26 14:23:11 JPR_IN_PROCESS_SUCCESS null,null,null,null,null,
S 2014-09-26 14:23:11 RECEIVE_DELIVER_SUCCESS JPR,null,null,null,null,
S 2014-09-26 14:23:11 STATUS_SET_SUCCESS DLVD,null,null,null,null,
Everything works fine in PI 7.3, but not in 7.11.
Maybe it's useful for many people who use 7.3 version, here is the result:
1)
pick-up library from PI system:
/usr/sap/XYZ/DVEBMGS00/j2ee/cluster/bin/interfaces/com.sap.aii.af.ms.ifc/lib/com.sap.aii.af.ms.ifc_api.jar
2)
import it to PI as Imported Object
3)
create new UDF
3a)
link imported jar at "Archives Used" tab
3b)
import all needed classes in "Import Instructions" tab
3c)
modify parameters and source of function
Object obj;
String out = null;
InitialContext initContext;
APIAccess apiAccess = null;
AbstractTrace trace = container.getTrace();
try {
initContext = new InitialContext();
obj = initContext.lookup("com.sap.engine.interfaces.messaging.api");
apiAccess = (APIAccess) obj;
} catch (NamingException e) {
trace.addWarning("Can't detect JNDI. Error: " + e.getExplanation());
}
MessageAccess msgAccess = apiAccess.getMessageAccess();
MessageDirection msgDirection = MessageDirection.valueOf(aDirection);
MessageKey msgKey = new MessageKey(aMessageID, msgDirection);
MessageData msgData = null;
try {
msgData = msgAccess.getMessageData(msgKey);
out = msgData.getStatus().toString();
} catch (MessageAccessException e) {
trace.addWarning("Can't get information. Error: " + e.getMessage());
}
// DLNG - messages that have been delivered
// TBDL - no error, Messages to be delivered
// HOLD - Held messages
// NDLV - Messages with system errors
// WAIT - Processing Backlog with Errors, Waiting messages
return out;
4)
implement it in your message mapping and try it
5)
enjoy result...
Unfortunately it's not working me in 7.11 version 🙁
There is exception like this:
Exception:[java.lang.ClassCastException: class com.sap.engine.messaging.impl.api.APIAccessImpl:service:com.sap.aii.af.ms.svc@com.sap.engine.boot.loader.ResourceMultiParentClassLoader@79e70d8f@alive incompatible with interface com.sap.engine.interfaces.messaging.api.APIAccess:com.sap.aii.ibrep.server.mapping.rt.ArchiveClassLoader@f953ed2] in class com.crossgate.Basf.eBilling.FL_BASF method getMessageStatus[0050569b-007d-1ed4-90de-d01cb536728c, INBOUND, com.sap.aii.mappingtool.tf7.rt.Context@614f4d43]
Problem is in this part: apiAccess = (APIAccess) obj;
Maybe there is classloader property to get what JAR file contains some .class.toString()? The appropriate interface is 100% at your system.
Yes, there is an API for that:
ctx = new InitialContext();
o = ctx.lookup("com.sap.engine.interfaces.messaging.api");
if (o==null) throw new StreamTransformationException("APIAccess isn't got");
s = o.getClass().getProtectionDomain().getCodeSource().getLocation().toString();
tr.addInfo("See file " + s);
At trace will be:See file file:/sapdata/usr_sap/SID/J00/j2ee/cluster/bin/services/com.sap.aii.af.ms.svc/lib/com.sap.aii.af.ms.svc_api.jar
(for 7.31)
It's different JAR, I used interface and not service. I try to rebuild code and update this thread soon...
Exception looks different but still here it is:
Exception:[java.lang.ClassCastException: class com.sap.engine.messaging.impl.api.APIAccessImpl:service:com.sap.aii.af.ms.svc@com.sap.engine.boot.loader.ResourceMultiParentClassLoader@2e3ed883@alive incompatible with class com.sap.engine.messaging.impl.api.APIAccessImpl:com.sap.aii.ibrep.server.mapping.rt.ArchiveClassLoader@727af65a]
I'm using right instance of APIAccessImpl class, but from different namespace, while running program is used Archive namespace.