| 1 | package fr.sii.ogham.sms.builder.cloudhopper; | |
| 2 | ||
| 3 | import static com.cloudhopper.smpp.SmppConstants.STATUS_ALYBND; | |
| 4 | import static com.cloudhopper.smpp.SmppConstants.STATUS_INVPASWD; | |
| 5 | import static com.cloudhopper.smpp.SmppConstants.STATUS_INVSERTYP; | |
| 6 | import static com.cloudhopper.smpp.SmppConstants.STATUS_INVSYSID; | |
| 7 | import static fr.sii.ogham.core.util.ExceptionUtils.fatalJvmError; | |
| 8 | import static fr.sii.ogham.core.util.ExceptionUtils.hasAnyCause; | |
| 9 | import static java.util.Arrays.asList; | |
| 10 | ||
| 11 | import java.util.function.Predicate; | |
| 12 | ||
| 13 | import com.cloudhopper.commons.gsm.DataCoding; | |
| 14 | import com.cloudhopper.smpp.type.SmppBindException; | |
| 15 | ||
| 16 | import fr.sii.ogham.sms.exception.message.EncodingException; | |
| 17 | import fr.sii.ogham.sms.sender.impl.cloudhopper.exception.DataCodingException; | |
| 18 | import fr.sii.ogham.sms.sender.impl.cloudhopper.exception.MessagePreparationException; | |
| 19 | ||
| 20 | /** | |
| 21 | * | |
| 22 | * @author Aurélien Baudet | |
| 23 | * | |
| 24 | */ | |
| 25 | public final class CloudhopperRetryablePredicates { | |
| 26 | ||
| 27 | private CloudhopperRetryablePredicates() { | |
| 28 | super(); | |
| 29 | } | |
| 30 | ||
| 31 | /** | |
| 32 | * Default predicate used to indicate if the error raised by Cloudhopper is | |
| 33 | * fatal or not. It returns {@code true} if the error is not fatal (means | |
| 34 | * that a new retry can be attempted). It returns {@code false} if the error | |
| 35 | * is fatal and no retry must be attempted. | |
| 36 | * | |
| 37 | * <p> | |
| 38 | * Here is the list of cases where there should have no retries: | |
| 39 | * <ul> | |
| 40 | * <li>There is a fatal JVM {@link Error} (like {@link OutOfMemoryError} for | |
| 41 | * example).</li> | |
| 42 | * <li>A bind request has been sent to the SMSC and it has responded with an | |
| 43 | * error indicating that the credentials are invalid</li> | |
| 44 | * <li>A bind request has been sent to the SMSC and it has responded with an | |
| 45 | * error indicating that the {@code system_type} is invalid</li> | |
| 46 | * <li>A bind request has been sent to the SMSC and it has responded with an | |
| 47 | * error indicating that client is already bound</li> | |
| 48 | * </ul> | |
| 49 | * | |
| 50 | * | |
| 51 | * @param error | |
| 52 | * the error to analyze | |
| 53 | * @return true if a connect may be retried | |
| 54 | */ | |
| 55 | @SuppressWarnings("squid:S1126") | |
| 56 | public static boolean canRetryConnecting(Throwable error) { | |
| 57 |
8
1. canRetryConnecting : negated conditional → NO_COVERAGE 2. canRetryConnecting : negated conditional → NO_COVERAGE 3. canRetryConnecting : negated conditional → NO_COVERAGE 4. canRetryConnecting : negated conditional → NO_COVERAGE 5. canRetryConnecting : negated conditional → KILLED 6. canRetryConnecting : negated conditional → KILLED 7. canRetryConnecting : negated conditional → KILLED 8. canRetryConnecting : negated conditional → KILLED |
if (fatalJvmError(error) || invalidCredentials(error) || invalidSystemType(error) || alreadyBound(error)) { |
| 58 |
2
1. canRetryConnecting : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canRetryConnecting → NO_COVERAGE 2. canRetryConnecting : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canRetryConnecting → KILLED |
return false; |
| 59 | } | |
| 60 |
2
1. canRetryConnecting : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canRetryConnecting → NO_COVERAGE 2. canRetryConnecting : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canRetryConnecting → KILLED |
return true; |
| 61 | } | |
| 62 | ||
| 63 | /** | |
| 64 | * Checks whether the error has been raised because the SMSC has sent a | |
| 65 | * response to a bind request indicating that the credentials are invalid | |
| 66 | * (wrong {@code system_id} or {@code password}). | |
| 67 | * | |
| 68 | * <p> | |
| 69 | * If the credentials are invalid, there is no point in retrying to connect. | |
| 70 | * | |
| 71 | * @param error | |
| 72 | * the raised error | |
| 73 | * @return true if the error is issued due to a bind failure (wrong | |
| 74 | * credentials) | |
| 75 | */ | |
| 76 | public static boolean invalidCredentials(Throwable error) { | |
| 77 |
2
1. invalidCredentials : negated conditional → NO_COVERAGE 2. invalidCredentials : negated conditional → TIMED_OUT |
if (error instanceof SmppBindException) { |
| 78 |
4
1. invalidCredentials : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidCredentials → NO_COVERAGE 2. invalidCredentials : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidCredentials → SURVIVED 3. invalidCredentials : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidCredentials → NO_COVERAGE 4. invalidCredentials : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidCredentials → KILLED |
return isCommandStatus((SmppBindException) error, STATUS_INVPASWD, STATUS_INVSYSID); |
| 79 | } | |
| 80 |
2
1. invalidCredentials : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidCredentials → NO_COVERAGE 2. invalidCredentials : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidCredentials → KILLED |
return false; |
| 81 | } | |
| 82 | ||
| 83 | /** | |
| 84 | * Checks whether the error has been raised because the SMSC has sent a | |
| 85 | * response to a bind request indicating that the {@code system_type} field | |
| 86 | * is invalid. | |
| 87 | * | |
| 88 | * <p> | |
| 89 | * If the {@code system_type} field is invalid, there is no point in | |
| 90 | * retrying to connect. | |
| 91 | * | |
| 92 | * @param error | |
| 93 | * the raised error | |
| 94 | * @return true if the error is issued due to a bind failure (wrong | |
| 95 | * {@code system_type}) | |
| 96 | */ | |
| 97 | public static boolean invalidSystemType(Throwable error) { | |
| 98 |
2
1. invalidSystemType : negated conditional → NO_COVERAGE 2. invalidSystemType : negated conditional → KILLED |
if (error instanceof SmppBindException) { |
| 99 |
2
1. invalidSystemType : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidSystemType → NO_COVERAGE 2. invalidSystemType : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidSystemType → NO_COVERAGE |
return isCommandStatus((SmppBindException) error, STATUS_INVSERTYP); |
| 100 | } | |
| 101 |
2
1. invalidSystemType : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidSystemType → NO_COVERAGE 2. invalidSystemType : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::invalidSystemType → KILLED |
return false; |
| 102 | } | |
| 103 | ||
| 104 | /** | |
| 105 | * Checks whether the error has been raised because the SMSC has sent a | |
| 106 | * response to a bind request indicating that the client is already bound. | |
| 107 | * | |
| 108 | * <p> | |
| 109 | * If the client is already bound, there is no point in retrying to connect. | |
| 110 | * | |
| 111 | * @param error | |
| 112 | * the raised error | |
| 113 | * @return true if the error is issued due to a bind failure (already bound) | |
| 114 | */ | |
| 115 | public static boolean alreadyBound(Throwable error) { | |
| 116 |
2
1. alreadyBound : negated conditional → NO_COVERAGE 2. alreadyBound : negated conditional → KILLED |
if (error instanceof SmppBindException) { |
| 117 |
2
1. alreadyBound : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::alreadyBound → NO_COVERAGE 2. alreadyBound : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::alreadyBound → NO_COVERAGE |
return isCommandStatus((SmppBindException) error, STATUS_ALYBND); |
| 118 | } | |
| 119 |
2
1. alreadyBound : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::alreadyBound → NO_COVERAGE 2. alreadyBound : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::alreadyBound → KILLED |
return false; |
| 120 | } | |
| 121 | ||
| 122 | /** | |
| 123 | * Default predicate used to indicate if the error raised by Cloudhopper is | |
| 124 | * fatal or not. It returns {@code true} if the error is not fatal (means | |
| 125 | * that a new retry can be attempted). It returns {@code false} if the error | |
| 126 | * is fatal and no retry must be attempted. | |
| 127 | * | |
| 128 | * <p> | |
| 129 | * Here is the list of cases where there should have no retries: | |
| 130 | * <ul> | |
| 131 | * <li>There is a fatal JVM {@link Error} (like {@link OutOfMemoryError} for | |
| 132 | * example).</li> | |
| 133 | * <li>A bind request has been sent to the SMSC and it has responded with an | |
| 134 | * error indicating that the credentials are invalid</li> | |
| 135 | * <li>A bind request has been sent to the SMSC and it has responded with an | |
| 136 | * error indicating that the {@code system_type} is invalid</li> | |
| 137 | * <li>A bind request has been sent to the SMSC and it has responded with an | |
| 138 | * error indicating that client is already bound</li> | |
| 139 | * </ul> | |
| 140 | * | |
| 141 | * @return the predicate | |
| 142 | */ | |
| 143 | public static Predicate<Throwable> canRetryConnecting() { | |
| 144 |
2
1. canRetryConnecting : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canRetryConnecting → SURVIVED 2. canRetryConnecting : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canRetryConnecting → KILLED |
return CloudhopperRetryablePredicates::canRetryConnecting; |
| 145 | } | |
| 146 | ||
| 147 | /** | |
| 148 | * Predicate that skip retry if one of theses condition is met: | |
| 149 | * | |
| 150 | * If the error is due to a preparation error (not sending). In this case, | |
| 151 | * retrying will result in the same behavior so it will fail again: | |
| 152 | * <ul> | |
| 153 | * <li>Data coding couldn't be determined</li> | |
| 154 | * <li>Encoding couldn't be determined</li> | |
| 155 | * <li>Message preparation has failed</li> | |
| 156 | * </ul> | |
| 157 | * | |
| 158 | * <p> | |
| 159 | * In other situations, the message may be sent again. | |
| 160 | * | |
| 161 | * | |
| 162 | * @param error | |
| 163 | * the error to analyze | |
| 164 | * @return true if a connect may be retried | |
| 165 | */ | |
| 166 | @SuppressWarnings("squid:S1126") | |
| 167 | public static boolean canResendMessage(Throwable error) { | |
| 168 |
9
1. canResendMessage : negated conditional → SURVIVED 2. canResendMessage : negated conditional → NO_COVERAGE 3. canResendMessage : negated conditional → SURVIVED 4. canResendMessage : negated conditional → NO_COVERAGE 5. canResendMessage : negated conditional → SURVIVED 6. canResendMessage : negated conditional → NO_COVERAGE 7. canResendMessage : negated conditional → KILLED 8. canResendMessage : negated conditional → KILLED 9. canResendMessage : negated conditional → KILLED |
if (isDataCodingError(error) || isEncodingError(error) || messagePreparationFailed(error)) { |
| 169 |
2
1. canResendMessage : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → NO_COVERAGE 2. canResendMessage : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → SURVIVED |
return false; |
| 170 | } | |
| 171 | // @formatter:off | |
| 172 |
3
1. canResendMessage : negated conditional → SURVIVED 2. canResendMessage : negated conditional → NO_COVERAGE 3. canResendMessage : negated conditional → KILLED |
if (hasAnyCause(error, CloudhopperRetryablePredicates::isDataCodingError) |
| 173 |
3
1. canResendMessage : negated conditional → NO_COVERAGE 2. canResendMessage : negated conditional → SURVIVED 3. canResendMessage : negated conditional → KILLED |
|| hasAnyCause(error, CloudhopperRetryablePredicates::isEncodingError) |
| 174 |
3
1. canResendMessage : negated conditional → NO_COVERAGE 2. canResendMessage : negated conditional → KILLED 3. canResendMessage : negated conditional → KILLED |
|| hasAnyCause(error, CloudhopperRetryablePredicates::messagePreparationFailed)) { |
| 175 |
2
1. canResendMessage : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → NO_COVERAGE 2. canResendMessage : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED |
return false; |
| 176 | } | |
| 177 | // @formatter:on | |
| 178 |
3
1. canResendMessage : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → SURVIVED 2. canResendMessage : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → NO_COVERAGE 3. canResendMessage : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED |
return true; |
| 179 | } | |
| 180 | ||
| 181 | /** | |
| 182 | * Indicates if the error is due to a {@link DataCoding} detection error. | |
| 183 | * | |
| 184 | * <p> | |
| 185 | * In this case, retrying will lead to the same error. | |
| 186 | * | |
| 187 | * @param error | |
| 188 | * the error to analyze | |
| 189 | * @return true if it is a data coding error | |
| 190 | */ | |
| 191 | public static boolean isDataCodingError(Throwable error) { | |
| 192 |
5
1. isDataCodingError : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isDataCodingError → SURVIVED 2. isDataCodingError : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isDataCodingError → NO_COVERAGE 3. isDataCodingError : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isDataCodingError → NO_COVERAGE 4. isDataCodingError : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isDataCodingError → SURVIVED 5. isDataCodingError : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isDataCodingError → KILLED |
return error instanceof DataCodingException; |
| 193 | } | |
| 194 | ||
| 195 | /** | |
| 196 | * Indicates if the error is due to an encoding detection error. | |
| 197 | * | |
| 198 | * <p> | |
| 199 | * In this case, retrying will lead to the same error. | |
| 200 | * | |
| 201 | * @param error | |
| 202 | * the error to analyze | |
| 203 | * @return true if it is an encoding error | |
| 204 | */ | |
| 205 | public static boolean isEncodingError(Throwable error) { | |
| 206 |
5
1. isEncodingError : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isEncodingError → NO_COVERAGE 2. isEncodingError : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isEncodingError → SURVIVED 3. isEncodingError : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isEncodingError → SURVIVED 4. isEncodingError : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isEncodingError → NO_COVERAGE 5. isEncodingError : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isEncodingError → KILLED |
return error instanceof EncodingException; |
| 207 | } | |
| 208 | ||
| 209 | /** | |
| 210 | * Indicates if the error is due to an error during preparation of the | |
| 211 | * message. | |
| 212 | * | |
| 213 | * <p> | |
| 214 | * In this case, retrying will lead to the same error. | |
| 215 | * | |
| 216 | * @param error | |
| 217 | * the error to analyze | |
| 218 | * @return true if it is a preparation error | |
| 219 | */ | |
| 220 | public static boolean messagePreparationFailed(Throwable error) { | |
| 221 |
6
1. messagePreparationFailed : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::messagePreparationFailed → SURVIVED 2. messagePreparationFailed : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::messagePreparationFailed → NO_COVERAGE 3. messagePreparationFailed : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::messagePreparationFailed → NO_COVERAGE 4. messagePreparationFailed : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::messagePreparationFailed → SURVIVED 5. messagePreparationFailed : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::messagePreparationFailed → KILLED 6. messagePreparationFailed : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::messagePreparationFailed → KILLED |
return error instanceof MessagePreparationException; |
| 222 | } | |
| 223 | ||
| 224 | /** | |
| 225 | * Predicate that skip retry if one of theses condition is met: | |
| 226 | * | |
| 227 | * If the error is due to a preparation error (not sending). In this case, | |
| 228 | * retrying will result in the same behavior so it will fail again: | |
| 229 | * <ul> | |
| 230 | * <li>Data coding couldn't be determined</li> | |
| 231 | * <li>Encoding couldn't be determined</li> | |
| 232 | * <li>Message preparation has failed</li> | |
| 233 | * </ul> | |
| 234 | * | |
| 235 | * <p> | |
| 236 | * In other situations, the message may be sent again. | |
| 237 | * | |
| 238 | * | |
| 239 | * @return the predicate that indicates if the message can be sent again | |
| 240 | */ | |
| 241 | public static Predicate<Throwable> canResendMessage() { | |
| 242 |
5
1. canResendMessage : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED 2. canResendMessage : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED 3. canResendMessage : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED 4. canResendMessage : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED 5. canResendMessage : replaced return value with null for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::canResendMessage → KILLED |
return CloudhopperRetryablePredicates::canResendMessage; |
| 243 | } | |
| 244 | ||
| 245 | private static boolean isCommandStatus(SmppBindException e, Integer... statuses) { | |
| 246 |
4
1. isCommandStatus : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isCommandStatus → NO_COVERAGE 2. isCommandStatus : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isCommandStatus → NO_COVERAGE 3. isCommandStatus : replaced boolean return with true for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isCommandStatus → SURVIVED 4. isCommandStatus : replaced boolean return with false for fr/sii/ogham/sms/builder/cloudhopper/CloudhopperRetryablePredicates::isCommandStatus → KILLED |
return asList(statuses).contains(e.getBindResponse().getCommandStatus()); |
| 247 | } | |
| 248 | } | |
Mutations | ||
| 57 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
| 58 |
1.1 2.2 |
|
| 60 |
1.1 2.2 |
|
| 77 |
1.1 2.2 |
|
| 78 |
1.1 2.2 3.3 4.4 |
|
| 80 |
1.1 2.2 |
|
| 98 |
1.1 2.2 |
|
| 99 |
1.1 2.2 |
|
| 101 |
1.1 2.2 |
|
| 116 |
1.1 2.2 |
|
| 117 |
1.1 2.2 |
|
| 119 |
1.1 2.2 |
|
| 144 |
1.1 2.2 |
|
| 168 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 |
|
| 169 |
1.1 2.2 |
|
| 172 |
1.1 2.2 3.3 |
|
| 173 |
1.1 2.2 3.3 |
|
| 174 |
1.1 2.2 3.3 |
|
| 175 |
1.1 2.2 |
|
| 178 |
1.1 2.2 3.3 |
|
| 192 |
1.1 2.2 3.3 4.4 5.5 |
|
| 206 |
1.1 2.2 3.3 4.4 5.5 |
|
| 221 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
| 242 |
1.1 2.2 3.3 4.4 5.5 |
|
| 246 |
1.1 2.2 3.3 4.4 |