java.lang.Object
com.tccc.kos.commons.core.service.trouble.Trouble

public class Trouble extends Object
Base class for troubles. Troubles are created by application code and added to TroubleService to make them visible within the system. Troubles can be used for various use cases:
  • Indication of a problem. For example, if a piece of hardware is missing a trouble can be used to indicate this error.
  • Action required. A trouble can set the resolveable flag which indicates an action that needs to be performed and then simply resolving the trouble can cause the action to be performed without the caller needing to know how to perform the action. For example, if a pump needs to be primed, a trouble can be created that will prime the pump when resolved.
  • Indication of state. For example, if the user is supposed to clean the device periodically, a trouble can be used to display a reminder. By making the trouble resolvable with no action, the user can dismiss the reminder by simply resolving the trouble.
Troubles have a concept of linked and impacted objects. Both are very similar in that any object can be added to either list and if the object implements TroubleAware then the object will receive events related to the trouble. However, impacted objects are included in equality whereas linked objects are not.

The purpose of impacted is to make two instances of the same trouble equal if they impact the same objects but not equal if they impact different object. Since it is common to generate multiple troubles of the same type over time, equality is used to ignore troubles that are already known. By using the impact list for equality it typically avoids the need for subclasses to define equals. In fact, care must be taken when redefining equals in subclasses as it must be very selective to maintain pruning of duplicate troubles. Examples of objects that are commonly added to the impact list are containers, pumps, holders, boards, and so on.

The purpose of the linked object list is to allow objects to be associated with the trouble which can be used for other processes. For example, if a service creates a trouble, it can add the service to the linked list and later remove all troubles linked to the service without needing to maintain references to the troubles.

The impact list cannot be added to once the trouble is added to the trouble service while the linked list can be modified at any time.

Troubles also have a concept of string tags. Tags will be exposed via endpoints and an be used to convey information about the trouble to external code such as a ui. For example consider a prime trouble which indicates a pump should be primed. When an ingredient is inserted it may create a prime trouble to make the ingredient healthy. Separately there may be a service that creates prime troubles if an ingredient hasn't been poured in a certain number of days. These troubles can have a tag to indicate they are the result of inactivity and the ui has the choice of displaying them different. This avoids the need to create a new type of trouble just to pass a hint to the ui.

Troubles can contain one or more ifaces. An iface is a symbolic name for a logical data interface provided by the trouble. Many troubles return similar data (holders, containers, pumps, etc...) and as each trouble is created there can be subtle variations that make external integrations, such as ui's, very difficult to write. An iface is a hint in the payload that tells the integration that it can expect certain pieces of data to exist with specific semantics. This also protects external code from a new trouble being created that happens to use a property name that is typically used in a different way. Integrations can see the iface is not in the list and assume the property does not contain the expected data.

As mentioned above, equality of troubles is very important as duplicate troubles cannot be added to the trouble list. The base Trouble class only compares class and impacted objects for equality. For subclasses it is important to consider what additional fields should be considered with regard to equality as it is rarely correct to simply include all fields. For example, consider a trouble that indicates an underflow on a pumps and it records the underflow rate for analytics. If the rate is included in equals, a pump may generate dozens of underflow troubles when there is really just one underlying issue. Troubles are logged even if duplicate so analytics can still see the occurrence but the actual list shown to the user would only reflect one trouble.

When serialized to json, troubles are generally restricted to one of the following views:

  • Trouble.View.class
  • Trouble.Debug.class extends Trouble.View.class
