1 | package fr.sii.ogham.sms.builder; | |
2 | ||
3 | import org.slf4j.Logger; | |
4 | import org.slf4j.LoggerFactory; | |
5 | ||
6 | import fr.sii.ogham.core.async.Awaiter; | |
7 | import fr.sii.ogham.core.builder.ActivableAtRuntime; | |
8 | import fr.sii.ogham.core.builder.Builder; | |
9 | import fr.sii.ogham.core.builder.MessagingBuilder; | |
10 | import fr.sii.ogham.core.builder.condition.RequiredClass; | |
11 | import fr.sii.ogham.core.builder.condition.RequiredClasses; | |
12 | import fr.sii.ogham.core.builder.condition.RequiredProperties; | |
13 | import fr.sii.ogham.core.builder.condition.RequiredProperty; | |
14 | import fr.sii.ogham.core.builder.configurer.MessagingConfigurer; | |
15 | import fr.sii.ogham.core.builder.context.BuildContext; | |
16 | import fr.sii.ogham.core.builder.retry.RetryBuilder; | |
17 | import fr.sii.ogham.core.builder.sender.SenderImplementationBuilderHelper; | |
18 | import fr.sii.ogham.core.builder.template.DetectorBuilder; | |
19 | import fr.sii.ogham.core.builder.template.TemplateBuilderHelper; | |
20 | import fr.sii.ogham.core.builder.template.VariantBuilder; | |
21 | import fr.sii.ogham.core.condition.Condition; | |
22 | import fr.sii.ogham.core.condition.fluent.MessageConditions; | |
23 | import fr.sii.ogham.core.filler.MessageFiller; | |
24 | import fr.sii.ogham.core.fluent.AbstractParent; | |
25 | import fr.sii.ogham.core.message.content.MultiTemplateContent; | |
26 | import fr.sii.ogham.core.message.content.Variant; | |
27 | import fr.sii.ogham.core.retry.ExponentialDelayRetry; | |
28 | import fr.sii.ogham.core.retry.FixedDelayRetry; | |
29 | import fr.sii.ogham.core.retry.FixedIntervalRetry; | |
30 | import fr.sii.ogham.core.retry.PerExecutionDelayRetry; | |
31 | import fr.sii.ogham.core.retry.RetryExecutor; | |
32 | import fr.sii.ogham.core.sender.AutoRetrySender; | |
33 | import fr.sii.ogham.core.sender.ConditionalSender; | |
34 | import fr.sii.ogham.core.sender.ContentTranslatorSender; | |
35 | import fr.sii.ogham.core.sender.FillerSender; | |
36 | import fr.sii.ogham.core.sender.MessageSender; | |
37 | import fr.sii.ogham.core.template.parser.TemplateParser; | |
38 | import fr.sii.ogham.core.translator.content.ContentTranslator; | |
39 | import fr.sii.ogham.core.translator.content.EveryContentTranslator; | |
40 | import fr.sii.ogham.core.translator.content.TemplateContentTranslator; | |
41 | import fr.sii.ogham.core.util.BuilderUtils; | |
42 | import fr.sii.ogham.sms.message.PhoneNumber; | |
43 | import fr.sii.ogham.sms.message.Sms; | |
44 | import fr.sii.ogham.sms.message.addressing.AddressedPhoneNumber; | |
45 | import fr.sii.ogham.sms.sender.PhoneNumberTranslatorSender; | |
46 | import fr.sii.ogham.sms.sender.SmsSender; | |
47 | ||
48 | /** | |
49 | * Configures how to send {@link Sms} messages. It allows to: | |
50 | * <ul> | |
51 | * <li>register and configure several sender implementations</li> | |
52 | * <li>register and configure several template engines for parsing templates as | |
53 | * message content</li> | |
54 | * <li>configure handling of missing {@link Sms} information</li> | |
55 | * <li>configure number format handling</li> | |
56 | * </ul> | |
57 | * | |
58 | * You can send a {@link Sms} using the minimal behavior and using Cloudhopper | |
59 | * implementation: | |
60 | * | |
61 | * <pre> | |
62 | * <code> | |
63 | * // Instantiate the messaging service | |
64 | * MessagingService service = new MessagingBuilder() | |
65 | * .sms() | |
66 | * .sender(CloudhopperBuilder.class) // enable SMS sending using Cloudhopper | |
67 | * .host("your SMPP server host") | |
68 | * .port("your SMPP server port") | |
69 | * .systemId("your SMPP system_id") | |
70 | * .password("an optional password") | |
71 | * .and() | |
72 | * .and() | |
73 | * .build(); | |
74 | * // send the sms | |
75 | * service.send(new Sms() | |
76 | * .from("sender phone number") | |
77 | * .content("sms content") | |
78 | * .to("recipient phone number")); | |
79 | * </code> | |
80 | * </pre> | |
81 | * | |
82 | * You can also send a {@link Sms} using a template (using Freemarker for | |
83 | * example): | |
84 | * | |
85 | * The Freemarker template ("sms/sample.txt.ftl"): | |
86 | * | |
87 | * <pre> | |
88 | * Sms content with variables: ${name} ${value} | |
89 | * </pre> | |
90 | * | |
91 | * Then you can send the {@link Sms} like this: | |
92 | * | |
93 | * <pre> | |
94 | * <code> | |
95 | * // Instantiate the messaging service | |
96 | * MessagingService service = new MessagingBuilder() | |
97 | * .sms() | |
98 | * .sender(CloudhopperBuilder.class) // enable SMS sending using Cloudhopper | |
99 | * .host("your SMPP server host") | |
100 | * .port("your SMPP server port") | |
101 | * .systemId("your SMPP system_id") | |
102 | * .password("an optional password") | |
103 | * .and() | |
104 | * .and() | |
105 | * .template(FreemarkerSmsBuilder.class) // enable templating using Freemarker | |
106 | * .classpath() | |
107 | * .lookup("classpath:") // search resources/templates in the classpath if a path is prefixed by "classpath:" | |
108 | * .and() | |
109 | * .and() | |
110 | * .build(); | |
111 | * // send the sms | |
112 | * service.send(new Sms() | |
113 | * .from("sender phone number") | |
114 | * .content(new TemplateContent("classpath:sms/sample.txt.ftl", new SampleBean("foo", 42))) | |
115 | * .to("recipient phone number")); | |
116 | * </code> | |
117 | * </pre> | |
118 | * | |
119 | * <p> | |
120 | * Instead of explicitly configures SMPP host/port/system_id/password in your | |
121 | * code, it could be better to externalize the configuration in a properties | |
122 | * file for example (for example a file named "sms.properties" in the | |
123 | * classpath). The previous example becomes: | |
124 | * | |
125 | * <pre> | |
126 | * <code> | |
127 | * // Instantiate the messaging service | |
128 | * MessagingService service = new MessagingBuilder() | |
129 | * .environment() | |
130 | * .properties("sms.properties") | |
131 | * .and() | |
132 | * .sms() | |
133 | * .sender(CloudhopperBuilder.class) // enable SMS sending using Cloudhopper | |
134 | * .host("${smpp.host}") | |
135 | * .port("${smpp.port}") | |
136 | * .systemId("${smpp.system-id}") | |
137 | * .password("${smpp.password}") | |
138 | * .and() | |
139 | * .and() | |
140 | * .template(FreemarkerSmsBuilder.class) // enable templating using Freemarker | |
141 | * .classpath() | |
142 | * .lookup("classpath:") // search resources/templates in the classpath if a path is prefixed by "classpath:" | |
143 | * .and() | |
144 | * .and() | |
145 | * .build(); | |
146 | * // send the sms | |
147 | * service.send(new Sms() | |
148 | * .from("sender phone number") | |
149 | * .content(new TemplateContent("classpath:sms/sample.txt.ftl", new SampleBean("foo", 42))) | |
150 | * .to("recipient phone number")); | |
151 | * </code> | |
152 | * </pre> | |
153 | * | |
154 | * The content of the file "sms.properties": | |
155 | * | |
156 | * <pre> | |
157 | * smpp.host=your SMPP server host | |
158 | * smpp.port=your SMPP server port | |
159 | * smpp.system-id=your SMPP system_id | |
160 | * smpp.password=an optional password | |
161 | * </pre> | |
162 | * | |
163 | * | |
164 | * Some fields of the SMS may be automatically filled by a default value if they | |
165 | * are not defined. For example, the sender phone number could be configured | |
166 | * only once for your application: | |
167 | * | |
168 | * <pre> | |
169 | * <code> | |
170 | * // Instantiate the messaging service | |
171 | * MessagingService service = new MessagingBuilder() | |
172 | * .environment() | |
173 | * .properties("sms.properties") | |
174 | * .and() | |
175 | * .sms() | |
176 | * .sender(CloudhopperBuilder.class) // enable SMS sending using Cloudhopper | |
177 | * .host().properties("${smpp.host}").and() | |
178 | * .port().properties("${smpp.port}").and() | |
179 | * .systemId().properties("${smpp.system-id}").and() | |
180 | * .password().properties("${smpp.password}").and() | |
181 | * .and() | |
182 | * .autofill() // enables and configures autofilling | |
183 | * .from() | |
184 | * .defaultValue().properties("${sms.sender.number}").and() | |
185 | * .and() | |
186 | * .and() | |
187 | * .and() | |
188 | * .template(FreemarkerSmsBuilder.class) // enable templating using Freemarker | |
189 | * .classpath() | |
190 | * .lookup("classpath:") // search resources/templates in the classpath if a path is prefixed by "classpath:" | |
191 | * .and() | |
192 | * .and() | |
193 | * .build(); | |
194 | * // send the sms (now the sender phone number can be omitted) | |
195 | * service.send(new Sms() | |
196 | * .content(new TemplateContent("classpath:sms/sample.txt.ftl", new SampleBean("foo", 42))) | |
197 | * .to("recipient phone number")); | |
198 | * </code> | |
199 | * </pre> | |
200 | * | |
201 | * The new content of the file "sms.properties": | |
202 | * | |
203 | * <pre> | |
204 | * smpp.host=your SMPP server host | |
205 | * smpp.port=your SMPP server port | |
206 | * smpp.system-id=your SMPP system_id | |
207 | * smpp.password=an optional password | |
208 | * sms.sender.number=the sender phone number | |
209 | * </pre> | |
210 | * | |
211 | * <p> | |
212 | * All the previous examples are provided to understand what can be configured. | |
213 | * Hopefully, Ogham provides auto-configuration with a default behavior that | |
214 | * fits 95% of usages. This auto-configuration is provided by | |
215 | * {@link MessagingConfigurer}s. Those configurers are automatically applied | |
216 | * when using predefined {@link MessagingBuilder}s like | |
217 | * {@link MessagingBuilder#minimal()} and {@link MessagingBuilder#standard()}. | |
218 | * | |
219 | * The previous sample using standard configuration becomes: | |
220 | * | |
221 | * <pre> | |
222 | * <code> | |
223 | * // Instantiate the messaging service | |
224 | * MessagingService service = MessagingBuilder.standard() | |
225 | * .environment() | |
226 | * .properties("sms.properties") | |
227 | * .and() | |
228 | * .build(); | |
229 | * // send the sms | |
230 | * service.send(new Sms() | |
231 | * .content(new TemplateContent("classpath:sms/sample.txt.ftl", new SampleBean("foo", 42))) | |
232 | * .to("recipient phone number")); | |
233 | * </code> | |
234 | * </pre> | |
235 | * | |
236 | * The new content of the file "sms.properties": | |
237 | * | |
238 | * <pre> | |
239 | * ogham.sms.smpp.host=your SMPP server host | |
240 | * ogham.sms.smpp.port=your SMPP server port | |
241 | * ogham.sms.smpp.system-id=your SMPP system_id | |
242 | * ogham.sms.smpp.password=an optional password | |
243 | * ogham.sms.from.default-value=the sender phone number | |
244 | * </pre> | |
245 | * | |
246 | * <p> | |
247 | * You can also use the auto-configuration for benefit from default behaviors | |
248 | * and override some behaviors for your needs: | |
249 | * | |
250 | * <pre> | |
251 | * <code> | |
252 | * // Instantiate the messaging service | |
253 | * MessagingService service = MessagingBuilder.standard() | |
254 | * .environment() | |
255 | * .properties("sms.properties") | |
256 | * .and() | |
257 | * .sms() | |
258 | * .autofill() | |
259 | * .from() | |
260 | * .defaultValue().properties("${sms.sender.number}").and() // overrides default sender phone number property | |
261 | * .and() | |
262 | * .and() | |
263 | * .and() | |
264 | * .build(); | |
265 | * // send the sms | |
266 | * service.send(new Sms() | |
267 | * .content(new TemplateContent("classpath:sms/sample.txt.ftl", new SampleBean("foo", 42))) | |
268 | * .to("recipient phone number")); | |
269 | * </code> | |
270 | * </pre> | |
271 | * | |
272 | * The new content of the file "sms.properties": | |
273 | * | |
274 | * <pre> | |
275 | * ogham.sms.smpp.host=your SMPP server host | |
276 | * ogham.sms.smpp.port=your SMPP server port | |
277 | * ogham.sms.smpp.system-id=your SMPP system_id | |
278 | * ogham.sms.smpp.password=an optional password | |
279 | * sms.sender.number=the sender phone number | |
280 | * </pre> | |
281 | * | |
282 | * @author Aurélien Baudet | |
283 | * | |
284 | */ | |
285 | public class SmsBuilder extends AbstractParent<MessagingBuilder> implements Builder<ConditionalSender> { | |
286 | private static final Logger LOG = LoggerFactory.getLogger(SmsBuilder.class); | |
287 | ||
288 | private final BuildContext buildContext; | |
289 | private final TemplateBuilderHelper<SmsBuilder> templateBuilderHelper; | |
290 | private final SenderImplementationBuilderHelper<SmsBuilder> senderBuilderHelper; | |
291 | private AutofillSmsBuilder autofillBuilder; | |
292 | private PhoneNumbersBuilder phoneNumbersBuilder; | |
293 | private RetryBuilder<SmsBuilder> retryBuilder; | |
294 | ||
295 | /** | |
296 | * Initializes the builder with a parent builder. The parent builder is used | |
297 | * when calling {@link #and()} method. The {@link BuildContext} is used to | |
298 | * evaluate properties when {@link #build()} method is called. | |
299 | * | |
300 | * @param parent | |
301 | * the parent builder | |
302 | * @param buildContext | |
303 | * for registering instances and property evaluation | |
304 | */ | |
305 | public SmsBuilder(MessagingBuilder parent, BuildContext buildContext) { | |
306 | super(parent); | |
307 | this.buildContext = buildContext; | |
308 | templateBuilderHelper = new TemplateBuilderHelper<>(this, buildContext); | |
309 | senderBuilderHelper = new SenderImplementationBuilderHelper<>(this, buildContext); | |
310 | } | |
311 | ||
312 | /** | |
313 | * Configures how Ogham will add default values to the {@link Sms} if some | |
314 | * information is missing. | |
315 | * | |
316 | * If sender phone number is missing, a default one can be defined in | |
317 | * configuration properties. | |
318 | * | |
319 | * If recipient phone number is missing, a default one can be defined in | |
320 | * configuration properties. | |
321 | * | |
322 | * <pre> | |
323 | * <code> | |
324 | * builder | |
325 | * .autofill() | |
326 | * .from() | |
327 | * .defaultValue().properties("${ogham.sms.from.default-value}").and() | |
328 | * .and() | |
329 | * .to() | |
330 | * .defaultValue().properties("${ogham.sms.to.default-value}").and() | |
331 | * .and() | |
332 | * .and() | |
333 | * </code> | |
334 | * </pre> | |
335 | * | |
336 | * @return the builder to configure autofilling of SMS | |
337 | */ | |
338 | public AutofillSmsBuilder autofill() { | |
339 |
8
1. autofill : negated conditional → NO_COVERAGE 2. autofill : negated conditional → KILLED 3. autofill : negated conditional → KILLED 4. autofill : negated conditional → KILLED 5. autofill : negated conditional → KILLED 6. autofill : negated conditional → KILLED 7. autofill : negated conditional → KILLED 8. autofill : negated conditional → KILLED |
if (autofillBuilder == null) { |
340 | autofillBuilder = new AutofillSmsBuilder(this, buildContext); | |
341 | } | |
342 |
8
1. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → NO_COVERAGE 2. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED 3. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED 4. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED 5. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED 6. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED 7. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED 8. autofill : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autofill → KILLED |
return autofillBuilder; |
343 | } | |
344 | ||
345 | /** | |
346 | * Configures the phone number conversions (from a {@link PhoneNumber} to an | |
347 | * {@link AddressedPhoneNumber}). | |
348 | * | |
349 | * The {@link PhoneNumber} is used by the developer to provide a simple | |
350 | * phone number without knowing how phone number works (no need to handle | |
351 | * formats, addressing, countries...). The {@link AddressedPhoneNumber} is | |
352 | * used by Ogham implementations to have a phone number that is usable by a | |
353 | * technical system. | |
354 | * | |
355 | * For example: | |
356 | * | |
357 | * <pre> | |
358 | * <code> | |
359 | * builder | |
360 | * .numbers() | |
361 | * .from() | |
362 | * .format() | |
363 | * .alphanumericCode().properties("${ogham.sms.from.alphanumeric-code-format.enable}").defaultValue(true).and() | |
364 | * .shortCode().properties("${ogham.sms.from.short-code-format.enable}").defaultValue(true).and() | |
365 | * .internationalNumber().properties("${ogham.sms.from.international-format.enable}").defaultValue(true).and() | |
366 | * .and() | |
367 | * .and() | |
368 | * .to() | |
369 | * .format() | |
370 | * .internationalNumber().properties("${ogham.sms.to.international-format.enable}").defaultValue(true); | |
371 | * </code> | |
372 | * </pre> | |
373 | * | |
374 | * @return the builder to configure phone number formats | |
375 | */ | |
376 | public PhoneNumbersBuilder numbers() { | |
377 |
8
1. numbers : negated conditional → NO_COVERAGE 2. numbers : negated conditional → KILLED 3. numbers : negated conditional → KILLED 4. numbers : negated conditional → KILLED 5. numbers : negated conditional → KILLED 6. numbers : negated conditional → KILLED 7. numbers : negated conditional → KILLED 8. numbers : negated conditional → KILLED |
if (phoneNumbersBuilder == null) { |
378 | phoneNumbersBuilder = new PhoneNumbersBuilder(this, buildContext); | |
379 | } | |
380 |
8
1. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → NO_COVERAGE 2. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED 3. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED 4. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED 5. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED 6. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED 7. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED 8. numbers : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::numbers → KILLED |
return phoneNumbersBuilder; |
381 | } | |
382 | ||
383 | /** | |
384 | * Registers and configures a {@link TemplateParser} through a dedicated | |
385 | * builder. | |
386 | * | |
387 | * For example: | |
388 | * | |
389 | * <pre> | |
390 | * .register(ThymeleafSmsBuilder.class) | |
391 | * .detector(new ThymeleafEngineDetector()); | |
392 | * </pre> | |
393 | * | |
394 | * <p> | |
395 | * Your {@link Builder} may implement {@link VariantBuilder} to handle | |
396 | * template {@link Variant}s (used for {@link MultiTemplateContent} that | |
397 | * provide a single path to templates with different extensions for | |
398 | * example). | |
399 | * </p> | |
400 | * | |
401 | * <p> | |
402 | * Your {@link Builder} may also implement {@link DetectorBuilder} in order | |
403 | * to indicate which kind of templates your {@link TemplateParser} is able | |
404 | * to parse. If your template parse is able to parse any template file you | |
405 | * are using, you may not need to implement {@link DetectorBuilder}. | |
406 | * </p> | |
407 | * | |
408 | * <p> | |
409 | * In order to be able to keep chaining, you builder instance may provide a | |
410 | * constructor with two arguments: | |
411 | * <ul> | |
412 | * <li>The type of the parent builder ({@code <P>})</li> | |
413 | * <li>The {@link BuildContext} instance</li> | |
414 | * </ul> | |
415 | * If you don't care about chaining, just provide a default constructor. | |
416 | * | |
417 | * @param builderClass | |
418 | * the builder class to instantiate | |
419 | * @param <T> | |
420 | * the type of the builder | |
421 | * @return the builder to configure the implementation | |
422 | */ | |
423 | public <T extends Builder<? extends TemplateParser>> T template(Class<T> builderClass) { | |
424 |
4
1. template : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::template → NO_COVERAGE 2. template : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::template → KILLED 3. template : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::template → KILLED 4. template : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::template → KILLED |
return templateBuilderHelper.register(builderClass); |
425 | } | |
426 | ||
427 | /** | |
428 | * Registers a custom message sender implementation. | |
429 | * | |
430 | * <p> | |
431 | * If your custom implementation is annotated by one or several of: | |
432 | * <ul> | |
433 | * <li>{@link RequiredClass}</li> | |
434 | * <li>{@link RequiredProperty}</li> | |
435 | * <li>{@link RequiredClasses}</li> | |
436 | * <li>{@link RequiredProperties}</li> | |
437 | * </ul> | |
438 | * Then if condition evaluation returns true, your implementation will be | |
439 | * used. If you provide several annotations, your implementation will be | |
440 | * used only if all conditions are met (and operator). | |
441 | * | |
442 | * <p> | |
443 | * If your custom implementation implements {@link ActivableAtRuntime}, and | |
444 | * the provided condition evaluation returns true, then your implementation | |
445 | * will be used. | |
446 | * | |
447 | * See {@link MessageConditions} to build your condition. | |
448 | * </p> | |
449 | * | |
450 | * <p> | |
451 | * If neither annotations nor implementation of {@link ActivableAtRuntime} | |
452 | * is used, then your custom implementation will be always used. All other | |
453 | * implementations (even standard ones) will never be used. | |
454 | * </p> | |
455 | * | |
456 | * @param sender | |
457 | * the sender to register | |
458 | * @return this instance for fluent chaining | |
459 | */ | |
460 | public SmsBuilder customSender(MessageSender sender) { | |
461 |
4
1. customSender : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::customSender → NO_COVERAGE 2. customSender : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::customSender → KILLED 3. customSender : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::customSender → KILLED 4. customSender : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::customSender → KILLED |
senderBuilderHelper.customSender(sender); |
462 |
3
1. customSender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::customSender → NO_COVERAGE 2. customSender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::customSender → SURVIVED 3. customSender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::customSender → KILLED |
return this; |
463 | } | |
464 | ||
465 | /** | |
466 | * Registers and configures sender through a dedicated builder. | |
467 | * | |
468 | * For example: | |
469 | * | |
470 | * <pre> | |
471 | * .sender(CloudhopperBuilder.class) | |
472 | * .host("localhost"); | |
473 | * </pre> | |
474 | * | |
475 | * <p> | |
476 | * If your custom builder is annotated by one or several of: | |
477 | * <ul> | |
478 | * <li>{@link RequiredClass}</li> | |
479 | * <li>{@link RequiredProperty}</li> | |
480 | * <li>{@link RequiredClasses}</li> | |
481 | * <li>{@link RequiredProperties}</li> | |
482 | * </ul> | |
483 | * Then if condition evaluation returns true, your built implementation will | |
484 | * be used. If you provide several annotations, your built implementation | |
485 | * will be used only if all conditions are met (and operator). | |
486 | * | |
487 | * <p> | |
488 | * If your custom builder implements {@link ActivableAtRuntime}, and the | |
489 | * provided condition evaluation returns true, then your built | |
490 | * implementation will be used. | |
491 | * | |
492 | * See {@link MessageConditions} to build your condition. | |
493 | * </p> | |
494 | * | |
495 | * <p> | |
496 | * If neither annotations nor implementation of {@link ActivableAtRuntime} | |
497 | * is used, then your built implementation will be always used. All other | |
498 | * implementations (even standard ones) will never be used. | |
499 | * </p> | |
500 | * | |
501 | * <p> | |
502 | * In order to be able to keep chaining, you builder instance may provide a | |
503 | * constructor with one argument with the type of the parent builder | |
504 | * ({@link SmsBuilder}). If you don't care about chaining, just provide a | |
505 | * default constructor. | |
506 | * </p> | |
507 | * | |
508 | * <p> | |
509 | * Your builder may return {@code null} when calling | |
510 | * {@link Builder#build()}. In this case it means that your implementation | |
511 | * can't be used due to current environment. Your implementation is then not | |
512 | * registered. | |
513 | * </p> | |
514 | * | |
515 | * @param builderClass | |
516 | * the builder class to instantiate | |
517 | * @param <T> | |
518 | * the type of the builder | |
519 | * @return the builder to configure the implementation | |
520 | */ | |
521 | public <T extends Builder<? extends MessageSender>> T sender(Class<T> builderClass) { | |
522 |
6
1. sender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::sender → NO_COVERAGE 2. sender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::sender → KILLED 3. sender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::sender → KILLED 4. sender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::sender → KILLED 5. sender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::sender → KILLED 6. sender : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::sender → KILLED |
return senderBuilderHelper.register(builderClass); |
523 | } | |
524 | ||
525 | /** | |
526 | * Configure automatic retry if message couldn't be sent. | |
527 | * | |
528 | * | |
529 | * For example: | |
530 | * | |
531 | * <pre> | |
532 | * .autoRetry() | |
533 | * .fixedDelay() | |
534 | * .maxRetries().properties("${ogham.sms.send-retry.max-attempts}").and() | |
535 | * .delay().properties("${ogham.sms.send-retry.delay-between-attempts}") | |
536 | * </pre> | |
537 | * | |
538 | * | |
539 | * <p> | |
540 | * This builder lets you configure: | |
541 | * <ul> | |
542 | * <li>The retry strategy: | |
543 | * <ul> | |
544 | * <li>{@link FixedDelayRetry}: wait for a fixed delay after the last | |
545 | * failure</li> | |
546 | * <li>{@link FixedIntervalRetry}: wait for a fixed delay between executions | |
547 | * (do not wait for the end of the action)</li> | |
548 | * <li>{@link ExponentialDelayRetry}: start with a delay, the next delay | |
549 | * will be doubled on so on</li> | |
550 | * <li>{@link PerExecutionDelayRetry}: provide a custom delay for each | |
551 | * execution</li> | |
552 | * </ul> | |
553 | * </li> | |
554 | * <li>The {@link RetryExecutor} implementation</li> | |
555 | * <li>The {@link Awaiter} implementation</li> | |
556 | * <li>The {@link Condition} used to determine if the raised error should | |
557 | * trigger a retry or not</li> | |
558 | * </ul> | |
559 | * | |
560 | * @return the builder to configure retry management | |
561 | */ | |
562 | public RetryBuilder<SmsBuilder> autoRetry() { | |
563 |
8
1. autoRetry : negated conditional → NO_COVERAGE 2. autoRetry : negated conditional → KILLED 3. autoRetry : negated conditional → KILLED 4. autoRetry : negated conditional → KILLED 5. autoRetry : negated conditional → KILLED 6. autoRetry : negated conditional → KILLED 7. autoRetry : negated conditional → KILLED 8. autoRetry : negated conditional → KILLED |
if (retryBuilder == null) { |
564 | retryBuilder = new RetryBuilder<>(this, buildContext); | |
565 | } | |
566 |
8
1. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → NO_COVERAGE 2. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED 3. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED 4. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED 5. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED 6. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED 7. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED 8. autoRetry : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::autoRetry → KILLED |
return retryBuilder; |
567 | } | |
568 | ||
569 | @Override | |
570 | public ConditionalSender build() { | |
571 | SmsSender smsSender = buildContext.register(new SmsSender()); | |
572 | ConditionalSender sender = smsSender; | |
573 |
8
1. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → SURVIVED 2. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → NO_COVERAGE 3. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → KILLED 4. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → KILLED 5. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → KILLED 6. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → KILLED 7. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → KILLED 8. build : removed call to fr/sii/ogham/core/builder/sender/SenderImplementationBuilderHelper::addSenders → KILLED |
senderBuilderHelper.addSenders(smsSender); |
574 |
5
1. build : negated conditional → SURVIVED 2. build : negated conditional → NO_COVERAGE 3. build : negated conditional → KILLED 4. build : negated conditional → KILLED 5. build : negated conditional → KILLED |
if (templateBuilderHelper.hasRegisteredTemplates()) { |
575 | ContentTranslator translator = buildContentTranslator(); | |
576 | LOG.debug("Content translation enabled {}", translator); | |
577 | sender = buildContext.register(new ContentTranslatorSender(translator, sender)); | |
578 | } | |
579 |
3
1. build : negated conditional → SURVIVED 2. build : negated conditional → NO_COVERAGE 3. build : negated conditional → KILLED |
if (phoneNumbersBuilder != null) { |
580 | PhoneNumberTranslatorPair pair = phoneNumbersBuilder.build(); | |
581 | sender = buildContext.register(new PhoneNumberTranslatorSender(pair.getSender(), pair.getRecipient(), sender)); | |
582 | } | |
583 |
3
1. build : negated conditional → NO_COVERAGE 2. build : negated conditional → SURVIVED 3. build : negated conditional → KILLED |
if (autofillBuilder != null) { |
584 | MessageFiller messageFiller = autofillBuilder.build(); | |
585 | LOG.debug("Automatic filling of message enabled {}", messageFiller); | |
586 | sender = buildContext.register(new FillerSender(messageFiller, sender)); | |
587 | } | |
588 | RetryExecutor retryExecutor = BuilderUtils.build(retryBuilder); | |
589 |
7
1. build : negated conditional → NO_COVERAGE 2. build : negated conditional → SURVIVED 3. build : negated conditional → KILLED 4. build : negated conditional → KILLED 5. build : negated conditional → KILLED 6. build : negated conditional → KILLED 7. build : negated conditional → KILLED |
if (retryExecutor != null) { |
590 | LOG.debug("Automatic retry of message sending enabled {}", retryExecutor); | |
591 | sender = buildContext.register(new AutoRetrySender(sender, retryExecutor)); | |
592 | } | |
593 |
8
1. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → NO_COVERAGE 2. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → SURVIVED 3. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → KILLED 4. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → KILLED 5. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → KILLED 6. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → KILLED 7. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → KILLED 8. build : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::build → KILLED |
return sender; |
594 | } | |
595 | ||
596 | private ContentTranslator buildContentTranslator() { | |
597 | EveryContentTranslator translator = buildContext.register(new EveryContentTranslator()); | |
598 |
4
1. buildContentTranslator : removed call to fr/sii/ogham/sms/builder/SmsBuilder::addTemplateTranslator → NO_COVERAGE 2. buildContentTranslator : removed call to fr/sii/ogham/sms/builder/SmsBuilder::addTemplateTranslator → KILLED 3. buildContentTranslator : removed call to fr/sii/ogham/sms/builder/SmsBuilder::addTemplateTranslator → KILLED 4. buildContentTranslator : removed call to fr/sii/ogham/sms/builder/SmsBuilder::addTemplateTranslator → KILLED |
addTemplateTranslator(translator); |
599 |
4
1. buildContentTranslator : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::buildContentTranslator → NO_COVERAGE 2. buildContentTranslator : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::buildContentTranslator → KILLED 3. buildContentTranslator : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::buildContentTranslator → KILLED 4. buildContentTranslator : replaced return value with null for fr/sii/ogham/sms/builder/SmsBuilder::buildContentTranslator → KILLED |
return translator; |
600 | } | |
601 | ||
602 | private void addTemplateTranslator(EveryContentTranslator translator) { | |
603 |
4
1. addTemplateTranslator : negated conditional → NO_COVERAGE 2. addTemplateTranslator : negated conditional → KILLED 3. addTemplateTranslator : negated conditional → KILLED 4. addTemplateTranslator : negated conditional → KILLED |
if (!templateBuilderHelper.hasRegisteredTemplates()) { |
604 | return; | |
605 | } | |
606 | TemplateParser templateParser = templateBuilderHelper.buildTemplateParser(); | |
607 | LOG.debug("Registering content translator that parses templates using {}", templateParser); | |
608 |
4
1. addTemplateTranslator : removed call to fr/sii/ogham/core/translator/content/EveryContentTranslator::addTranslator → NO_COVERAGE 2. addTemplateTranslator : removed call to fr/sii/ogham/core/translator/content/EveryContentTranslator::addTranslator → KILLED 3. addTemplateTranslator : removed call to fr/sii/ogham/core/translator/content/EveryContentTranslator::addTranslator → KILLED 4. addTemplateTranslator : removed call to fr/sii/ogham/core/translator/content/EveryContentTranslator::addTranslator → KILLED |
translator.addTranslator(buildContext.register(new TemplateContentTranslator(templateParser))); |
609 | } | |
610 | ||
611 | } | |
Mutations | ||
339 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
342 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
377 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
380 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
424 |
1.1 2.2 3.3 4.4 |
|
461 |
1.1 2.2 3.3 4.4 |
|
462 |
1.1 2.2 3.3 |
|
522 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
563 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
566 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
573 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
574 |
1.1 2.2 3.3 4.4 5.5 |
|
579 |
1.1 2.2 3.3 |
|
583 |
1.1 2.2 3.3 |
|
589 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 |
|
593 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
598 |
1.1 2.2 3.3 4.4 |
|
599 |
1.1 2.2 3.3 4.4 |
|
603 |
1.1 2.2 3.3 4.4 |
|
608 |
1.1 2.2 3.3 4.4 |