Exception Handler using Java Compute Node

Every project has its own way of implementing Error Handler and the subflows are the most common among them and in some cases the same is getting converted into an user defined node.

The problem with subflows are, “Every project should reference it and with UDN , the message flows and esql files become cmf file which hides information about what are all inside it. For any reason, if you need the code back again, it is difficult to get from CMF file so this is not a good solution.

Moving to Monitoring Profiles is another option but not sure why most of the projects did not explore the greatness of this. So, anyone who is looking for a pure java implementation for exception handling without any CMF issues, here is the code.

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

import com.ibm.broker.javacompute.MbJavaComputeNode;
import com.ibm.broker.plugin.MbElement;
import com.ibm.broker.plugin.MbException;
import com.ibm.broker.plugin.MbJSON;
import com.ibm.broker.plugin.MbMessage;
import com.ibm.broker.plugin.MbMessageAssembly;
import com.ibm.broker.plugin.MbOutputTerminal;
import com.ibm.broker.plugin.MbUserException;
import com.ibm.broker.plugin.MbXMLNSC;
import com.iib.broker.utilities.MbUtils;


public class ExJCN extends MbJavaComputeNode {
  
  private String errorNumber;
  private String exceptionType;
  private String errorFunction;
  private String errorMsg;
  private String errorPath;
  private String errorLocation;
  private String transactionXPath;
  private String causeOfError;
  private String logLevel;
  private String sqlStatement;
  private boolean isValidInputMessage;
  
  
  private Pattern REGEX_PATTERN ;
  private Set<String> ignoreErrorTexts;
  
  public void onInitialize() throws MbException {
    REGEX_PATTERN = Pattern.compile("^\\p{XDigit}+$");
    ignoreErrorTexts = new HashSet<String>(Arrays.asList(
        new String[] {"Caught exception and rethrowing","Error detected, rethrowing","Node throwing exception"}));
    
  }
  
