Skip to content

Commit f9be336

Browse files
committed
OF-2500: Switch from java.sql.Timestamp to java.time.Instant
Not using the older Java SQL data types but `java.time` ones instead makes life a lot less complex when dealing with time zone oddities. This change requires JDBC 4.2 support in the database driver, and expects the corresponding database columns to be of type `TIMESTAMP WITH TIME ZONE`
1 parent 8fe960e commit f9be336

File tree

7 files changed

+98
-93
lines changed

7 files changed

+98
-93
lines changed

documentation/database-guide.html

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,18 @@ <h2>Data Type Conventions</h2>
4949
</p>
5050
<h3>Dates in Openfire 5.2.0 and later</h3>
5151
<p>
52-
Openfire stores dates as TIMESTAMP values, with fractional seconds parts. To help prevent environmental
52+
Openfire stores dates as TIMESTAMP WITH TIME ZONE values, with fractional seconds parts. To help prevent environmental
5353
configurations from imposing a timezone context to these values, values are to be written to and obtained
54-
from the database using the JDBC <code>setTimestamp()</code> methods with a third argument that explicitly
55-
defines the UTC-calendar, as shown below.
54+
from the database using the JDBC 4.2 <code>getObject()</code> <code>setObject()</code> methods with a Java
55+
representations from <code>java.time</code> (e.g. <code>java.time.Instant</code>).
5656
</p>
5757
<p>To read a timestamp from the database:</p>
5858
<ul style="list-style: none">
59-
<li><code>Date creationDate = rs.getTimestamp("creationDate", Calendar.getInstance(TimeZone.getTimeZone("UTC")));</code></li>
59+
<li><code>Instant creationDate = rs.getObject("creationDate")</code></li>
6060
</ul>
6161
<p>Conversely, to write a timestamp to the database:</p>
6262
<ul style="list-style: none">
63-
<li><code>pstmt.setTimestamp("creationDate", new Timestamp(creationDate.getTime()), Calendar.getInstance(TimeZone.getTimeZone("UTC")));</code></li>
63+
<li><code>pstmt.setObject("creationDate", instant</code></li>
6464
</ul>
6565
<h3>Dates prior to Openfire 5.2.0</h3>
6666
<p>
@@ -281,7 +281,7 @@ <h2>Database Tables</h2>
281281
</tr>
282282
<tr>
283283
<td>creationDate</td>
284-
<td>TIMESTAMP</td>
284+
<td>TIMESTAMP WITH TIME ZONE</td>
285285
<td></td>
286286
<td>Date message stored</td>
287287
</tr>
@@ -326,7 +326,7 @@ <h2>Database Tables</h2>
326326
</tr>
327327
<tr>
328328
<td>offlineDate</td>
329-
<td>TIMESTAMP</td>
329+
<td>TIMESTAMP WITH TIME ZONE</td>
330330
<td></td>
331331
<td>Date message stored</td>
332332
</tr>
@@ -417,13 +417,13 @@ <h2>Database Tables</h2>
417417
</tr>
418418
<tr>
419419
<td>creationDate</td>
420-
<td>TIMESTAMP</td>
420+
<td>TIMESTAMP WITH TIME ZONE</td>
421421
<td></td>
422422
<td>Creation Date</td>
423423
</tr>
424424
<tr>
425425
<td>modificationDate</td>
426-
<td>TIMESTAMP</td>
426+
<td>TIMESTAMP WITH TIME ZONE</td>
427427
<td></td>
428428
<td>Last Modified Date</td>
429429
</tr>
@@ -489,13 +489,13 @@ <h2>Database Tables</h2>
489489
</tr>
490490
<tr>
491491
<td>startTime</td>
492-
<td>TIMESTAMP</td>
492+
<td>TIMESTAMP WITH TIME ZONE</td>
493493
<td></td>
494494
<td>Time when the flag is to start being effective (null for 'now')</td>
495495
</tr>
496496
<tr>
497497
<td>endTime</td>
498-
<td>TIMESTAMP</td>
498+
<td>TIMESTAMP WITH TIME ZONE</td>
499499
<td></td>
500500
<td>Time when the flag is to end being effective (null for 'forever')</td>
501501
</tr>
@@ -921,13 +921,13 @@ <h2>Database Tables</h2>
921921
</tr>
922922
<tr>
923923
<td>creationDate</td>
924-
<td>TIMESTAMP</td>
924+
<td>TIMESTAMP WITH TIME ZONE</td>
925925
<td></td>
926926
<td>Creation Date</td>
927927
</tr>
928928
<tr>
929929
<td>modificationDate</td>
930-
<td>TIMESTAMP</td>
930+
<td>TIMESTAMP WITH TIME ZONE</td>
931931
<td></td>
932932
<td>Last Modified Date</td>
933933
</tr>
@@ -951,13 +951,13 @@ <h2>Database Tables</h2>
951951
</tr>
952952
<tr>
953953
<td>lockedDate</td>
954-
<td>TIMESTAMP</td>
954+
<td>TIMESTAMP WITH TIME ZONE</td>
955955
<td></td>
956956
<td>the date when the room was locked</td>
957957
</tr>
958958
<tr>
959959
<td>emptyDate</td>
960-
<td>TIMESTAMP</td>
960+
<td>TIMESTAMP WITH TIME ZONE</td>
961961
<td></td>
962962
<td>The date when the last occupant left the room</td>
963963
</tr>
@@ -1131,7 +1131,7 @@ <h2>Database Tables</h2>
11311131
</tr>
11321132
<tr>
11331133
<td>retiredAt</td>
1134-
<td>TIMESTAMP</td>
1134+
<td>TIMESTAMP WITH TIME ZONE</td>
11351135
<td>n/a</td>
11361136
<td>When the room was retired (defaults to current timestamp)</td>
11371137
</tr>
@@ -1236,7 +1236,7 @@ <h2>Database Tables</h2>
12361236
</tr>
12371237
<tr>
12381238
<td>logTime</td>
1239-
<td>TIMESTAMP</td>
1239+
<td>TIMESTAMP WITH TIME ZONE</td>
12401240
<td></td>
12411241
<td>Date when the message was sent to the room</td>
12421242
</tr>
@@ -1287,13 +1287,13 @@ <h2>Database Tables</h2>
12871287
</tr>
12881288
<tr>
12891289
<td>creationDate</td>
1290-
<td>TIMESTAMP</td>
1290+
<td>TIMESTAMP WITH TIME ZONE</td>
12911291
<td></td>
12921292
<td>Creation Date</td>
12931293
</tr>
12941294
<tr>
12951295
<td>modificationDate</td>
1296-
<td>TIMESTAMP</td>
1296+
<td>TIMESTAMP WITH TIME ZONE</td>
12971297
<td></td>
12981298
<td>Last Modified Date</td>
12991299
</tr>
@@ -1594,7 +1594,7 @@ <h2>Database Tables</h2>
15941594
</tr>
15951595
<tr>
15961596
<td>creationDate</td>
1597-
<td>TIMESTAMP</td>
1597+
<td>TIMESTAMP WITH TIME ZONE</td>
15981598
<td></td>
15991599
<td>Creation Date</td>
16001600
</tr>
@@ -1675,7 +1675,7 @@ <h2>Database Tables</h2>
16751675
</tr>
16761676
<tr>
16771677
<td>expire</td>
1678-
<td>TIMESTAMP</td>
1678+
<td>TIMESTAMP WITH TIME ZONE</td>
16791679
<td></td>
16801680
<td>Date at which a leased subscription will end or has ended</td>
16811681
</tr>

xmppserver/src/main/java/org/jivesoftware/openfire/OfflineMessageStore.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,15 @@ public OfflineMessage addMessage(Message message) {
184184
con = DbConnectionManager.getConnection();
185185
pstmt = con.prepareStatement(INSERT_OFFLINE);
186186

187-
Timestamp creationDate = new Timestamp(System.currentTimeMillis());
187+
Instant creationDate = Instant.now();
188188
pstmt.setString(1, username);
189189
pstmt.setLong(2, messageID);
190-
pstmt.setTimestamp(3, creationDate, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
190+
pstmt.setObject(3, creationDate);
191191
pstmt.setInt(4, msgXML.length());
192192
pstmt.setString(5, msgXML);
193193
pstmt.executeUpdate();
194194

195-
offlineMessage = new OfflineMessage(creationDate, message.getElement());
195+
offlineMessage = new OfflineMessage(Date.from(creationDate), message.getElement());
196196
}
197197
catch (Exception e) {
198198
Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
@@ -232,18 +232,18 @@ public Collection<OfflineMessage> getMessages(String username, boolean delete) {
232232
rs = pstmt.executeQuery();
233233
while (rs.next()) {
234234
String msgXML = rs.getString(1);
235-
Date creationDate = rs.getTimestamp(2, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
235+
Instant creationDate = rs.getObject(2, Instant.class);
236236
OfflineMessage message;
237237
try {
238-
message = new OfflineMessage(creationDate, SAXReaderUtil.readRootElement(msgXML));
238+
message = new OfflineMessage(Date.from(creationDate), SAXReaderUtil.readRootElement(msgXML));
239239
} catch (ExecutionException e) {
240240
// Try again after removing invalid XML chars (e.g. &#12;)
241241
Matcher matcher = pattern.matcher(msgXML);
242242
if (matcher.find()) {
243243
msgXML = matcher.replaceAll("");
244244
}
245245
try {
246-
message = new OfflineMessage(creationDate, SAXReaderUtil.readRootElement(msgXML));
246+
message = new OfflineMessage(Date.from(creationDate), SAXReaderUtil.readRootElement(msgXML));
247247
} catch (ExecutionException de) {
248248
Log.error("Failed to route packet (offline message): " + msgXML, de);
249249
continue; // skip and process remaining offline messages
@@ -313,7 +313,7 @@ public OfflineMessage getMessage(String username, Date creationDate) {
313313
con = DbConnectionManager.getConnection();
314314
pstmt = con.prepareStatement(LOAD_OFFLINE_MESSAGE);
315315
pstmt.setString(1, username);
316-
pstmt.setTimestamp(2, new Timestamp(creationDate.getTime()), Calendar.getInstance(TimeZone.getTimeZone("UTC")));
316+
pstmt.setObject(2, creationDate.toInstant());
317317
rs = pstmt.executeQuery();
318318
while (rs.next()) {
319319
String msgXML = rs.getString(1);
@@ -380,7 +380,7 @@ public void deleteMessage(String username, Date creationDate) {
380380
con = DbConnectionManager.getConnection();
381381
pstmt = con.prepareStatement(DELETE_OFFLINE_MESSAGE);
382382
pstmt.setString(1, username);
383-
pstmt.setTimestamp(2, new Timestamp(creationDate.getTime()), Calendar.getInstance(TimeZone.getTimeZone("UTC")));
383+
pstmt.setObject(2, creationDate.toInstant());
384384
pstmt.executeUpdate();
385385

386386
// Force a refresh for next call to getSize(username),
@@ -683,7 +683,7 @@ private boolean deleteOldOfflineMessagesFromDB() {
683683
pstmt = con.prepareStatement(DELETE_OFFLINE_MESSAGE_BEFORE);
684684

685685
final Instant pastTime = Instant.now().minus(OFFLINE_AUTOCLEAN_DAYSTOLIVE.getValue());
686-
pstmt.setTimestamp(1, new Timestamp(pastTime.toEpochMilli()), Calendar.getInstance(TimeZone.getTimeZone("UTC")));
686+
pstmt.setObject(1, pastTime);
687687
final int updateCount = pstmt.executeUpdate();
688688
Log.info("Offline message cleaning - Cleaning successful. Removed {} message(s)", updateCount);
689689
return true;

xmppserver/src/main/java/org/jivesoftware/openfire/lockout/DefaultLockOutProvider.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.jivesoftware.openfire.lockout;
1717

1818
import java.sql.*;
19+
import java.time.Instant;
1920
import java.util.Calendar;
2021
import java.util.Date;
2122
import java.util.TimeZone;
@@ -67,9 +68,9 @@ public LockOutFlag getDisabledStatus(String username) {
6768
if (!rs.next()) {
6869
return null;
6970
}
70-
Date startTime = rs.getTimestamp(2, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
71-
Date endTime = rs.getTimestamp(3, Calendar.getInstance(TimeZone.getTimeZone("UTC")));
72-
ret = new LockOutFlag(username, startTime, endTime);
71+
Instant startTime = rs.getObject(2, Instant.class);
72+
Instant endTime = rs.getObject(3, Instant.class);
73+
ret = new LockOutFlag(username, startTime == null ? null : Date.from(startTime), endTime == null ? null : Date.from(endTime));
7374
}
7475
catch (Exception e) {
7576
Log.error("Error loading lockout information from DB", e);
@@ -107,16 +108,16 @@ public void setDisabledStatus(LockOutFlag flag) {
107108
pstmt = con.prepareStatement(ADD_FLAG);
108109
pstmt.setString(1, flag.getUsername());
109110
if (flag.getStartTime() != null) {
110-
pstmt.setTimestamp(2, new Timestamp(flag.getStartTime().getTime()), Calendar.getInstance(TimeZone.getTimeZone("UTC")));
111+
pstmt.setObject(2, flag.getStartTime().toInstant());
111112
}
112113
else {
113-
pstmt.setNull(2, Types.TIMESTAMP);
114+
pstmt.setNull(2, Types.TIMESTAMP_WITH_TIMEZONE);
114115
}
115116
if (flag.getEndTime() != null) {
116-
pstmt.setTimestamp(3, new Timestamp(flag.getEndTime().getTime()), Calendar.getInstance(TimeZone.getTimeZone("UTC")));
117+
pstmt.setObject(3, flag.getEndTime().toInstant());
117118
}
118119
else {
119-
pstmt.setNull(3, Types.TIMESTAMP);
120+
pstmt.setNull(3, Types.TIMESTAMP_WITH_TIMEZONE);
120121
}
121122
pstmt.executeUpdate();
122123
}

0 commit comments

Comments
 (0)