lunes, 30 de junio de 2014

IBM Integration Bus (Message Broker) resources

Steps to configure an Oracle ODBC data source on Windows: "Connecting to a database from Windows systems". Includes necessary steps like "Select Enable SQLDescribeParam", "Select Procedure Returns Results", "Select Login Timeout and set the value to 0" and the WorkArounds registry string with 536870912 value, which you may forget, causing you unnecesary pain and suffering. Personally, I was struggling with the '[IBM][ODBC Oracle Wire Protocol driver]Data type for parameter x has changed since first SQLExecute call.' error (which always ocurred the second time I executed an specific flow, and that was caused by the lack of the registry key) and the very generic "[Microsoft][ODBC Driver Manager] Driver does not support this function" error (forum references: first SQLExecute call Error and ODBC Oracle Wire Protocol driver Error).

SQLSTATE explanation: ESQL: What is the SQLSTATE?

Explanation about the difference between a REFERENCE to OutputRoot and the real OutputRoot: "Message Headers not getting copied to output" 3rd post (useful when you use "IN REFERENCE OutputRoot" in functions or procedures)

Explanation of the logic behind "All calls to CURRENT_TIMESTAMP within the processing of one node are guaranteed to return the same value":  "a node is an interruption to the execution path of a message. At the time the node is called, the thread is suspended and a call to get current timestamp retrieves the time value stored in the environment at the time the execution path was interrupted. If you want to measure time between certain points within your ESQL logic of the same node, you can do so by calling Date.getTime() Java api call from ESQL" (source: Very strange issue with CURRENT_TIMESTAMPlancelotlinc user post).

List of Websphere Message Broker resources that changed its name in IBM Integration Bus: Do you want to know the new name of a WebSphere Message Broker resource that has changed its name in IBM Integration Bus v9?

Implicit casting rules from database data types to ESQL data types: Data types of values from external databases

Standard output (stdout) (Windows 7): C:\ProgramData\IBM\MQSI\components\[BROKER_NAME]\[ID]\console.txt

ESQL-to-Java data-type mapping table.

Log4j troubleshooting: keep an eye on the stdout file (console.txt, see above); when you call the initLogger method (com.ibm.broker.IAM3.Log4jNode.initLog4j) you should see a line like the following in that file:

2015-03-05 21:56:31.384     17 log4j:WARN Config file used: /C:/Lib/iib-log4j/conf/brokerlog.xml

You should also find there information about problems finding or parsing that file. If you have checked everything and still can't see logs entries in the log file, try using a tool like Process Explorer (for Windows; it shows you all file access events for all running process) and filter for events which "Path" contains some pattern your log file name has or just filter for events for "DataFlowEngine.exe" process name; look for example what I found in my case:


I was not aware that I was specifying the "File" param value for my file appender using backslashes (C:\Logs\xxx.log) which resulted in this file for IIB: C:\Program Files\IBM\MQSI\9.0.0.1\bin\Logsxxx.log. Once I changed the "File" param value for the windows path using slashes (C:/Logs/xxx.log) I could see events in my log file.