  public void evaluate(MbMessageAssembly inAssembly) throws MbException {
    MbOutputTerminal out = getOutputTerminal("out");
//		MbOutputTerminal alt = getOutputTerminal("alternate");

    MbMessage inMessage = inAssembly.getMessage();
    MbMessageAssembly outAssembly = null;
    try {
      MbMessage outMessage = new MbMessage();
      outAssembly = new MbMessageAssembly(inAssembly, inAssembly.getLocalEnvironment(),inAssembly.getExceptionList(),outMessage);
      // ----------------------------------------------------------
      // Add user code below
      MbElement exception = MbUtils.getExceptionList(inAssembly).getFirstChild();
      MbElement inputRoot = inMessage.getRootElement();
      String ipMsgStatus = MbUtils.isValidMessage(inputRoot);
      this.isValidInputMessage = ipMsgStatus == null ? true : false;
      
      if (exception != null && exception.getName().endsWith("Exception")) {
        getExceptionDetails(exception, inputRoot);
      }else if (null != inputRoot && !MbUtils.getValueAsString(inputRoot, "HTTPResponseHeader/X-Original-HTTP-Status-Code").isEmpty() 
        && !MbUtils.getValueAsString(inputRoot, "HTTPResponseHeader/X-Original-HTTP-Status-Code").equals("200")	) {
        
        MbElement httpResponse = inputRoot.getFirstElementByPath("HTTPResponseHeader");
        this.exceptionType = httpResponse.getFirstChild().getValueAsString();
        this.errorNumber = MbUtils.getValueAsString(httpResponse, "X-Original-HTTP-Status-Code");
        this.errorLocation = MbUtils.getValueAsString(httpResponse, "X-Original-HTTP-Status-Line") +":"+MbUtils.getValueAsString(httpResponse,"Server");
        
        if (inputRoot.getLastChild().getName().equals("BLOB")) {
          this.errorMsg = MbUtils.getStringFromBlob(inputRoot);
        }else{
          this.errorMsg = MbUtils.getPayloadAsString(inputRoot, inputRoot.getLastChild().getName());
        }
      }else if (MbUtils.getLocalEnvironment(inAssembly).getFirstElementByPath("/HTTP/Input/Timeout") != null) {
        this.exceptionType = "Timeout Exception";
        this.errorNumber = "504";
        this.errorMsg = "Request could not be completed with in the defined " 
        + MbUtils.getValueAsString(MbUtils.getLocalEnvironment(inAssembly).getFirstElementByPath("/HTTP/Input/Timeout"), "OriginalClientWaitTime") + " seconds.";
        this.errorLocation = getMessageFlow().getName();
      }else if (null != inputRoot && inputRoot.getLastChild().getName().equals("SOAP")) {
        
        exception = inputRoot.getFirstElementByPath("/Body/Fault");
        
        if (exception != null) {

          this.exceptionType = MbUtils.getValueAsString(exception, "faultcode");
          this.errorMsg = MbUtils.getValueAsString(exception, "faultstring") ;
          
          MbElement detail = exception.getFirstElementByPath("detail"); 
          detail = detail == null ? null : detail.getFirstChild();
          
          while (detail != null) {
            this.errorMsg = this.errorMsg +";" + detail.getName() +" = "+ detail.getValue();
            detail = detail.getNextSibling();
          }
          this.errorLocation = MbUtils.getValueAsString(exception, "faultactor");
        } 
      }else{
        String unknown = "Unknown";
        this.errorNumber = unknown;
        this.errorFunction = unknown;
        this.errorLocation = getMessageFlow().getName();
        this.errorMsg = "Unable to capture the error message as it is not having any exceptionlist, soap fault, http timeout, http error. Debug the flow and see what is happening";
        this.errorPath = unknown;
        this.exceptionType = unknown;
      }
      
      
      MbMessage errorMessage = new MbMessage();
      MbElement errorRoot = errorMessage.getRootElement();
      MbElement errorXML = null;
      
      switch (MbUtils.getDomainName(inputRoot).toUpperCase()) {
      case MbJSON.PARSER_NAME:
        errorXML = prepErrorMessageInJSON(errorRoot);
        break;
      case "SOAP":
        errorXML = MbUtils.prepSOAPFault(errorRoot, MbUtils.getValueAsString(inputRoot, "HTTPInputHeader/X-Original-HTTP-Command"));
        break;
      default:
        errorXML = prepErrorMessageInXML(errorRoot);
        break;
      }
      
      MbElement detail = errorXML.createElementAsLastChild(MbElement.TYPE_NAME, "Detail", null);
      getTransactionDetails(inputRoot, detail);
      
      if (!this.isValidInputMessage) {
        this.errorMsg += "\n"+ipMsgStatus;
      }
      
      prepErrorMessage(detail);
      MbUtils.copyLocalEnvironmentAsXML(inAssembly, errorXML);
      MbUtils.copyEnvironmentAsXML(inAssembly, errorXML);
      
      MbElement outputRoot = outMessage.getRootElement();
      MbUtils.copyProperties(inputRoot, outputRoot);
      MbUtils.copyMQMD(inputRoot, outputRoot);
      MbUtils.createMQRFH2(inputRoot, outputRoot);
      MbUtils.setPubTopic(outputRoot, "Excep");
      
      MbElement parts = MbUtils.createMIMEParser(outputRoot, "ExceptionHandler");
      MbUtils.addToMimePart(parts,  errorRoot);
      
      if (isValidInputMessage) {
        MbUtils.addToMimePart(parts,  inputRoot);
      }
      
      // End of user code
      // ----------------------------------------------------------
    } catch (MbException e) {
      // Re-throw to allow Broker handling of MbException
      throw e;
    } catch (RuntimeException e) {
      // Re-throw to allow Broker handling of RuntimeException
      throw e;
    } catch (Exception e) {
      // Consider replacing Exception with type(s) thrown by user code
      // Example handling ensures all exceptions are re-thrown to be handled in the flow
      throw new MbUserException(this, "evaluate()", "", "", e.toString(),
          null);
    }
    // The following should only be changed
    // if not propagating message to the 'out' terminal
    out.propagate(outAssembly);

  }
  
  
  public MbElement prepErrorMessageInJSON(MbElement outRoot) throws MbException {
    return
           outRoot.createElementAsLastChild(MbJSON.PARSER_NAME)
           .createElementAsLastChild(MbJSON.ARRAY, "Data", null)
           .createElementAsLastChild(MbElement.TYPE_NAME, "Error", null);
  }
  
  public MbElement prepErrorMessageInXML(MbElement outRoot) throws MbException {
    return
           outRoot.createElementAsLastChild(MbXMLNSC.PARSER_NAME).
          createElementAsLastChild(MbElement.TYPE_NAME, "Error", null);
  }
  
  
  
public void getExceptionDetails(MbElement exceptionRef, MbElement inputRoot) throws MbException {
    
    StringBuffer errorMessage = new StringBuffer("");
    StringBuffer errorPath = new StringBuffer("");
    String text,label,hexText,payload;
    byte[] bs = null;
    
    if (inputRoot.getLastChild().getName().equals("BLOB")) {
      bs = (byte[]) MbUtils.getBlob(inputRoot).getValue();
    } else if (isValidInputMessage) {
      bs = MbUtils.getPayloadAsBlob(inputRoot);
    }
//		MbElement blob;
    final int MSG_MAX_SIZE = 10240;
    final String FAILED_MSG_IGNORE = "Failed message is more than "+ MSG_MAX_SIZE +" bytes.So not capturing it";
    
    while ( exceptionRef != null && exceptionRef.getName().endsWith("Exception")) {
      
      text = MbUtils.getValueAsString(exceptionRef, "Text");
      
      if (!ignoreErrorTexts.contains(text)) {
        errorMessage.append(text);
        errorMessage.append("->");
      }
      
      label = MbUtils.getValueAsString(exceptionRef, "Label");
      if (!label.isEmpty()) {
        errorPath.append(label);
        errorPath.append("-->");
        
        if (!label.startsWith("Imb")) {
          this.errorLocation = label;
        }
      }
      
      for (MbElement errInsRef = exceptionRef.getFirstElementByPath("Insert"); errInsRef != null && errInsRef.getName().equals("Insert"); errInsRef = errInsRef.getNextSibling()) {
        
        text = MbUtils.getValueAsString(errInsRef, "Text");
        
        if (null != text && !text.isEmpty() ) {
          
            switch (Integer.parseInt(errInsRef.getFirstChild().getValueAsString())) {
            case 2:
              if (text.contains("SOCKET")) {
                errorMessage.append(text);
                errorMessage.append("->");
              }
              break;
            case 12:
              if (text.getBytes().length <= MSG_MAX_SIZE && isHex(text)) {
                
                payload = null != bs && bs.length <= MSG_MAX_SIZE ?  new String(bs) : FAILED_MSG_IGNORE;
                hexText = !payload.equals(FAILED_MSG_IGNORE) ? convertHexToString(text) : "";
                
                if (hexText.contentEquals(payload) ) {
                  errorMessage.append("Failed Input Message :\n");
                }
                errorMessage.append(hexText);
              }else{
                errorMessage.append(FAILED_MSG_IGNORE);
              }
              errorMessage.append("->");
              break;
            default:
              errorMessage.append(text);
              errorMessage.append("->");
            }
        } 
      }
      text = MbUtils.getValueAsString(exceptionRef, "Function");
      this.sqlStatement = text.isEmpty()  ? null : text;
      
      errorMessage.append("\n");
      exceptionRef = exceptionRef.getLastChild();
    }
    
    exceptionRef = exceptionRef.getParent();
    this.errorNumber = MbUtils.getValueAsString(exceptionRef, "Number");
    this.errorFunction = MbUtils.getValueAsString(exceptionRef, "Function");
    this.causeOfError = MbUtils.getValueAsString(exceptionRef, "Text");
    this.exceptionType = exceptionRef.getName();
    this.errorMsg = errorMessage.substring(0, errorMessage.length() - "->\n".length());
    this.errorPath = errorPath.substring(0, errorPath.length() - "-->".length());
    
  }


public void prepErrorMessage( MbElement output) throws MbException {
  
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "TimeStamp", new Date().toString());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ExceptionType", this.exceptionType);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ErrorNumber", this.errorNumber);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "CauseOfError", this.causeOfError);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ErrorMessage", this.errorMsg);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ErrorLocation", this.errorLocation);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ErrorFunction", this.errorFunction);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ErrorPath", this.errorPath);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "SqlStatement", this.sqlStatement);
  
}
public Boolean isHex(String str) {
    return REGEX_PATTERN.matcher(str).matches();
}


private String convertHexToString(String hex){

    StringBuilder sb = new StringBuilder();

    for( int i=0; i<hex.length()-1; i+=2 ){
      sb.append((char)Integer.parseInt(hex.substring(i, (i + 2)), 16));
    }
    return sb.toString();
  }

public void getBrokerDetails(MbElement output) throws MbException {
  
  output = output.createElementAsLastChild(MbElement.TYPE_NAME, "RuntimeDetail", null);
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "MessageFlowLabel", getMessageFlow().getName());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "Application", getMessageFlow().getApplicationName());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "Library", getMessageFlow().getLibraryName());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "ExecutionGroup", getExecutionGroup().getName());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "BrokerName", getBroker().getName());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "Queue Manager", getBroker().getQueueManagerName());
  output.createElementAsLastChild(MbElement.TYPE_NAME_VALUE, "User", System.getProperty("user.home"));
}
}

If you are looking for complete implementation of the PI or any session with Java Compute Node, connect with us at support@vaithu.com/WhatsApp +1 6123058684. We offer complete solution in IIB for low cost.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.