Skip to content

Commit cd52a5f

Browse files
Merge pull request #15 from DavidTheExplorer/1.7.2
v1.7.2
2 parents 61078a0 + f086de0 commit cd52a5f

File tree

11 files changed

+128
-107
lines changed

11 files changed

+128
-107
lines changed

README.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,25 @@ Maven Repository:
6060

6161

6262
## Adjustments
63-
Everything in the library is customizable.
64-
- If the endpoint of Pikud Ha'oref was changed or your alerts come from somewhere else:\
65-
Implement `AlertSource` and then create your notifier like this:
66-
63+
- Override the frequency of Tzeva Adom checks:
64+
```java
65+
new TzevaAdomNotifier.Builder()
66+
.requestEvery(Duration.ofSeconds(3))
67+
```
68+
69+
- Change the endpoint of Pikud Ha'oref if it was changed:
6770
```java
71+
//PHOAlertSource is the default AlertSource implementation, you can also implement your own.
72+
PHOAlertSource alertSource = new PHOAlertSource();
73+
alertSource.changeRequestURL(new URL("..."));
74+
6875
new TzevaAdomNotifier.Builder()
69-
.requestFrom(new YourAlertSource())
76+
.requestFrom(alertSource)
7077
```
71-
- Override the frequency of the Tzeva Adom checks:
72-
78+
79+
- Prevent console spam when handling exceptions:
7380
```java
81+
//LimitedExceptionHandler is a wrapper of Consumer<Exception> that stops handling after X times
7482
new TzevaAdomNotifier.Builder()
75-
.requestEvery(Duration.ofSeconds(3))
76-
```
83+
.onFailedRequest(new LimitedExceptionHandler(3, yourExceptionHandler));
84+
```

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>dte</groupId>
66
<artifactId>tzevaadomapi</artifactId>
7-
<version>1.7.1</version>
7+
<version>1.7.2</version>
88

99
<build>
1010
<plugins>

src/main/java/dte/tzevaadomapi/alertsource/AlertSource.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
public interface AlertSource
1111
{
1212
/**
13-
* Returns the most recent {@link Alert} that happened in Israel.
13+
* Returns the most recent {@link Alert} that happened in Israel
1414
*
15-
* @return Either the alert, or {@link #NO_RESULT} if one wasn't found.
15+
* @return The alert, or null if no info was found.
1616
* @throws Exception If an exception happened while gathering the alert.
1717
*/
1818
Alert getMostRecentAlert() throws Exception;
@@ -21,16 +21,8 @@ public interface AlertSource
2121
* Returns all the alerts that happened since the provided {@code alert}.
2222
*
2323
* @param alert The 'minimum' alert.
24-
* @return The alerts list, or an empty result if there was no Tzeva Adom afterwards.
25-
* @throws Exception iF an exception happened while gathering the alerts.
24+
* @return The alerts list, or an empty result if there was no Tzeva Adom afterward.
25+
* @throws Exception If an exception happened while gathering the alerts.
2626
*/
2727
Deque<Alert> getSince(Alert alert) throws Exception;
28-
29-
30-
/**
31-
* This object signals when {@link #getMostRecentAlert()} couldn't find an alert to return, and no exceptions occurred.
32-
* <p>
33-
* This is addressed because <i>Pikud Ha'oref</i> returns an empty JSON if no Tzeva Adom happened in the last 24 hours.
34-
*/
35-
Alert NO_RESULT = null;
3628
}

src/main/java/dte/tzevaadomapi/alertsource/OnlineAlertSource.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
public abstract class OnlineAlertSource implements AlertSource
1717
{
18-
private final URL requestURL;
18+
private URL requestURL;
1919
private final Proxy proxy;
2020

2121
public OnlineAlertSource(URL requestURL)
@@ -28,6 +28,11 @@ public OnlineAlertSource(URL requestURL, Proxy proxy)
2828
this.requestURL = requestURL;
2929
this.proxy = proxy;
3030
}
31+
32+
public void changeRequestURL(URL requestURL)
33+
{
34+
this.requestURL = requestURL;
35+
}
3136

3237
protected URLConnection openConnection() throws IOException
3338
{

src/main/java/dte/tzevaadomapi/alertsource/PHOAlertSource.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dte.tzevaadomapi.alertsource;
22

3+
import java.io.IOException;
34
import java.lang.reflect.Type;
45
import java.net.Proxy;
56
import java.net.URL;
@@ -17,7 +18,7 @@
1718

1819
import dte.tzevaadomapi.alert.Alert;
1920
import dte.tzevaadomapi.utils.URLFactory;
20-
import dte.tzevaadomapi.utils.UncheckedExceptions.CheckedFunction;
21+
import dte.tzevaadomapi.utils.CheckedFunction;
2122

2223
/**
2324
* Requests Alerts from Pikud Ha'Oref.
@@ -79,12 +80,19 @@ public Deque<Alert> getSince(Alert alert) throws Exception
7980
});
8081
}
8182

82-
//starts reading the JSON list posted by Pikud Ha'oref, and applies the function on it
83-
private <T> T beginReadingArray(CheckedFunction<JsonReader, T> resultParser)
83+
//starts reading the JSON array posted by Pikud Ha'oref, and applies the function on it
84+
private <T> T beginReadingArray(CheckedFunction<JsonReader, T> resultParser)
8485
{
8586
try(JsonReader reader = new JsonReader(newInputStreamReader()))
8687
{
87-
reader.beginArray();
88+
try
89+
{
90+
reader.beginArray();
91+
}
92+
catch(IOException exception)
93+
{
94+
return null;
95+
}
8896

8997
return resultParser.apply(reader);
9098
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package dte.tzevaadomapi.exceptionhandler;
2+
3+
import dte.tzevaadomapi.alert.Alert;
4+
import dte.tzevaadomapi.listener.TzevaAdomListener;
5+
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
import java.util.function.Consumer;
9+
10+
/**
11+
* An implementation of {@link Consumer<Exception>} that handles a predetermined amount of exceptions, and then stops until the next Tzeva Adom;
12+
* This prevents unwanted behaviour such as logging the same exception endlessly(e.g. when the <i>AlertSource</i>'s server is offline).
13+
*
14+
* @implSpec To prevent swallowing 2 different exceptions, the limit is applied per <i>Exception Class</i> -
15+
* So for a limit of 5, only 5 <i>NullPointerException</i>s and 5 <i>IOException</i>s would be handled.
16+
*/
17+
public class LimitedExceptionHandler implements Consumer<Exception>, TzevaAdomListener
18+
{
19+
private final Consumer<Exception> delegate;
20+
private final int limit;
21+
private final Map<Class<?>, Integer> timesHandled = new HashMap<>();
22+
23+
public LimitedExceptionHandler(int limit, Consumer<Exception> delegate)
24+
{
25+
this.delegate = delegate;
26+
this.limit = limit;
27+
}
28+
29+
@Override
30+
public void accept(Exception exception)
31+
{
32+
this.timesHandled.merge(exception.getClass(), 1, Integer::sum);
33+
34+
if(this.timesHandled.get(exception.getClass()) <= this.limit)
35+
this.delegate.accept(exception);
36+
}
37+
38+
@Override
39+
public void onTzevaAdom(Alert alert)
40+
{
41+
this.timesHandled.clear();
42+
}
43+
}

src/main/java/dte/tzevaadomapi/listener/TzevaAdomListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import dte.tzevaadomapi.alert.Alert;
44

55
/**
6-
* Represents an action that should be done <b>immediately</b> when a Tzeva Adom takes place.
6+
* Represents an action that should be executed <b>immediately</b> upon a Tzeva Adom.
77
*/
88
@FunctionalInterface
99
public interface TzevaAdomListener

src/main/java/dte/tzevaadomapi/notifier/TzevaAdomHistory.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ void update(Collection<Alert> alerts)
2626
}
2727

2828
/**
29-
* Returns the most recent alert in the history of the underlying notifier.
29+
* Returns the most recent alert in this history.
3030
*
3131
* @return The most recent alert.
3232
*/
@@ -36,10 +36,12 @@ public Optional<Alert> getMostRecent()
3636
}
3737

3838
/**
39-
* Returns the list of alerts that happened in a provided {@code region} since the underlying notifier was started.
39+
* Returns the list of alerts that happened in a provided {@code region}.
4040
*
4141
* @param region The region, may be partial.
4242
* @return The region's history.
43+
*
44+
* @implSpec This method accepts partial regions - So if "תל" is provided, the result would contain alerts from both <i>תל אביב</i> and <i>תל מונד</i>.
4345
*/
4446
public List<Alert> ofRegion(String region)
4547
{
@@ -49,7 +51,7 @@ public List<Alert> ofRegion(String region)
4951
}
5052

5153
/**
52-
* Returns a {@link Deque} representation of this history.
54+
* Returns a {@link Deque} snapshot of this history.
5355
*
5456
* @return The deque.
5557
*/
@@ -59,7 +61,7 @@ public Deque<Alert> toDeque()
5961
}
6062

6163
/**
62-
* Returns how many alerts were recorded since the underlying notifier was started.
64+
* Returns how many alerts this history contains.
6365
*
6466
* @return This history's size.
6567
*/

src/main/java/dte/tzevaadomapi/notifier/TzevaAdomNotifier.java

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
import dte.tzevaadomapi.alert.Alert;
1313
import dte.tzevaadomapi.alertsource.AlertSource;
1414
import dte.tzevaadomapi.alertsource.PHOAlertSource;
15+
import dte.tzevaadomapi.exceptionhandler.LimitedExceptionHandler;
1516
import dte.tzevaadomapi.listener.TzevaAdomListener;
16-
import dte.tzevaadomapi.utils.UncheckedExceptions.CheckedSupplier;
17+
import dte.tzevaadomapi.utils.CheckedFunction;
1718

1819
/**
19-
* Notifies registered listeners immediately once a <b>Tzeva Adom</b> takes place.
20+
* Notifies registered listeners immediately upon a <b>Tzeva Adom</b>.
2021
*/
2122
public class TzevaAdomNotifier
2223
{
@@ -25,6 +26,7 @@ public class TzevaAdomNotifier
2526
private final Consumer<Exception> requestFailureHandler;
2627
private final Collection<TzevaAdomListener> listeners;
2728
private final TzevaAdomHistory history = new TzevaAdomHistory();
29+
2830
private Alert mostRecentAlert; //only used in listenAsync(), holding it here solves the effectively final problem
2931

3032
private TzevaAdomNotifier(Collection<TzevaAdomListener> listeners, AlertSource alertSource, Duration requestDelay, Consumer<Exception> requestFailureHandler)
@@ -36,7 +38,7 @@ private TzevaAdomNotifier(Collection<TzevaAdomListener> listeners, AlertSource a
3638
}
3739

3840
/**
39-
* Starts listening to incoming alerts, and returns the corresponding {@link CompletableFuture} object for further control.
41+
* Starts listening to alerts, and returns the corresponding {@link CompletableFuture} object for further control.
4042
* <p>
4143
* In order to implement a program whose sole workflow is to be idle until there is an alert, call {@link CompletableFuture#join() join()} on the result.
4244
*
@@ -47,13 +49,13 @@ public CompletableFuture<Void> listenAsync()
4749
return CompletableFuture.runAsync(() ->
4850
{
4951
//start with the most recent alert
50-
this.mostRecentAlert = requestMostRecentAlert();
52+
this.mostRecentAlert = requestFromSource(AlertSource::getMostRecentAlert);
5153

5254
while(true)
5355
{
54-
Deque<Alert> newAlerts = requestFromSource(() -> this.alertSource.getSince(this.mostRecentAlert));
55-
56-
//if there are no new alerts - it's not Tzeva Adom
56+
Deque<Alert> newAlerts = requestFromSource(source -> source.getSince(this.mostRecentAlert));
57+
58+
//wait until a terror organization decides to launch rockets
5759
if(newAlerts.isEmpty())
5860
continue;
5961

@@ -68,7 +70,7 @@ public CompletableFuture<Void> listenAsync()
6870
}
6971

7072
/**
71-
* Adds a listener to notify when it's Tzeva Adom.
73+
* Adds a listener to notify upon a Tzeva Adom.
7274
*
7375
* @param listener The listener.
7476
*/
@@ -80,37 +82,28 @@ public void addListener(TzevaAdomListener listener)
8082
/**
8183
* Returns the captured history since {@link #listenAsync()} was called.
8284
*
83-
* @return The tzeva adom history.
85+
* @return The Tzeva Adom history.
8486
*/
8587
public TzevaAdomHistory getHistory()
8688
{
8789
return this.history;
8890
}
89-
90-
private Alert requestMostRecentAlert()
91-
{
92-
Alert alert;
93-
94-
//wait until Hamas decides to launch rockets
95-
do
96-
{
97-
alert = requestFromSource(this.alertSource::getMostRecentAlert);
98-
}
99-
while(alert == AlertSource.NO_RESULT);
100-
101-
return alert;
102-
}
10391

104-
private <T> T requestFromSource(CheckedSupplier<T> resultFactory)
92+
private <T> T requestFromSource(CheckedFunction<AlertSource, T> request)
10593
{
10694
while(true)
10795
{
10896
try
10997
{
11098
//sleep the defined delay
11199
TimeUnit.MILLISECONDS.sleep(this.requestDelay.toMillis());
112-
113-
return resultFactory.get();
100+
101+
T result = request.apply(this.alertSource);
102+
103+
if(result == null)
104+
continue;
105+
106+
return result;
114107
}
115108
catch(Exception exception)
116109
{
@@ -146,6 +139,14 @@ public Builder requestEvery(Duration requestDelay)
146139
return this;
147140
}
148141

142+
/**
143+
* Determines how to handle exceptions when alerts are fetched.
144+
*
145+
* @apiNote To prevent unwanted behaviour such as logging the same IOException when the server is offline, You can pass a {@link LimitedExceptionHandler}.
146+
*
147+
* @param handler The exception handler.
148+
* @return The same instance.
149+
*/
149150
public Builder onFailedRequest(Consumer<Exception> handler)
150151
{
151152
this.requestFailureHandler = handler;
@@ -174,6 +175,10 @@ public TzevaAdomNotifier build()
174175
if(this.listeners.isEmpty())
175176
throw new IllegalStateException("At least one listener must be provided to create a TzevaAdomNotifier.");
176177

178+
//support for LimitedExceptionHandler
179+
if(this.requestFailureHandler instanceof TzevaAdomListener)
180+
this.listeners.add((TzevaAdomListener) this.requestFailureHandler);
181+
177182
return new TzevaAdomNotifier(this.listeners, this.alertSource, this.requestDelay, this.requestFailureHandler);
178183
}
179184
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package dte.tzevaadomapi.utils;
2+
3+
@FunctionalInterface
4+
public interface CheckedFunction<T, R>
5+
{
6+
R apply(T object) throws Exception;
7+
}

0 commit comments

Comments
 (0)