Rules, plugins & batch

The platform gives you three main hooks for custom behaviour: rules that run at fixed moments, plugins where the app asks your code for an answer, and batch processes that run in the background. Knowing which hook to use is half the design.

Which hook when?

NeedHook
Check or default data every time an entity is savedPre-update or validation rules
Decide who gets new workAssignment (and segmentation) rules
React to a change by notifying another systemEvent fired rules + messaging (module 16)
Replace how the app does one specific thing (number generation, policy search, rating, billing system)Plugin
Process many records on a scheduleBatch process or work queue

Rule sets

Rules are Gosu, organised in Studio as a tree: rule set categories → rule sets → rules, each rule having a condition and an action, and optional child rules. The app calls a rule set at a fixed moment, passing in the root entity.

Rule set typeRunsTypical use
Pre-updateWhen a bundle commits, before validationDefault and derive values, create activities
ValidationWhen a bundle commits (and on demand)Reject invalid data at a validation level
Segmentation (CC)On new claims and exposuresSet complexity segment and strategy
AssignmentWhen work needs an ownerPick group, then user
Workplan (CC)After assignment of new claimsCreate initial activities
Event firedWhen an entity event fires for a messaging destinationCreate integration messages

A validation rule, written as condition and action:

// Rule: Loss date cannot be in the future
// CONDITION
claim.LossDate != null and claim.LossDate > gw.api.util.DateUtil.currentDate()

// ACTION
claim.reject(ValidationLevel.TC_LOADSAVE,
             DisplayKey.get("Ext.Validation.LossDateInFuture"),
             null, null)
  • Rules run top to bottom. An action can call actions.exit() to stop the rest of the rule set — common in assignment once an owner is found.
  • Validation levels let data be incomplete early (load-save) but require more before later steps, e.g. "ready to pay" or "sent to external system".
  • Keep conditions cheap: they run on every commit. Avoid queries in pre-update rules unless they're scoped tightly.
Testability

Put the real logic in a Gosu class and have the rule call it in one line. Then GUnit tests can call the class directly, and rule trees stay readable for BAs reviewing them.

Plugins

A plugin is an interface the application calls when it needs something done — and which implementation runs is configured, not hard-coded. Each app ships with plugin interfaces such as number generators, policy search (ClaimCenter), billing system and policy system links (PolicyCenter ↔ BillingCenter), document storage and rating. Exact names vary by app and release.

  • The plugin registry (.gwp files) maps each interface to an implementation class and its parameters.
  • Base often ships a demo or internal implementation; projects replace it with one that calls their real systems.
  • Parameters in the registry (URLs, timeouts) can differ per environment.

You can also define your own plugin interface, so callers don't care which vendor sits behind it:

package acme.fraud

interface IFraudScoringPlugin_Ext {
  function score(claim : Claim) : int
}

class VendorFraudScoringPlugin_Ext implements IFraudScoringPlugin_Ext {
  override function score(claim : Claim) : int {
    var response = FraudApiClient_Ext.instance().check(claim.PublicID, claim.LossCause.Code)
    return response.Score
  }
}

// Caller: look the plugin up through the registry, not with "new"
var plugin = gw.plugin.Plugins.get(IFraudScoringPlugin_Ext)
claim.FraudScore_Ext = plugin.score(claim)

Batch processes and work queues

Background work runs in two styles:

Batch process

A single job that does its work start to finish: e.g. renewals start, invoice billing, activity escalation. Simple, but runs on one server thread.

Work queue

A writer finds the items to process; many workers on many servers process them in parallel, each item in its own transaction. Use for high volumes.

Both are scheduled through the scheduler configuration and can be run on demand from the server tools screens. A minimal custom batch process:

package acme.batch

uses gw.processes.BatchProcessBase

class CloseStaleDraftClaims_Ext extends BatchProcessBase {

  construct() {
    super(BatchProcessType.TC_CLOSESTALEDRAFTCLAIMS_EXT)
  }

  override function doWork() {
    for (claim in StaleDraftClaimFinder_Ext.find(30)) {
      if (TerminateRequested) return
      gw.transaction.Transaction.runWithNewBundle(\ bundle -> {
        bundle.add(claim).markAbandoned_Ext()
      })
      incrementOperationsCompleted()
    }
  }
}
  • Register a new BatchProcessType typecode for it, and add it to the process registry and scheduler.
  • Check TerminateRequested so operators can stop it cleanly.
  • Commit in small bundles so one bad record doesn't roll back the whole run.

Common pitfalls

  • Doing integration calls inside pre-update rules — the commit waits on a remote system and fails if it's down. Use messaging instead.
  • Pre-update rules that change data which triggers more rules, causing loops or surprising side effects.
  • Assignment rules that fall through without assigning anyone; always have a default.
  • Batch jobs that load everything into memory instead of streaming query results.