Dynamic Database Name and Multiple Database Support: IBM presentationAccessing databases from ESQL, "ESQL Enhancements Part 1" presentation (other link). The last link contains the syntax trees for UNCOORDINATED database references and DSN specific COMMITs and ROLLBACKs; I haven't found references and/or examples of that syntax in other places; the "The transactional model" section of the official documentation says you can use UNCOORDINATEDCOMMITs, ROLLBACKs in "Database auxiliary transactions", but I don't see this reflected in the syntax threes in that same documentation (IBM, why are you like this? :( ).

How to change the WebSphere MQ Explorer (from IIB 9)  language to english: add -Duser.language=en to C:\Program Files (x86)\IBM\WebSphere MQ Explorer\MQExplorer.ini (source).

ODBC SQLSTATE codes:
Example procedure to navigate a message tree (including the Exception List): Navigating a message tree (including the Exception List).

IBM Integration Bus (IIB) MQ retry and requeue tests.

IBM Integracion Bus (IIB) COMMIT, ROLLBACK and UNCOORDINATED tests.

DeverloperWorks' Top 12 IBM Integration Bus articles.


lunes, 9 de junio de 2014

Cambiar el idioma de IBM Integration Toolkit 9 a inglés

Por defecto el idioma de la UI de IBM Integration Toolkit 9 (de IBM Integration Bus) UI está determinado por la local actual. Mi OS está instalado en Español, pero prefiero usar las herramientas de desarrollo en inglés (mensajes de error, tutoriales, etc.)

Para forzar IBM Integration Toolkit 9 a usar inglés, añadir la siguiente línea

-nl en_US

al archivo INSTALLATION_PATH\mb.ini (por ejemplo C:\Program Files\IBM\IntegrationToolkit90\mb.ini)



Change IBM Integration Toolkit 9 language to english

By default, IBM Integration Toolkit 9 (from IBM Integration Bus) UI language is determined by the computer locale. My OS is installed and configured to use Spanish, but I prefer to use the development tools in English (error messages, tutorials, etc.)

To force IBM Integration Toolkit 9 to use English, add the following line

-nl en_US

to INSTALLATION_PATH\mb.ini (for example, C:\Program Files\IBM\IntegrationToolkit90\mb.ini)


viernes, 7 de marzo de 2014

Notes about Oracle AQ default exception queues


  • All AQ Queues have a default associated exception queue where messages that "may not be consumed within given constraints" are moved.
  • By default the exception queue name is called AQ$_[ORIGINAL_QUEUE_NAME]_E. For example, if the original normal queue is called MYQUEUE, its default exception queue is called AQ$_MYQUEUE_E. I notice that for example, in SQL Developer, I can't see the exception queue in the "Queues" tree (but it "exists"). Also by default, the default exception queues are not enabled for dequeuing. Yoy can use this instruction to enable dequeuing: dbms_aqadm.start_queue(queue_name=>'MYQUEUE', enqueue=>FALSE,dequeue=>TRUE). You can't enable an exception queue for enqueuing. Here is additional information about the exception queues.
  • By default, both queues (the normal queue and the exception queue), are supported by the same queue table. You can check it by directly querying the queue table (MYQUEUETABLE) and looking the Q_NAME column or by querying the queue table AQ view (AQ$MYQUEUETABLE) and looking the QUEUE column (this is the prefered way). The value for the "expired" messages is AQ$_MYQUEUE_E and the value for the normal messages is MYQUEUE.
  • You can use an "user-defined exception queue", but it is set through a message header; that is, at message level and not at queue level. At the message enqueue time, you can set the exception queue for that message through a header (JMS_OracleExcpQ); if that message expires, it will be moved to that user-defined exception queue. Here, you can check some good instructions to use it in PL/SQL or Java JMS.
Here are some references in My Oracle Support:

Using JMS message selectors in a message consumer using an Oracle AQ (exception) queue

I was testing JMS selectors in a Message Consumer based on an Oracle AQ (exception) queue; I needed to dequeue only the messages with certain message ID's; at first, I tried with this syntax, but found an unexpected behavior:

"JMSMessageID='ID:XXX' OR JMSMessageID='ID:YYY'"

For example, let's say we have this messages in the queue (this specific messages are in the queue's default exception queue):


If I use the following selector,

String selector = 
  "JMSMessageID='ID:4A3966F3C0604B6DAED6D6E7FC62705A' OR 
   JMSMessageID='ID:01AF9624372C4D8A88CFAFD37D4395E7' OR
   JMSMessageID='ID:42C32FA9BBCB48ABA074EB69C94D1A82' OR
   JMSMessageID='ID:4973147F8AC54BD1A5DA2CE81F9622CD'";

the first receive() works OK, returning one of the matching messages (consistently, the first in the selector):

[TestServlet][doGet]selector=JMSMessageID='ID:4A3966F3C0604B6DAED6D6E7FC62705A' OR JMSMessageID='ID:01AF9624372C4D8A88CFAFD37D4395E7' OR JMSMessageID='ID:42C32FA9BBCB48ABA074EB69C94D1A82' OR JMSMessageID='ID:4973147F8AC54BD1A5DA2CE81F9622CD
[TestServlet][doGet]messageID=ID:4A3966F3C0604B6DAED6D6E7FC62705A

But, from the second, and so on, the receive() method behaves as if there aren't messages matching the selector. In the end, just one message got processed:


