ContentsIndexHome
PreviousUpNext
Callback.java

Some calls on the SDK which will cause errors, to show how the implementations of the ICallBack will react.

The LoggingCallback and the DebuggingCallback are both implementations of the ICallBack. 

The LoggingCallback logs the minor errors in stead of throwing them, the DebuggingCallback asserts false if an InvalidUse error is encountered. 

The CallBack run function makes some calls on the SDK which will cause errors, to show how the implementations of the ICallBack will react.

  1: package com.aimms.aimmssdk.examples.callback;
  2: 
  3: import com.aimms.aimmssdk.*;
  4: import java.util.ArrayList;
  5: import org.slf4j.Logger;
  6: import org.slf4j.LoggerFactory;
  7: 
  8: /**
  9:  * This example demonstrates the custom handling of errors. It overrides the
 10:  * default behavior in which AimmsExceptions are thrown in all error cases in
 11:  * two ways: The LoggingCallback logs all minor errors, and keeps throwing the
 12:  * severe ones The DebuggingCallback logs as well, and asserts where errors can
 13:  * only be expected if the model was called erroneously.
 14:  */
 15: class Callback {
 16: 
 17:     static private Logger logger = LoggerFactory.getLogger(Callback.class);
 18: 
 19:     static class LoggingCallback implements ICallBack {
 20: 
 21:         public void onMessages(ArrayList<AimmsMessage> messages) {
 22:             // Overridden behavior:
 23:             // Only throw if an error is caused by the communication layer or the AIMMS model. 
 24:             // Log the message in other cases. 
 25: 
 26:             for (int m = 0; m < messages.size(); ++m) {
 27:                 AimmsMessage msg = messages.get(m);
 28:                 switch (msg.code) {
 29:                     case AM_Info:
 30:                         logger.info(msg.message);
 31:                         break;
 32:                     case AM_Warning:
 33:                         logger.warn(msg.message);
 34:                         break;
 35:                     case AM_InvalidUse:
 36:                         logger.error("InvalidUse: {}", msg.message);
 37:                         break;
 38:                     case AM_Runtime:
 39:                         logger.error("Runtime: {}", msg.message);
 40:                         break;
 41:                     case AM_License:
 42:                         throw new AimmsLicenseException(msg.message);
 43:                     case AM_Communication:
 44:                         throw new AimmsCommunicationException(msg.message);
 45:                     case AM_General:  // AIMMS Model errors
 46:                         throw new AimmsException(msg.code, msg.message);
 47:                     default:
 48:                         throw new AimmsException(msg.code, msg.message);
 49:                 }
 50: 
 51:                 // The rationale behind this distinction is that in the cases in which exceptions are thrown now, 
 52:                 // the state of the data is not known. For example: if during the flush of the buffers an error on 
 53:                 // a specific value is detected by the AIMMS model, it is not known which part of the remaining data 
 54:                 // is already written to the model. 
 55:             }
 56:         }
 57:     }
 58: 
 59:     static class DebuggingCallback implements ICallBack {
 60: 
 61: 
 62:     public void onMessages(ArrayList<AimmsMessage> messages) {
 63:         // Overridden behavior:
 64:         // 
 65:         //  assert whether an AM_InvalidUse is encountered, because that is an indication of a bug. 
 66:         //
 67:         // For example: adding a string to a numerical parameter gives AM_InvalidUse. 
 68: 
 69: 
 70:         for (int m = 0; m < messages.size(); ++m) {
 71:             AimmsMessage msg = messages.get(m);
 72: 
 73:             switch (msg.code) {
 74:                 case AM_InvalidUse:
 75:                     logger.error(msg.message);
 76:                     assert (false);
 77:                     break;
 78:                 default:
 79:                     logger.error("{}:{}", msg.code, msg.message);
 80:                     break;
 81:             }
 82: 
 83:         }
 84:     }
 85: }
 86:     
 87:     
 88:     
 89:     public static void run(ISession session) {
 90:         LoggingCallback cb = new LoggingCallback();
 91:         //Attach the custom Callback to the session.
 92:         session.setCallBack(cb);
 93: 
 94:         //To illustrate the behavior of our callback, make some mistakes:
 95: 
 96:         //Try to retrieve the set Locations as a multidimensional parameter.
 97:         //This is an InvalidUse error: 
 98:         //   LoggingCallback will not throw
 99:         //   DebuggingCallback will assert here. 
100:         IMultiDimData parameterLocations = session.openMultiDim("Locations");
101: 
102:         if (parameterLocations == null) {
103:             logger.error("Expected an InvalidUse error!");
104:         }
105: 
106:         IMultiDimData parameterSupply = session.openMultiDim("Supply");
107:         //Do not forget to check the result if the callback does not throw on each error!
108:         if (parameterSupply == null) {
109:             logger.error("Did not expect an error here!");
110:         } else {
111:             // Since we did not add any depots, we will get a runtime error here:
112:             parameterSupply.setValue(new Tuple("London"), 20.0);
113: 
114:             // Using "insert" will resolve this error, since it adds London to the index domain of Supply if not existing. 
115:             parameterSupply.insert(new Tuple("London"), 20.0);
116: 
117:             // Supply is a numeric parameter: it will not allow strings as value:
118:             // DebuggingCallback will assert.
119:             parameterSupply.insert(new Tuple("Paris"), "thirty");
120:         }
121: 
122:         IMultiDimData parameterDemand = session.openMultiDim("Demand");
123: 
124:         if (parameterDemand == null) {
125:             logger.error("Did not expect an error here!");
126:         } else {
127:             ITuple tup = parameterDemand.createTuple();
128: 
129:             // "London" exists in the set Locations (because we added it to Depots by inserting in Supply),
130:             // but the index domain of Demand is Customers, which  is still empty.
131:             // The nocheck_ functions are less expensive because they do not check this in advance.
132:             // Cout will not show an error after these actions:
133:             tup.getElement(0).nocheck_setLabel("London");
134:             parameterDemand.setValue(tup, 90);
135:             tup.close();
136: 
137:             // But this performance improvement comes with a downside:
138:             try {
139:                 // The mistake will become apparent when the data is sent to the model. 
140:                 // Then it is undetermined wether the inserted value for "London" in Supply is indeed inserted in Aimms.
141:                 // If Londen is added to the domainset and this set is flushed before the Supply is flushed,
142:                 // the Supply of Londen will be set to its new value without an error.
143:                 // If Londen is not added or Supply is flushed earlier (which may be due to an internal flush),
144:                 // then an error will occur and even other buffered modifications to Supply may not have been inserted in Aimms.
145:                 session.updateData();
146: 
147:             } catch (AimmsException e) {
148:                 logger.error(e.getMessage());
149:             }
150: 
151:             parameterSupply.close();
152:             parameterDemand.close();
153:         }
154:     }
155: }