Normal usage is to only serialize with Trouble.View so any data in subclasses that needs to be shared across endpoints must be annotated with this view. The /debug endpoints are only intended for debugging and provides more visibility into troubles. Any additional data to be included in /debug endpoints should be annotated with Trouble.Debug.
Since:
1.0
Version:
2022-09-17
  • Constructor Details

    • Trouble

      public Trouble()
  • Method Details

    • getType

      public String getType()
      Return the type of the trouble which is the simple class name.
    • addIface

      public void addIface(String iface)
      Add a symbolic interface name to the trouble. This is to indicate to external systems that they can expect certain data to exist in the trouble. For example, an external system may want to gather all troubles that relate to a board using the handle path to the impacted board. Rather than checking every trouble for board path, the related troubles can add an interface name to indicate it is board related. This also allows other troubles that happen to include a board path, but are not actually troubles about the board to not be accidentally grouped by the external system.

      The most common pattern is for a base Trouble subclass to add the interface names and associated data so subclasses get the functionality automatically.

      Another common pattern is for troubles that don't share a common base class but share common data (such as including board path to indicate the board that is impacted) to add the same interface so the data appears consistent to external systems like ui code.

      Parameters:
      iface - the subType to add
    • addLink

      public void addLink(Object obj)
      Link an object to the trouble
      Parameters:
      obj - the object to link
    • removeLink

      public void removeLink(Object obj)
      Remove a linked object from the trouble
      Parameters:
      obj - the linked object to remove
    • isLinked

      public boolean isLinked(Object obj)
      Return true if the specified object is linked to the trouble.
    • addTag

      public void addTag(String tag)
      Add a tag to the trouble
      Parameters:
      tag - the tag to add
    • removeTag

      public void removeTag(String tag)
      Remove a tag from the trouble
      Parameters:
      tag - the tag to remove
    • isTagged

      public boolean isTagged(String obj)
      Return true if the specified string is in the tag list.
    • addImpacted

      public void addImpacted(Object obj)
      Add an object to the impact list. The impact list is used for equality so be aware of how the impact list is used in the case duplicate troubles are expected.
      Parameters:
      obj - the impacted object
    • removeImpacted

      public void removeImpacted(Object obj)
      Remove an object from the impact list. The impact list is used for equality so be aware of how the impact list is used in the case duplicate troubles are expected.
      Parameters:
      obj - the object to remove
    • isImpacted

      public boolean isImpacted(Object obj)
      Return true if the specified object is in the impact list.
    • isResolvable

      public boolean isResolvable()
      Return true if the trouble is resolvable. This must always return the same value over the life of the trouble.
    • resolvable

      public void resolvable()
      Indicate that the trouble is resolvable. This should be called in the trouble constructor to indicate that the trouble can perform an action by calling resolve() .
    • getCount

      public int getCount()
      TroubleService doesn't allow duplicate troubles to be added (based on object equality). This means that if a trouble that it equal to an existing trouble is added, the new trouble will be ignored. However, TroubleService will bump the count on the existing trouble to reflect that another equal trouble was added. The count cannot be part of equality as it would cause troubles that are otherwise equal to no longer be equal. The count is also not reflected in the json view of the object as troubles are not re-broadcast when the count changes.

      This is a useful way to determine how many troubles of a given type have occurred without requiring external counters.

    • incCount

      public void incCount()
      Increment the count for the trouble. This is called when another trouble that is equal to this is added via addTrouble() . This can be called externally in the event that a service would have created an equal trouble and needs to record that in the count, but wants to avoid creating the trouble simply to increment.
    • resolve

      public FutureWork resolve()
      If resolvable, return the work to perform to potentially resolve the underlying problem that caused the trouble. If the work completes successfully then the trouble will be removed, otherwise it will remain in the list and continue to be resolvable. If this trouble returns true for isResolvable() it must return a valid work from this method.
      Returns:
      the work to resolve the trouble
    • onRemoved

      public void onRemoved()
      Called when a trouble is removed from TroubleService . This can be used to perform some final cleanup associated with the trouble.
    • getId

      public int getId()
    • getImpacted

      public Set<Object> getImpacted()
    • getLinked

      public Set<Object> getLinked()
    • getTags

      public Set<String> getTags()
    • getIfaces

      public Set<String> getIfaces()
    • getCreateTime

      public ZonedDateTime getCreateTime()
    • getInfo

      public TroubleInfo getInfo()
    • getReason

      public String getReason()
    • getClientData

      public JsonViewWrapper getClientData()
    • getGroup

      public String getGroup()
    • toString

      public String toString()
      Overrides:
      toString in class Object
    • equals

      public boolean equals(Object o)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • setReason

      public Trouble setReason(String reason)
      Returns:
      this .
    • setResolvable

      public Trouble setResolvable(boolean resolvable)
      Returns:
      this .