All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fr.sii.ogham.sms.builder.cloudhopper.MessageSplitterBuilder Maven / Gradle / Ivy

The newest version!
package fr.sii.ogham.sms.builder.cloudhopper;

import static com.cloudhopper.commons.charset.CharsetUtil.NAME_GSM7;
import static com.cloudhopper.commons.charset.CharsetUtil.NAME_GSM8;
import static com.cloudhopper.commons.charset.CharsetUtil.NAME_ISO_8859_1;
import static com.cloudhopper.commons.charset.CharsetUtil.NAME_UCS_2;
import static fr.sii.ogham.sms.SmsConstants.SmppSplitConstants.SEGMENT_SIZE_GSM_7BIT_SMS_PACKING_MODE;
import static fr.sii.ogham.sms.SmsConstants.SmppSplitConstants.SEGMENT_SIZE_GSM_8BIT;
import static fr.sii.ogham.sms.SmsConstants.SmppSplitConstants.SEGMENT_SIZE_UCS2;

import java.util.Random;

import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
import fr.sii.ogham.core.builder.configurer.Configurer;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.builder.env.EnvironmentBuilder;
import fr.sii.ogham.core.exception.builder.BuildException;
import fr.sii.ogham.core.fluent.AbstractParent;
import fr.sii.ogham.core.util.PriorizedList;
import fr.sii.ogham.sms.SmsConstants.SmppSplitConstants.SegmentSizes;
import fr.sii.ogham.sms.encoder.Encoder;
import fr.sii.ogham.sms.encoder.SupportingEncoder;
import fr.sii.ogham.sms.sender.impl.cloudhopper.encoder.CloudhopperCharsetSupportingEncoder;
import fr.sii.ogham.sms.sender.impl.cloudhopper.encoder.NamedCharset;
import fr.sii.ogham.sms.sender.impl.cloudhopper.splitter.SupportedEncoderConditionalSplitter;
import fr.sii.ogham.sms.splitter.FirstSupportingMessageSplitter;
import fr.sii.ogham.sms.splitter.GsmMessageSplitter;
import fr.sii.ogham.sms.splitter.MessageSplitter;
import fr.sii.ogham.sms.splitter.RandomReferenceNumberGenerator;
import fr.sii.ogham.sms.splitter.ReferenceNumberGenerator;
import fr.sii.ogham.sms.splitter.SupportingSplitter;

