Skip to content

Commit 661e6e0

Browse files
author
Tim Harper
authored
Merge pull request #66 from ccutrer/aggregate_notifications
batch notifications when possible
2 parents 5bbf893 + 9992673 commit 661e6e0

File tree

4 files changed

+101
-20
lines changed

4 files changed

+101
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.beowulfe.hap.impl.connections;
2+
3+
import com.beowulfe.hap.characteristics.EventableCharacteristic;
4+
5+
public class PendingNotification {
6+
public int aid;
7+
public int iid;
8+
public EventableCharacteristic characteristic;
9+
10+
public PendingNotification(int aid, int iid, EventableCharacteristic characteristic) {
11+
this.aid = aid;
12+
this.iid = iid;
13+
this.characteristic = characteristic;
14+
}
15+
}

src/main/java/com/beowulfe/hap/impl/connections/SubscriptionManager.java

+39-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.beowulfe.hap.impl.http.HomekitClientConnection;
55
import com.beowulfe.hap.impl.http.HttpResponse;
66
import com.beowulfe.hap.impl.json.EventController;
7+
import java.util.ArrayList;
78
import java.util.Collections;
89
import java.util.Iterator;
910
import java.util.Set;
@@ -20,6 +21,9 @@ public class SubscriptionManager {
2021
new ConcurrentHashMap<>();
2122
private final ConcurrentMap<HomekitClientConnection, Set<EventableCharacteristic>> reverse =
2223
new ConcurrentHashMap<>();
24+
private final ConcurrentMap<HomekitClientConnection, ArrayList<PendingNotification>>
25+
pendingNotifications = new ConcurrentHashMap<>();
26+
private int nestedBatches = 0;
2327

2428
public synchronized void addSubscription(
2529
int aid,
@@ -72,6 +76,7 @@ public synchronized void removeSubscription(
7276

7377
public synchronized void removeConnection(HomekitClientConnection connection) {
7478
Set<EventableCharacteristic> characteristics = reverse.remove(connection);
79+
pendingNotifications.remove(connection);
7580
if (characteristics != null) {
7681
for (EventableCharacteristic characteristic : characteristics) {
7782
Set<HomekitClientConnection> characteristicSubscriptions =
@@ -91,10 +96,42 @@ private <T> Set<T> newSet() {
9196
return Collections.newSetFromMap(new ConcurrentHashMap<T, Boolean>());
9297
}
9398

94-
public void publish(int accessoryId, int iid, EventableCharacteristic changed) {
99+
public synchronized void batchUpdate() {
100+
++this.nestedBatches;
101+
}
102+
103+
public synchronized void completeUpdateBatch() {
104+
if (--this.nestedBatches == 0 && !pendingNotifications.isEmpty()) {
105+
LOGGER.info("Publishing batched changes");
106+
for (ConcurrentMap.Entry<HomekitClientConnection, ArrayList<PendingNotification>> entry :
107+
pendingNotifications.entrySet()) {
108+
try {
109+
HttpResponse message = new EventController().getMessage(entry.getValue());
110+
entry.getKey().outOfBand(message);
111+
} catch (Exception e) {
112+
LOGGER.error("Faled to create new event message", e);
113+
}
114+
}
115+
pendingNotifications.clear();
116+
}
117+
}
118+
119+
public synchronized void publish(int accessoryId, int iid, EventableCharacteristic changed) {
120+
if (nestedBatches != 0) {
121+
LOGGER.info("Batching change for " + accessoryId);
122+
PendingNotification notification = new PendingNotification(accessoryId, iid, changed);
123+
for (HomekitClientConnection connection : subscriptions.get(changed)) {
124+
if (!pendingNotifications.containsKey(connection)) {
125+
pendingNotifications.put(connection, new ArrayList<PendingNotification>());
126+
}
127+
pendingNotifications.get(connection).add(notification);
128+
}
129+
return;
130+
}
131+
95132
try {
96133
HttpResponse message = new EventController().getMessage(accessoryId, iid, changed);
97-
LOGGER.info("Publishing changes for " + accessoryId);
134+
LOGGER.info("Publishing change for " + accessoryId);
98135
for (HomekitClientConnection connection : subscriptions.get(changed)) {
99136
connection.outOfBand(message);
100137
}

src/main/java/com/beowulfe/hap/impl/json/CharacteristicsController.java

+24-18
Original file line numberDiff line numberDiff line change
@@ -68,28 +68,34 @@ public HttpResponse get(HttpRequest request) throws Exception {
6868

6969
public HttpResponse put(HttpRequest request, HomekitClientConnection connection)
7070
throws Exception {
71-
try (ByteArrayInputStream bais = new ByteArrayInputStream(request.getBody())) {
72-
JsonArray jsonCharacteristics =
73-
Json.createReader(bais).readObject().getJsonArray("characteristics");
74-
for (JsonValue value : jsonCharacteristics) {
75-
JsonObject jsonCharacteristic = (JsonObject) value;
76-
int aid = jsonCharacteristic.getInt("aid");
77-
int iid = jsonCharacteristic.getInt("iid");
78-
Characteristic characteristic = registry.getCharacteristics(aid).get(iid);
71+
subscriptions.batchUpdate();
72+
try {
73+
try (ByteArrayInputStream bais = new ByteArrayInputStream(request.getBody())) {
74+
JsonArray jsonCharacteristics =
75+
Json.createReader(bais).readObject().getJsonArray("characteristics");
76+
for (JsonValue value : jsonCharacteristics) {
77+
JsonObject jsonCharacteristic = (JsonObject) value;
78+
int aid = jsonCharacteristic.getInt("aid");
79+
int iid = jsonCharacteristic.getInt("iid");
80+
Characteristic characteristic = registry.getCharacteristics(aid).get(iid);
7981

80-
if (jsonCharacteristic.containsKey("value")) {
81-
characteristic.setValue(jsonCharacteristic.get("value"));
82-
}
83-
if (jsonCharacteristic.containsKey("ev")
84-
&& characteristic instanceof EventableCharacteristic) {
85-
if (jsonCharacteristic.getBoolean("ev")) {
86-
subscriptions.addSubscription(
87-
aid, iid, (EventableCharacteristic) characteristic, connection);
88-
} else {
89-
subscriptions.removeSubscription((EventableCharacteristic) characteristic, connection);
82+
if (jsonCharacteristic.containsKey("value")) {
83+
characteristic.setValue(jsonCharacteristic.get("value"));
84+
}
85+
if (jsonCharacteristic.containsKey("ev")
86+
&& characteristic instanceof EventableCharacteristic) {
87+
if (jsonCharacteristic.getBoolean("ev")) {
88+
subscriptions.addSubscription(
89+
aid, iid, (EventableCharacteristic) characteristic, connection);
90+
} else {
91+
subscriptions.removeSubscription(
92+
(EventableCharacteristic) characteristic, connection);
93+
}
9094
}
9195
}
9296
}
97+
} finally {
98+
subscriptions.completeUpdateBatch();
9399
}
94100
return new HapJsonNoContentResponse();
95101
}

src/main/java/com/beowulfe/hap/impl/json/EventController.java

+23
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.beowulfe.hap.impl.json;
22

33
import com.beowulfe.hap.characteristics.EventableCharacteristic;
4+
import com.beowulfe.hap.impl.connections.PendingNotification;
45
import com.beowulfe.hap.impl.http.HttpResponse;
56
import java.io.ByteArrayOutputStream;
7+
import java.util.ArrayList;
68
import javax.json.Json;
79
import javax.json.JsonArrayBuilder;
810
import javax.json.JsonObject;
@@ -29,4 +31,25 @@ public HttpResponse getMessage(int accessoryId, int iid, EventableCharacteristic
2931
return new EventResponse(dataBytes);
3032
}
3133
}
34+
35+
public HttpResponse getMessage(ArrayList<PendingNotification> notifications) throws Exception {
36+
JsonArrayBuilder characteristics = Json.createArrayBuilder();
37+
38+
for (PendingNotification notification : notifications) {
39+
JsonObjectBuilder characteristicBuilder = Json.createObjectBuilder();
40+
characteristicBuilder.add("aid", notification.aid);
41+
characteristicBuilder.add("iid", notification.iid);
42+
notification.characteristic.supplyValue(characteristicBuilder);
43+
characteristics.add(characteristicBuilder.build());
44+
}
45+
46+
JsonObject data = Json.createObjectBuilder().add("characteristics", characteristics).build();
47+
48+
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
49+
Json.createWriter(baos).write(data);
50+
byte[] dataBytes = baos.toByteArray();
51+
52+
return new EventResponse(dataBytes);
53+
}
54+
}
3255
}

0 commit comments

Comments
 (0)