ContentsIndexHome
Body Source
Body Source
  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: }