I enabled the jdbc and jms debug on the managed server (I'm working on Weblogic 12c), and found this, starting from the second receive(): "java.sql.SQLException: ORA-25263: no message in queue PRUEBAS.AQ$_MYQUEUETABLE_E with message ID 4A3966F3C0604B6DAED6D6E7FC62705A".

[[oracle.jdbc.driver.T4CXAConnection@29cccbf, owner=[weblogic.jdbc.wrapper.JTAConnection_weblogic_jdbc_wrapper_XAConnection_oracle_jdbc_driver_LogicalConnection-myQueuePruebas_DS-17, oracle.jdbc.driver.LogicalConnection@29d1486], rmConn=oracle.jdbc.driver.LogicalConnection@29d1486]] dequeue(unknown) throws: java.sql.SQLException: ORA-25263: no message in queue PRUEBAS.AQ$_MYQUEUETABLE_E with message ID 4A3966F3C0604B6DAED6D6E7FC62705A
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:389)
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:382)
        at oracle.jdbc.driver.T4Caqdq.processError(T4Caqdq.java:488)
        at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:450)
        at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
        at oracle.jdbc.driver.T4Caqdq.doOAQDQ(T4Caqdq.java:180)
        at oracle.jdbc.driver.T4CConnection.doDequeue(T4CConnection.java:3976)
        at oracle.jdbc.driver.PhysicalConnection.dequeue(PhysicalConnection.java:10106)
        at oracle.jdbc.driver.PhysicalConnection.dequeue(PhysicalConnection.java:10153)
        at oracle.jdbc.OracleConnectionWrapper.dequeue(OracleConnectionWrapper.java:1525)
        at weblogic.jdbc.wrapper.XAConnection_oracle_jdbc_driver_LogicalConnection.dequeue(Unknown Source)
        at weblogic.jdbc.wrapper.JTAConnection_weblogic_jdbc_wrapper_XAConnection_oracle_jdbc_driver_LogicalConnection.dequeue(Unknown Source)
        at oracle.jms.AQjmsConsumer.jdbcConnectionDequeueCheck25242(AQjmsConsumer.java:1713)
        at oracle.jms.AQjmsConsumer.jdbcDequeue(AQjmsConsumer.java:1295)
        at oracle.jms.AQjmsConsumer.receiveFromAQ(AQjmsConsumer.java:1035)
        at oracle.jms.AQjmsConsumer.receiveFromAQ(AQjmsConsumer.java:960)
        at oracle.jms.AQjmsConsumer.receiveFromAQ(AQjmsConsumer.java:938)
        at oracle.jms.AQjmsConsumer.receive(AQjmsConsumer.java:790)
        at test.PruebaConsumerSelectorServlet.doGet(PruebaConsumerSelectorServlet.java:55)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:844)
        at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:242)
        at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:216)
        at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:132)
        at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:338)
        at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:221)
        at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3284)
        at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3254)
        at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
        at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
        at weblogic.servlet.provider.WlsSubjectHandle.run(WlsSubjectHandle.java:57)
        at weblogic.servlet.internal.WebAppServletContext.doSecuredExecute(WebAppServletContext.java:2163)
        at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2089)
        at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2074)
        at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1513)
        at weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor.run(ContainerSupportProviderImpl.java:254)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)

Am I doing somethig wrong? is this a bug? my test code is pretty straighfoward; something like this:

String selector = 
  "JMSMessageID='ID:4A3966F3C0604B6DAED6D6E7FC62705A' OR 
   JMSMessageID='ID:01AF9624372C4D8A88CFAFD37D4395E7' OR
   JMSMessageID='ID:42C32FA9BBCB48ABA074EB69C94D1A82' OR
   JMSMessageID='ID:4973147F8AC54BD1A5DA2CE81F9622CD'";
messageConsumer = session.createConsumer(queue, selector);
Message msg = null;
System.out.println("[TestServlet][doGet]selector=" + messageConsumer.getMessageSelector());
while((msg = messageConsumer.receive(10000)) != null) {          
if(msg != null) {
System.out.println("[TestServlet][doGet]messageID=" + msg.getJMSMessageID());
}
}

Fortunately, trying with other selector syntax, the test worked OK. The syntax is:

"JMSMessageID IN ('ID:XXX', 'ID:YYY')"

For example, having this messages in the queue


And this selector

String selector = 
  "JMSMessageID IN (
    'ID:ED976B4A41F241F594F570A8F2250D26'