/**
 * Configures how Cloudhopper will split messages.
 * 
 * 

* The splitter will check if the whole message can fit in a single segment. If * not the splitter will split the whole message in several segments with a * header to indicate splitting information such as number of segments, * reference number and current segment number. * *

* {@link Encoder} configured using {@link CloudhopperBuilder#encoder()} is used * to encode each segment. * *

* If automatic guessing of best standard encoder is enabled for {@link Encoder} * (using {@code encoder().autoGuess(true)}), and message splitting is enabled, * then standard message splitting is configured such as: *

    *
  • If GSM 7-bit encoder is enabled, {@link GsmMessageSplitter} is used to * split messages that support this encoding. If whole message can fit in a * single segment of 160 characters. Longer message is split into segments of * either 153 characters or 152 characters (depending on reference number * generation, see {@link ReferenceNumberGenerator})
  • *
  • If GSM 8-bit encoder is enabled, {@link GsmMessageSplitter} is used to * split messages that support this encoding. If whole message can fit in a * single segment of 140 characters. Longer message is split into segments of * either 134 characters or 133 characters (depending on reference number * generation, see {@link ReferenceNumberGenerator})
  • *
  • If UCS-2 encoder is enabled, {@link GsmMessageSplitter} is used to split * messages that support this encoding. If whole message can fit in a single * segment of 70 characters. Longer message is split into segments of either 67 * characters or 66 characters (depending on reference number generation, see * {@link ReferenceNumberGenerator})
  • *
* * Each registered splitter uses the same priority as associated * {@link Encoder}. * * If you don't want standard message splitting based on supported * {@link Encoder}s, you can either disable message splitting or provide a * custom splitter with higher priority. * *

* This builder allows to configure: *

    *
  • Enable/disable message splitting
  • *
  • Provide a custom split strategy
  • *
  • Choose strategy for reference number generation
  • *
* *
 * {@code
 * .splitter()
 *   .enable()
 *     .properties("${ogham.sms.cloudhopper.split.enable}", "${ogham.sms.split.enable}")
 *     .devaultValue(true)
 *     .and()
 *   .customSplitter(new MyCustomSplitter(), 100000)
 *   .referenceNumber()
 *     .random()
 *     .random(new Random())
 *     .generator(new MyCustomReferenceNumberGenerator())
 * }
 * 
* * @author Aurélien Baudet * */ public class MessageSplitterBuilder extends AbstractParent implements Builder { private final BuildContext buildContext; private final ReadableEncoderBuilder encoderBuilder; private final ConfigurationValueBuilderHelper enableValueBuilder; private final PriorizedList customSplitters; private MessageSplitter customSplitter; private ReferenceNumberGeneratorBuilder referenceNumberBuilder; /** * Initializes the builder with a parent builder. The parent builder is used * when calling {@link #and()} method. The {@link EnvironmentBuilder} is * used to evaluate properties when {@link #build()} method is called. * * @param parent * the parent builder * @param buildContext * for registering instances and property evaluation * @param encoderBuilder * the encoder builder that is used to configure standard message * splitting based on encoding charset */ public MessageSplitterBuilder(CloudhopperBuilder parent, BuildContext buildContext, ReadableEncoderBuilder encoderBuilder) { super(parent); this.buildContext = buildContext; this.encoderBuilder = encoderBuilder; enableValueBuilder = buildContext.newConfigurationValueBuilder(this, Boolean.class); customSplitters = new PriorizedList<>(); } /** * Enable/disable message splitting. * *

* The value set using this method takes precedence over any property and * default value configured using {@link #enable()}. * *

	 * .enable(false)
	 * .enable()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* *
	 * .enable(false)
	 * .enable()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* * In both cases, {@code enable(false)} is used. * *

* If this method is called several times, only the last value is used. * *

* If {@code null} value is set, it is like not setting a value at all. The * property/default value configuration is applied. * * @param enable * enable or disable message splitting * @return this instance for fluent chaining */ public MessageSplitterBuilder enable(Boolean enable) { enableValueBuilder.setValue(enable); return this; } /** * Enable/disable message splitting. * *

* This method is mainly used by {@link Configurer}s to register some * property keys and/or a default value. The aim is to let developer be able * to externalize its configuration (using system properties, configuration * file or anything else). If the developer doesn't configure any value for * the registered properties, the default value is used (if set). * *

	 * .enable()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* *

* Non-null value set using {@link #enable(Boolean)} takes precedence over * property values and default value. * *

	 * .enable(false)
	 * .enable()
	 *   .properties("${custom.property.high-priority}", "${custom.property.low-priority}")
	 *   .defaultValue(true)
	 * 
* * The value {@code false} is used regardless of the value of the properties * and default value. * *

* See {@link ConfigurationValueBuilder} for more information. * * * @return the builder to configure property keys/default value */ public ConfigurationValueBuilder enable() { return enableValueBuilder; } /** * Configures how Cloudhopper should generate a reference number. * *

* Reference number is used to identify segments that belong to the same * message. Every segment of the split message must have the same reference * number. * *

* This builder allows to configure: *

    *
  • Enable random generation strategy
  • *
  • Customize random generation by providing a custom {@link Random}
  • *
  • Provide a custom generator
  • *
* *
	 * {@code
	 *   .referenceNumber()
	 *     .random()
	 *     .random(new Random())
	 *     .generator(new MyCustomReferenceNumberGenerator())
	 * }
	 * 
* * * @return the builder to configure reference number generation * @see ReferenceNumberGenerator */ public ReferenceNumberGeneratorBuilder referenceNumber() { if (referenceNumberBuilder == null) { referenceNumberBuilder = new ReferenceNumberGeneratorBuilder(this, buildContext); } return referenceNumberBuilder; } /** * Register a custom splitter strategy. * *

* Using this method totally disable all other features. Only the provided * splitter is used. * *

* If this method is called several times, only the last one is used. * *

* If {@code null} value is provided, then custom splitting is disabled. * * @param splitter * the splitter to use * @return this instance for fluent chaining */ public MessageSplitterBuilder customSplitter(MessageSplitter splitter) { this.customSplitter = splitter; return this; } /** * Register a custom splitter strategy in the chain of splitters. * *

* It is possible to register several custom splitters. * *

* The priority is used to indicate in which order the custom splitter must * be applied in the chain. * *

* If the custom splitter implements {@link SupportingSplitter}, then the * splitter can indicate if it is able to handle to message to split. If the * splitter can't handle the message, then the next splitter is tried. * *

* If the custom splitter doesn't implement {@link SupportingSplitter}, then * the splitter is considered as able to handle the message. Splitting is * used with this splitter. So if such splitter is registered with a higher * priority than others, no other splitter will be tried. * * @param splitter * the splitter to register * @param priority * the associated priority (greater value means higher priority) * @return this instance for fluent chaining */ public MessageSplitterBuilder customSplitter(MessageSplitter splitter, int priority) { customSplitters.register(splitter, priority); return this; } @Override public MessageSplitter build() { if (customSplitter != null) { return customSplitter; } if (!splittingEnabled()) { return null; } if (encoderBuilder.autoGuessEnabled()) { return buildAutoGuessSplitter(); } if (!customSplitters.isEmpty()) { return buildContext.register(new FirstSupportingMessageSplitter(customSplitters.getOrdered())); } throw new BuildException("Split of SMS is enabled but no splitter is configured"); } private boolean splittingEnabled() { return enableValueBuilder.getValue(false); } private MessageSplitter buildAutoGuessSplitter() { PriorizedList registry = new PriorizedList<>(); registerStandardSplitter(encoderBuilder.getGsm7Priorities(), NAME_GSM7, SEGMENT_SIZE_GSM_7BIT_SMS_PACKING_MODE, registry); registerStandardSplitter(encoderBuilder.getGsm8Priorities(), NAME_GSM8, SEGMENT_SIZE_GSM_8BIT, registry); registerStandardSplitter(encoderBuilder.getLatin1Priorities(), NAME_ISO_8859_1, SEGMENT_SIZE_GSM_8BIT, registry); registerStandardSplitter(encoderBuilder.getUcs2Priorities(), NAME_UCS_2, SEGMENT_SIZE_UCS2, registry); registry.register(customSplitters); return buildContext.register(new FirstSupportingMessageSplitter(registry.getOrdered())); } private void registerStandardSplitter(StandardEncodingHelper priorities, String supportedCharsetName, SegmentSizes maxSizes, PriorizedList registry) { Integer priority = priorities.getValue(); if (priority == null || priority <= 0) { return; } registry.register(buildStandardSplitter(supportedCharsetName, maxSizes), priority); } private MessageSplitter buildStandardSplitter(String supportingCharset, SegmentSizes maxSizes) { SupportingEncoder encoder = buildContext.register(new CloudhopperCharsetSupportingEncoder(NamedCharset.from(supportingCharset))); return buildContext.register(new SupportedEncoderConditionalSplitter(encoder, buildContext.register(new GsmMessageSplitter(encoder, maxSizes, buildReferenceNumberGenerator())))); } private ReferenceNumberGenerator buildReferenceNumberGenerator() { if (referenceNumberBuilder != null) { return referenceNumberBuilder.build(); } return buildContext.register(new RandomReferenceNumberGenerator()); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy