Class SubscriptionStore
IZS.SUBSCRIPTIONS: one row per (user, application, Topic, device), carrying the transport and what it needs, plus the device's last-seen address.The key is (uid, app_id, topic, device_uuid) (GATE 1). The device is in it, so a user holds the same (application, Topic) on as many devices as they subscribe from; "all my devices" is the resolver expanding the key over the device dimension, not a different row. app_id is the validated application name, * for the iiziRun subscriptions of the server, and topic is the empty string for the application-level subscription - both NOT NULL, because a NULL in a unique key is not unique in Derby or PostgreSQL and the previous DDL's NULL conventions made the key unenforceable for exactly the two values the client sends most.
SQL relied on, and its portability. CREATE TABLE with BIGINT, VARCHAR(n), CHAR(36), SMALLINT; a named UNIQUE constraint; a FOREIGN KEY ... ON DELETE CASCADE to IZS.USERS(uid); SELECT, INSERT, UPDATE, DELETE with ? parameters; IN (...) lists; DROP TABLE; DatabaseMetaData.getColumns. All of it is SQL-92 and runs unchanged on Derby (the shipped default) and PostgreSQL (the likely production target). Nothing here needs MERGE, UPSERT, LIMIT, sequences or a vendor type. Identifiers are unquoted, so each database folds them to its own case, which is why the migration probe looks the table up in both.
Migration. ensureSchema() creates the table when it is absent and replaces it when it has the previous shape (aid BIGINT with a foreign key to IZS.APPS that nothing populates, device_uuid CHAR(20) that cannot hold a 36-character UUID, no device in the key). The previous shape's rows are dropped: none can be reached by any client, because no 20-character device UUID matches a 36-character one, and Christopher has said nothing depends on current data. The drop is logged SEVERE with the row count so it never happens unseen.
Thread-safe: every method takes its own connection and commits or rolls back it.
- Author:
- Christopher Mindus
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumThe outcome of a registration.Field Summary
FieldsConstructor Summary
ConstructorsMethod Summary
Modifier and TypeMethodDescriptionintCounts subscriptions with the same dimensions asquery(long[], List, List, List).booleandelete(SubscriptionRecord record) Removes one subscription by its identity, e.g.booleanCreates the table when absent, replaces it when it has the previous shape.Finds one subscription by its key.Queries subscriptions.register(long uid, String appId, String topic, String deviceUuid, PushTransport transport, String subId, String domain, String userAgent, String remoteIp) Registers a subscription: creates the row, or replaces the subscription of an existing key when it changed.voidStores the region the device's last address resolved to.intRecords that a device presented itself: updateslast_seenandlast_ipof every subscription of the device, and clears the resolved region when the address changed so it is resolved again.intunregister(long uid, String appId, String topic, String deviceUuid) Removes the subscriptions of a device for an application and Topic.intunregisterBySubscription(long uid, String deviceUuid, String subId) Removes the subscriptions of a device that carry a given transport subscription: the client's unsubscribe body may carry only the subscription and no application or Topic.
Field Details
ALL_APPS
The application ID of the server's iiziRun subscriptions.- See Also:
Constructor Details
SubscriptionStore
Constructor.- Parameters:
connections- The connection source.
Method Details
ensureSchema
Creates the table when absent, replaces it when it has the previous shape.- Returns:
- true if the table was created or replaced, false if it was already current.
- Throws:
SQLException- For SQL errors.
register
public SubscriptionStore.RegisterResult register(long uid, String appId, String topic, String deviceUuid, PushTransport transport, String subId, String domain, String userAgent, String remoteIp) throws SQLException Registers a subscription: creates the row, or replaces the subscription of an existing key when it changed.- Parameters:
uid- The user ID.appId- The application ID,*for iiziRun.topic- The Topic, empty for the application-level subscription.deviceUuid- The device UUID, canonical 36 characters.transport- The transport.subId- The transport's subscription.domain- The host name the client used.userAgent- The user agent, null for none.remoteIp- The remote address, null when unknown.- Returns:
- What happened.
- Throws:
SQLException- For SQL errors.
unregister
Removes the subscriptions of a device for an application and Topic.- Parameters:
uid- The user ID.appId- The application ID, null for any.topic- The Topic, null for any.deviceUuid- The device UUID.- Returns:
- The number of rows removed.
- Throws:
SQLException- For SQL errors.
unregisterBySubscription
Removes the subscriptions of a device that carry a given transport subscription: the client's unsubscribe body may carry only the subscription and no application or Topic.- Parameters:
uid- The user ID.deviceUuid- The device UUID.subId- The transport subscription.- Returns:
- The number of rows removed.
- Throws:
SQLException- For SQL errors.
delete
Removes one subscription by its identity, e.g. after the push service reported it gone.- Parameters:
record- The subscription.- Returns:
- true if a row was removed.
- Throws:
SQLException- For SQL errors.
find
public SubscriptionRecord find(long uid, String appId, String topic, String deviceUuid) throws SQLException Finds one subscription by its key.- Parameters:
uid- The user ID.appId- The application ID.topic- The Topic.deviceUuid- The device UUID.- Returns:
- The subscription, or null.
- Throws:
SQLException- For SQL errors.
query
public List<SubscriptionRecord> query(long[] uids, List<String> appIds, List<String> topics, List<String> devices) throws SQLException Queries subscriptions. An empty or null list means "any" for that dimension; the result is in (uid, app_id, topic, device_uuid) order. Rows whose stored transport ordinal is outside the constant set are skipped and logged.- Parameters:
uids- The user IDs, empty or null for any.appIds- The application IDs, empty or null for any.topics- The Topics, empty or null for any.devices- The device UUIDs, empty or null for any.- Returns:
- The subscriptions.
- Throws:
SQLException- For SQL errors.
count
public int count(long[] uids, List<String> appIds, List<String> topics, List<String> devices) throws SQLException Counts subscriptions with the same dimensions asquery(long[], List, List, List).- Parameters:
uids- The user IDs, empty or null for any.appIds- The application IDs, empty or null for any.topics- The Topics, empty or null for any.devices- The device UUIDs, empty or null for any.- Returns:
- The count.
- Throws:
SQLException- For SQL errors.
touch
Records that a device presented itself: updateslast_seenandlast_ipof every subscription of the device, and clears the resolved region when the address changed so it is resolved again.- Parameters:
uid- The user ID.deviceUuid- The device UUID.remoteIp- The remote address, null when unknown.- Returns:
- The number of rows touched.
- Throws:
SQLException- For SQL errors.
setRegion
Stores the region the device's last address resolved to.- Parameters:
uid- The user ID.deviceUuid- The device UUID.region- The region code, null for unknown.- Throws:
SQLException- For SQL errors.