'ID:724794DE2C4648AF9EF7EB1CE3B0BC22'
'ID:33CF68A695E04F6B89F1024F7DFE5925'
'ID:3E7059E3489C472096C58A833434893C')"

The test works as expected:

[TestServlet][doGet]messageID=ID:724794DE2C4648AF9EF7EB1CE3B0BC22
[TestServlet][doGet]messageID=ID:3E7059E3489C472096C58A833434893C
[TestServlet][doGet]messageID=ID:33CF68A695E04F6B89F1024F7DFE5925
[TestServlet][doGet]messageID=ID:ED976B4A41F241F594F570A8F2250D26

The final result in the table is:


I found some (un)related cases in My Oracle Support.
I found also an Hermes bug report discusing the same case:

martes, 4 de marzo de 2014

Weblogic: setRollbackOnly called on transaction

Exception: weblogic.transaction.internal.AppSetRollbackOnlyException: setRollbackOnly called on transaction.

This exception can happen when you invoke transactional methods after calling MessageDrivenContext.setRollbackonly():

For example, given this (default: container, required) transactional MDB:

@MessageDriven
public class PruebaMdbA implements MessageListener {
   
    @EJB
    private PruebaSessionNoIntA pruebaSessionNoIntA;

    @Resource
    MessageDrivenContext mdc;

    public void onMessage(Message message) {
        //...
    mdc.setRollbackOnly();
        //...
     pruebaSessionNoIntA.testTx();
    }

//...
}

And this (default: container, required) transactional session EJB:

@Stateless
@LocalBean
public class PruebaSessionNoIntA {
    public void testTx() {
     //...
    }
}

If you invoke the onMessage() MDB method, it starts a transaction; when you invoke the session EJB method (pruebaSessionNoIntA.testTx()) after marking the transaction as rollbak only, you get the exception:

... <BEA-010080> <An error occurred while attempting to process a message inside a Message-Driven Bean: javax.ejb.EJBTransactionRolledbackException: EJB Exception: 
Nested exception: javax.ejb.TransactionRolledbackLocalException: EJB Exception: 
Nested exception: weblogic.transaction.internal.AppSetRollbackOnlyException: setRollbackOnly called on transaction
Exception is : javax.ejb.EJBTransactionRolledbackException: EJB Exception: 
at weblogic.ejb.container.internal.BaseLocalObject.handleSystemException(BaseLocalObject.java:453)
at weblogic.ejb.container.internal.BaseLocalObject.getBeanInstance(BaseLocalObject.java:162)
at weblogic.ejb.container.internal.BaseLocalObject.preInvoke(BaseLocalObject.java:103)
at weblogic.ejb.container.internal.BaseLocalObject.__WL_preInvoke(BaseLocalObject.java:67)
at weblogic.ejb.container.internal.SessionLocalMethodInvoker.invoke(SessionLocalMethodInvoker.java:20)
at test.PruebaSessionNoIntA_uae4n4_NoIntfViewImpl.testTx(Unknown Source)
at test.PruebaMdbA.onMessage(PruebaMdbA.java:40)
at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:585)
at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:488)
at weblogic.ejb.container.internal.NewJMSMessagePoller.processOneMessage(NewJMSMessagePoller.java:290)
at weblogic.ejb.container.internal.NewJMSMessagePoller.run(NewJMSMessagePoller.java:126)
at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:545)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)
Caused by: javax.ejb.TransactionRolledbackLocalException: EJB Exception: 
at weblogic.ejb.container.internal.EJBRuntimeUtils.throwTransactionRolledbackLocal(EJBRuntimeUtils.java:236)
at weblogic.ejb.container.internal.EJBRuntimeUtils.throwWrappedTransactionRolledbackLocal(EJBRuntimeUtils.java:255)
at weblogic.ejb.container.manager.BaseEJBManager.handleSystemException(BaseEJBManager.java:348)
at weblogic.ejb.container.manager.BaseEJBManager.setupTxListener(BaseEJBManager.java:276)
at weblogic.ejb.container.manager.StatelessManager.preInvoke(StatelessManager.java:171)
at weblogic.ejb.container.internal.BaseLocalObject.getBeanInstance(BaseLocalObject.java:146)
... 12 more
Caused by: weblogic.transaction.internal.AppSetRollbackOnlyException: setRollbackOnly called on transaction
at weblogic.transaction.internal.TransactionImpl.setRollbackOnly(TransactionImpl.java:555)
at weblogic.ejb.container.internal.BaseEJBContext.setRollbackOnly(BaseEJBContext.java:242)
at test.PruebaMdbA.onMessage(PruebaMdbA.java:36)
... 7 more







