@@ -45,17 +45,42 @@ public class SosIpcTransceiver extends BroadcastReceiver {
4545 public static final String ACTION_SQAN_BROADCAST = "org.sofwerx.sqan.pkt" ;
4646 private final static String SQAN_PACKET_BYTES = "bytes" ;
4747 private final static String SQAN_PACKET_CHANNEL = "channel" ;
48+ final static long DEFAULT_OUTGOING_THROTTLE_RATE = 1000l * 5l ;
49+ final static long DEFAULT_INCOMING_THROTTLE_RATE = 1000l * 10l ;
4850 private static boolean enableSqAN = true ;
4951 private static String channel = SosService .DEFAULT_SWE_CHANNEL ;
5052 private SosMessageListener listener ;
5153
54+ //Since XML is expensive to marshall/unmarshall, include throttling to ignore messages that come in or go out too fast
55+ private static long throttleRate = -1l ;
56+ private static long nextAvailableIntake = Long .MIN_VALUE ;
57+
5258 public SosIpcTransceiver (SosMessageListener listener ) {
5359 this .listener = listener ;
5460 }
5561
5662 public static void setChannel (String channel ) { SosIpcTransceiver .channel = channel ; }
5763 public static void setEnableSqAN (boolean enable ) { SosIpcTransceiver .enableSqAN = enable ; }
5864
65+ /**
66+ * Sets a throttle rate (i.e. a min amo8nt of time between messages in ms); all messages
67+ * received in excess of the throttle rate will be dropped (done to prevent bogging
68+ * down the processor in XML marshallig/unmarshalling operations).
69+ * @param rate rate in ms (or -1l if no throttling is needed)
70+ */
71+ public static void setThrottleRate (long rate ) {
72+ if (throttleRate != rate ) {
73+ throttleRate = rate ;
74+ if (rate > 0l )
75+ Log .d (TAG , "Setting throttle interval to " + Long .toString (rate ) + "ms" );
76+ else {
77+ Log .d (TAG , "Removing throttle" );
78+ }
79+ }
80+ }
81+
82+ public static void clearThrottle () { setThrottleRate (-1l ); }
83+
5984 @ Override
6085 public void onReceive (Context context , Intent intent ) {
6186 if ((context != null ) && (intent != null )) {
@@ -98,22 +123,27 @@ public void onMessageReceived(final Context context,final String source, final S
98123 Log .e (TAG , "Null operation received from SOS broadcast IPC" );
99124 return ;
100125 }
101- new Thread (() -> {
102- try {
103- DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance ();
104- DocumentBuilder docBuilder = builderFactory .newDocumentBuilder ();
105- Document doc = docBuilder .parse (new InputSource (new ByteArrayInputStream (input .getBytes ("utf-8" ))));
106- if (doc != null ) {
107- AbstractSosOperation operation = AbstractSosOperation .newFromXML (doc );
108- if (operation != null ) {
109- if (listener != null )
110- listener .onSosOperationReceived (operation );
126+ if ((throttleRate <= 0l ) || (System .currentTimeMillis () > nextAvailableIntake )) {
127+ if (throttleRate > 0l )
128+ nextAvailableIntake = System .currentTimeMillis () + throttleRate ;
129+ new Thread (() -> {
130+ try {
131+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory .newInstance ();
132+ DocumentBuilder docBuilder = builderFactory .newDocumentBuilder ();
133+ Document doc = docBuilder .parse (new InputSource (new ByteArrayInputStream (input .getBytes ("utf-8" ))));
134+ if (doc != null ) {
135+ AbstractSosOperation operation = AbstractSosOperation .newFromXML (doc );
136+ if (operation != null ) {
137+ if (listener != null )
138+ listener .onSosOperationReceived (operation );
139+ }
111140 }
141+ } catch (ParserConfigurationException | IOException | SAXException e ) {
142+ Log .e (TAG , "SOS IPC broadcast was not XML: " + input );
112143 }
113- } catch (ParserConfigurationException | IOException | SAXException e ) {
114- Log .e (TAG ,"SOS IPC broadcast was not XML: " +input );
115- }
116- }).start ();
144+ }).start ();
145+ } else
146+ Log .d (TAG ,"Dropping message from " +source +" due to flooding: " +input );
117147 }
118148
119149 /**
@@ -122,25 +152,30 @@ public void onMessageReceived(final Context context,final String source, final S
122152 * @param operation
123153 */
124154 public void broadcast (final Context context , final AbstractSosOperation operation ) throws SosException {
125- if (operation != null ) {
126- if (!operation .isValid ()) {
127- throw new SosException (operation .getClass ().getSimpleName ()+" does not have all required information" );
128- }
129- new Thread (() -> {
130- Document doc = null ;
131- try {
132- doc = operation .toXML ();
133- } catch (ParserConfigurationException e ) {
134- //throw new SosException("Unable to create document: " + e.getMessage());
135- }
136- try {
137- if (doc != null )
138- broadcast (context , toString (doc ));
139- } catch (Exception ex ) {
140- //throw new SosException("Unable to convert XML document to string: " + ex.getMessage());
155+ if ((throttleRate <= 0l ) || (System .currentTimeMillis () > nextAvailableIntake )) {
156+ if (throttleRate > 0l )
157+ nextAvailableIntake = System .currentTimeMillis () + throttleRate ;
158+ if (operation != null ) {
159+ if (!operation .isValid ()) {
160+ throw new SosException (operation .getClass ().getSimpleName () + " does not have all required information" );
141161 }
142- }).start ();
143- }
162+ new Thread (() -> {
163+ Document doc = null ;
164+ try {
165+ doc = operation .toXML ();
166+ } catch (ParserConfigurationException e ) {
167+ //throw new SosException("Unable to create document: " + e.getMessage());
168+ }
169+ try {
170+ if (doc != null )
171+ broadcast (context , toString (doc ));
172+ } catch (Exception ex ) {
173+ //throw new SosException("Unable to convert XML document to string: " + ex.getMessage());
174+ }
175+ }).start ();
176+ }
177+ } else
178+ Log .d (TAG ,operation .getClass ().getSimpleName ()+" operation received but ignored since the current throttle rate of " +Long .toString (throttleRate )+"ms is being exceeded" );
144179 }
145180
146181 public final static String toString (Document doc ) throws TransformerException {
0 commit comments