3.9. Policy Enforcement
samples/policy folder, demonstrates two approaches that can be used to provide "policy enforcement". This example makes uses of the example in Switchyard application, located in the samples/ordermgmt folder.
3.9.1. Synchronous Enforcement
3.9.1.1. The Policy
org.overlord.rtgov.quickstarts.demos.orders.interceptors.ExchangeValidator. This class is derived from an abstract base class that provides most of the required functionality for converting an Exchange message into an activity event. For example:
@Audit({Processors.TRANSFORMATION})
@Named("ExchangeValidator")
public class ExchangeValidator extends AbstractExchangeValidator implements Auditor {
/**
* {@inheritDoc}
*/
public void afterCall(Processors processor, org.apache.camel.Exchange exch) {
ExchangePhase phase=exch.getProperty("org.switchyard.bus.camel.phase", ExchangePhase.class);
if (phase == ExchangePhase.OUT) {
handleExchange(exch, phase);
}
}
/**
* {@inheritDoc}
*/
public void beforeCall(Processors processor, org.apache.camel.Exchange exch) {
ExchangePhase phase=exch.getProperty("org.switchyard.bus.camel.phase", ExchangePhase.class);
if (phase == ExchangePhase.IN) {
handleExchange(exch, phase);
}
}
}
[{
"name" : "RestrictUsage",
"version" : "1",
"predicate" : {
"@class" : "org.overlord.rtgov.ep.mvel.MVELPredicate",
"expression" : "event instanceof org.overlord.rtgov.activity.model.soa.RequestReceived && event.serviceType == \"{urn:switchyard-quickstart-demo:orders:0.1.0}OrderService\""
},
"eventProcessor" : {
"@class" : "org.overlord.rtgov.ep.mvel.MVELEventProcessor",
"script" : "VerifyLastUsage.mvel",
"services" : {
"CacheManager" : {
"@class" : "org.overlord.rtgov.common.infinispan.service.InfinispanCacheManager"
}
}
}
}]
VerifyLastUsage.mvel, which is:
String customer=event.properties.get("customer");
if (customer == null) {
return;
}
cm = epc.getService("CacheManager");
// Attempt to lock the entry
if (!cm.lock("Principals", customer)) {
epc.handle(new java.lang.RuntimeException("Unable to lock entry for principal '"+customer+"'"));
return;
}
// Access the cache of principals
principals = cm.getCache("Principals");
principal = principals.get(customer);
if (principal == null) {
principal = new java.util.HashMap();
}
java.util.Date current=principal.get(event.serviceType+"-lastaccess");
java.util.Date now=new java.util.Date();
if (current != null && (now.getTime()-current.getTime()) < 2000) {
epc.handle(new java.lang.RuntimeException("Customer '"+customer+"' cannot perform more than one request every 2 seconds"));
return;
}
principal.put(event.serviceType+"-lastaccess", now);
principals.put(customer, principal);
epc.logDebug("Updated principal '"+customer+"': "+principals.get(customer));
This script uses the CacheManager service, configured within the EventProcessor component, to obtain a cache called "Principals". This cache is used to store information about Principals as a map of properties. The implementation uses Infinispan, to enable the cache to be shared between other applications, as well as in a distributed/cluster environment (based on the infinispan configuration).
handle() method on the EventProcessor context. This results in the exception being thrown back to the execution environment, interrupting the execution of the business transaction.
3.9.1.2. Quickstart Example
3.9.1.3. Installing the Example
- Start the Switchyard server using the following command from the bin folder:
./standalone.sh -c standalone-full.xml
- Install the example Switchyard application by running the following command from the
${rtgov}/samples/ordermgmtfolder:mvn jboss-as:deploy
- Change to the
${rtgov}/samples/policy/syncfolder and run the following command again:mvn jboss-as:deploy
3.9.1.4. Running the Example
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<orders:submitOrder xmlns:orders="urn:switchyard-quickstart-demo:orders:1.0">
<order>
<orderId>1</orderId>
<itemId>BUTTER</itemId>
<quantity>100</quantity>
<customer>Fred</customer>
</order>
</orders:submitOrder>
</soap:Body>
</soap:Envelope>
The messages can be sent using an appropriate SOAP client (e.g. SOAP-UI) or by running the test client available with the Switchyard application, by running the following command from the ${rtgov}/samples/ordermgmt/app folder:
mvn exec:java -Dreq=order1 -Dcount=2
Result
If the two requests are received within two seconds of each other, it results in the following response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>org.switchyard.exception.SwitchYardException: Customer 'Fred' cannot perform more than one request every 2 seconds</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
3.9.2. Asynchronous Enforcement
- Activity event analysis, using the Event Processor Network mechanism, can be used to implement business policies
- Results from the business policies can be cached for reference by other applications
- Platform specific interceptors can reference the results to impact the behavior of the business transaction (for example, prevent suspended customers purchasing further items)
3.9.2.1. The Policy
Event Analysis
The runtime governance infrastructure analyses the activity events generated by an executing business transaction using one or more Event Processor Networks (or EPN). A standard EPN is deployed within the infrastructure to isolate the SOA events (e.g. request/responses being sent or received). This quickstart deploys another EPN that subscribes to the events produced by the standard EPN:
{
"name" : "AssessCreditPolicyEPN",
"version" : "1",
"subscriptions" : [ {
"nodeName" : "AssessCredit",
"subject" : "SOAEvents"
} ],
"nodes" : [
{
"name" : "AssessCredit",
"sourceNodes" : [ ],
"destinationSubjects" : [ ],
"maxRetries" : 3,
"retryInterval" : 0,
"predicate" : {
"@class" : "org.overlord.rtgov.ep.mvel.MVELPredicate",
"expression" : "event.serviceProvider && !event.request && event.serviceType == \"{urn:switchyard-quickstart-demo:orders:0.1.0}OrderService\""
},
"eventProcessor" : {
"@class" : "org.overlord.rtgov.ep.mvel.MVELEventProcessor",
"script" : "AssessCredit.mvel",
"services" : {
"CacheManager" : {
"@class" : "org.overlord.rtgov.common.infinispan.service.InfinispanCacheManager"
}
}
}
}
]
}
This EPN subscribes to the published SOA events and applies the predicate which ensures that only events from a service provider interface, that are responses and are associated with the OrderService service, will be processed. Events that pass this predicate are then submitted to the business policy (defined in the MVEL script AssessCredit.mvel), which is:
String customer=event.properties.get("customer");
if (customer == null) {
return;
}
cm = epc.getService("CacheManager");
// Attempt to lock the entry
if (!cm.lock("Principals", customer)) {
epc.handle(new Exception("Unable to lock entry for principal '"+customer+"'"));
return;
}
// Access the cache of principals
principals = cm.getCache("Principals");
principal = principals.get(customer);
if (principal == null) {
principal = new java.util.HashMap();
}
int current=principal.get("exposure");
if (current == null) {
current = 0;
}
if (event.operation == "submitOrder") {
double total=event.properties.get("total");
int newtotal=current+total;
if (newtotal > 150 && current <= 150) {
principal.put("suspended", Boolean.TRUE);
}
principal.put("exposure", newtotal);
} else if (event.operation == "makePayment") {
double amount=event.properties.get("amount");
int newamount=current-amount;
if (newamount <= 150 && current > 150) {
principal.put("suspended", Boolean.FALSE);
}
principal.put("exposure", newamount);
}
principals.put(customer, principal);
epc.logDebug("Updated principal '"+customer+"': "+principals.get(customer));
This script uses the CacheManager service, configured within the EPN node, to obtain a cache called "Principals". This cache is used to store information about Principals as a map of properties. The implementation uses Infinispan, to enable the cache to be shared between other applications, as well as in a distributed/cluster environment (based on the infinispan configuration).
Result Management
The results derived from the previous policy are stored in an Infinispan implemented cache called "Principals". To make this information available to runtime governance clients, we use the Active Collection mechanism - more specifically we define an Active Collection, as part of the standard installation, that wraps the Infinispan cache. The configuration of the Active Collection Source is:
[
{
......
},{
"@class" : "org.overlord.rtgov.active.collection.ActiveCollectionSource",
"name" : "Principals",
"type" : "Map",
"lazy" : true,
"visibility" : "Private",
"factory" : {
"@class" : "org.overlord.rtgov.active.collection.infinispan.InfinispanActiveCollectionFactory",
"cache" : "Principals"
}
}
]
The visibility is marked as private to ensure that exposure information regarding customers is not publicly available via the Active Collection REST API.
The Enforcer
The enforcement is provided by a specific Switchyard Auditor implementation (PolicyEnforcer) that is included with the order management application. The main part of this auditor is:
public void beforeCall(Processors processor, org.apache.camel.Exchange exch) {
....
if (_principals != null) {
org.switchyard.bus.camel.CamelMessage mesg=(org.switchyard.bus.camel.CamelMessage)exch.getIn();
if (mesg == null) {
LOG.severe("Could not obtain message for phase ("+phase+") and exchange: "+exch);
return;
}
org.switchyard.Context context=new org.switchyard.bus.camel.CamelCompositeContext(exch, mesg);
java.util.Set<Property> contextProps=context.getProperties(
org.switchyard.Scope.MESSAGE);
Property p=null;
for (Property prop : contextProps) {
if (prop.getName().equals("org.switchyard.contentType")) {
p = prop;
break;
}
}
if (p != null && p.getValue().toString().equals(
"{urn:switchyard-quickstart-demo:orders:1.0}submitOrder")) {
String customer=getCustomer(mesg);
if (customer != null) {
if (_principals.containsKey(customer)) {
@SuppressWarnings("unchecked")
java.util.Map<String,java.io.Serializable> props=
(java.util.Map<String,java.io.Serializable>)
_principals.get(customer);
// Check if customer is suspended
if (props.containsKey("suspended")
&& props.get("suspended").equals(Boolean.TRUE)) {
throw new RuntimeException("Customer '"+customer
+"' has been suspended");
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("*********** Policy Enforcer: customer '"
+customer+"' has not been suspended");
LOG.fine("*********** Principal: "+_principals.get(customer));
}
} else {
LOG.warning("Unable to find customer name");
}
}
}
The variable _principals refers to an Active Map used to maintain information about the principal (i.e. the customer in this case). This information is updated using the policy rule defined in the previous section..
3.9.2.2. Quickstart Example
3.9.2.3. Installing the Example
- To install the example, start the Switchyard server using the following command from the bin folder:
./standalone.sh -c standalone-full.xml
- Install the example Switchyard application, achieved by running the following command from the ${rtgov}/samples/ordermgmt folder:
mvn jboss-as:deploy
- Change to the
${rtgov}/samples/policy/asyncfolder and run the following command again:mvn jboss-as:deploy
3.9.2.4. Running the Example
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<orders:submitOrder xmlns:orders="urn:switchyard-quickstart-demo:orders:1.0">
<order>
<orderId>1</orderId>
<itemId>BUTTER</itemId>
<quantity>100</quantity>
<customer>Fred</customer>
</order>
</orders:submitOrder>
</soap:Body>
</soap:Envelope>
The message can be sent using an appropriate SOAP client (e.g. SOAP-UI) or by running the test client available with the Switchyard application, by running the following command from the ${rtgov}/samples/ordermgmt/app folder:
mvn exec:java -Dreq=order1
Result
This results in the following response, indicating that the purchase was successful, as well as identifying the total cost of the purchase (that is 125).
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<orders:submitOrderResponse xmlns:orders="urn:switchyard-quickstart-demo:orders:1.0">
<orderAck>
<orderId>1</orderId>
<accepted>true</accepted>
<status>Order Accepted</status>
<customer>Fred</customer>
<total>125.0</total>
</orderAck>
</orders:submitOrderResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
If the customer's debt exceeds the threshold of 150 then the customer would be suspended. Therefore if the same request is issued again, resulting in another total of 125, then the overall exposure regarding this customer is now 250. If we then attempt to issue the same request a third time, this time we will receive a SOAP fault from the server. This is due to the fact that the PolicyEnforcer auditor has intercepted the request, and detected that the customer is now suspended.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SOAP-ENV:Server</faultcode>
<faultstring>Customer 'Fred' has been suspended</faultstring>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
If we now send a "makePayment" request as follows to the same URL:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:switchyard-quickstart-demo:orders:1.0">
<soapenv:Header/>
<soapenv:Body>
<urn:makePayment>
<payment>
<customer>Fred</customer>
<amount>200</amount>
</payment>
</urn:makePayment>
</soapenv:Body>
</soapenv:Envelope>
This can be sent using a suitable SOAP client (for example, SOAP-UI) or the test client in the order management application:
mvn exec:java -Dreq=fredpayThis results in the customer being unsuspended, as it removes 200 from their current exposure (leaving 50). To confirm this, try sending the submitOrder request again.

Where did the comment section go?
Red Hat's documentation publication system recently went through an upgrade to enable speedier, more mobile-friendly content. We decided to re-evaluate our commenting platform to ensure that it meets your expectations and serves as an optimal feedback mechanism. During this redesign, we invite your input on providing feedback on Red Hat documentation via the discussion platform.