lunes, 3 de marzo de 2014

Maximum redelivery retries and retry delay in AQ backed MDB in Weblogic

When working with AQ backed MDB in Weblogic, the maximum redelivery retry count and retry delay can be configured at AQ queue level. The two related parameters, are:
  • max_retries
  • retry_delay
They can be configured at queue creation time (DBMS_AQADM.CREATE_QUEUE) or can be updated in a existent queue (DBMS_AQADM.ALTER_QUEUE). The default values are 5 for max_retries and 0 for retry (as soon as possible). The complete set of parameters can be found here.

In Weblogic, you can verify the parameter's operation with something like this:

@MessageDriven(
  activationConfig = {
    @ActivationConfigProperty(propertyName = "ConnectionFactoryJndiName", propertyValue = "..."),
    @ActivationConfigProperty(propertyName = "DestinationJndiName", propertyValue = "..."),
    @ActivationConfigProperty(propertyName = "DestinationType", propertyValue = "javax.jms.Queue")
  })
public class TestMyQueueMDB implements MessageListener {
  
  @Resource
  private MessageDrivenContext mdc;
  
  public void onMessage(Message message) {
    try {
      System.out.println("onMessage][START]time=" + new Date());
      System.out.println("JMSMessageID     : " + message.getJMSMessageID());
      System.out.println("JMSRedelivered   : " + message.getJMSRedelivered());
      System.out.println("JMSXDeliveryCount: " + message.getIntProperty("JMSXDeliveryCount"));

      //force retry
      mdc.setRollbackOnly();
    } catch(Exception e) {
      //...
    } finally {
      System.out.println("[onMessage][END]");
      System.out.println();
    }
  }
  //...
}

With an queue created with the default values,

dbms_aqadm.create_queue(
  queue_name=>'myQueue',
  queue_table=>'myQueueTable'
);

it should print something like this:

[onMessage][START]time: Mon Mar 03 13:18:26 COT 2014
JMSMessageID     : ID:205BD8B6042C4348A778FC5D467D2581
JMSRedelivered   : false
JMSXDeliveryCount: 1
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:18:26 COT 2014
JMSMessageID     : ID:205BD8B6042C4348A778FC5D467D2581
JMSRedelivered   : true
JMSXDeliveryCount: 2
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:18:26 COT 2014
JMSMessageID     : ID:205BD8B6042C4348A778FC5D467D2581
JMSRedelivered   : true
JMSXDeliveryCount: 3
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:18:26 COT 2014
JMSMessageID     : ID:205BD8B6042C4348A778FC5D467D2581
JMSRedelivered   : true
JMSXDeliveryCount: 4
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:18:26 COT 2014
JMSMessageID     : ID:205BD8B6042C4348A778FC5D467D2581
JMSRedelivered   : true
JMSXDeliveryCount: 5
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:18:26 COT 2014
JMSMessageID     : ID:205BD8B6042C4348A778FC5D467D2581
JMSRedelivered   : true
JMSXDeliveryCount: 6
[onMessage][END]

After doing an update like the following,

BEGIN
DBMS_AQADM.ALTER_QUEUE (
   queue_name => 'MYQUEUE',
   max_retries => 2,
   retry_delay => 10);
END;
/

it should print something like

[onMessage][START]time: Mon Mar 03 13:13:16 COT 2014
JMSMessageID     : ID:61819BE03F7942DE92B2FA196EF13888
JMSRedelivered   : false
JMSXDeliveryCount: 1
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:13:26 COT 2014
JMSMessageID     : ID:61819BE03F7942DE92B2FA196EF13888
JMSRedelivered   : true
JMSXDeliveryCount: 2
[onMessage][END]

[onMessage][START]time: Mon Mar 03 13:13:37 COT 2014
JMSMessageID     : ID:61819BE03F7942DE92B2FA196EF13888
JMSRedelivered   : true
JMSXDeliveryCount: 3
[onMessage][END]

References: