Chapter 18. Auditing

18.1. SwitchYard Auditing

SwitchYard possesses auditing functionality. It traces exchanges through their various mediation states. The auditing functionality requires the CDI environment (the META-INF/beans.xml file) to run. The auditing functionality also works in a test environment.

18.2. Enable Custom Auditors

Audit mechanism requires CDI runtime environment to run.
To enable custom auditors, define the Auditor implementations with the @Named annotation. It helps Apache Camel component to recognize all the auditor implementations. Camel Exchange Bus, a default implementation used by SwitchYard, look up for bean definitions with @Audit annotation.

@Audit
@Named("custom auditor")
public class SimpleAuditor implements Auditor
{
@Override
public void beforeCall(Processors processor, Exchange exchange)
{
System.out.println("Before " + processor.name());
}
 
@Override
public void afterCall(Processors processor, Exchange exchange) 
{
System.out.println("After " + processor.name());
}
 
}

Note

Do not include any state inside the Custom Auditor's field. Red Hat recommends you to use exchange properties or message headers to store values.

18.3. Mediation State

Mediation state is the term used to described the interim states a SwitchYard exchange goes through as it is sent from a service consumer to a service provider.

18.4. List of Mediation States

Domain handlers
In this state all the handlers defined in switchyard.xml are executed. This is an early phase of mediation where you can either implement own logic or choose the service provider logic to use.
Addressing
If this is not specified by the domain handlers then the addressing handler will determine what to do by using the consumer contract.
Transaction
If the service is required to run a transaction this handler starts it.
Security
This state verifies constraints related to authentication and authorization.
General policy
This executes checks other than those for security and transactions.
Validation
This executes custom validators.
Transformation
This prepares the payload by calling a provider.
Validation (2)
This validates the transformed payload.
Provider call
This calls the provisional service.
Transaction (2)
This commits or, if necessary, rolls back the transaction.
If the service consumer is synchronous and the exchange pattern is set to in-out, then some of these handlers may be called once again:
Domain handlers
These are called when a response is generated by a provider service.
Validation
This verifies the output generated by the provider.
Transformation
This converts the payload to the structure required by the consumer.
Validation
This checks the output after the transformation has occurred.
Consumer callback
This returns the exchange to the service consumer.

18.5. Create a Custom Auditor

Prerequisities

  • CDI Runtime
  • Annotate your auditor implementations with @Named in order to have Camel recognize them.

    Note

    The Camel Exchange Bus looks for bean definitions with the @Audit annotation.
    Here is code that shows what a very simple auditor would look like:
    				
    @Audit
    @Named("custom auditor")
    public class SimpleAuditor implements Auditor {
     
        @Override
        public void beforeCall(Processors processor, Exchange exchange) {
            System.out.println("Before " + processor.name());
        }
     
        @Override
        public void afterCall(Processors processor, Exchange exchange) {
            System.out.println("After " + processor.name());
        }
     
    }				
    

    Important

    Be aware that the afterCall method is not called if the step it surrounds throws an exception. If this happens, afterCall will be skipped.
Result

You can see many statements like 'Before DOMAIN_HANDLERS' and 'Before ADDRESSING' appearing in the server console. This is because every step of mediation is surrounded by this SimpleAuditor class.

18.6. Determine Location for Audit Assignment

In SwitchYard, you may choose to provide an argument to the @Audit annotation. The accepted values for this comes from org.switchyard.bus.camel.processors.Processors enumeration. For example, the following combination can handle only validation occurrences:
@Audit(Processors.VALIDATION)
Note the following important facts about validation, transformation, and transaction in SwitchYard:
  • The validation is executed twice for in-only exchanges and four times for in-out exchanges.
  • The validation occurs before and after transformation of inbound messages.
  • When SwitchYard sends outgoing messages, the validation occurs before and after transformation of outbound messages.
  • Transformation is executed once for in-only exchanges and twice for in-out exchanges.
  • Transaction phase is always executed twice.
If you want to implement only one execution of your auditor, use the following combination:
@Audit(Processors.PROVIDER_CALLBACK).
Here, the auditor is executed just before sending exchange to service implementation. You can also implement one auditor instance with few mediation steps. For example, a bean with annotation following:
@Audit({Processors.PROVIDER_CALLBACK, Processors.CONSUMER_CALLBACK})
This bean is executed twice. One pair of before or after call for provider service and second pair for outgoing response.

18.7. Use Exchange Properties

As only one instance of auditor is created by default and there is no guarantee for dispatching order, ensure that the custom auditors do not preserve state inside any of the fields. If you want to store values, use exchange properties or message headers. Here is an example of how to count processing time using Exchange properties as temporary storage:
@Named("custom auditor")
public class SimpleAuditor implements Auditor {
 
    private Logger _logger = Logger.getLogger(SimpleAuditor.class);
 
    @Override
    public void beforeCall(Processors processor, Exchange exchange) {
        exchange.setProperty("time", System.currentTimeMillis());
    }
 
    @Override
    public void afterCall(Processors processor, Exchange exchange) {
        long time = System.currentTimeMillis() - exchange.getProperty("time", 0, Long.class);
        _logger.info("Step " + processor.name() + " took " + time + "ms");
    }
 
}