1 /*
2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.time.Instant;
29 import java.util.Objects;
30 import java.util.logging.Level;
31 import java.util.logging.LogRecord;
32 import java.util.logging.SimpleFormatter;
33 
34 /**
35  * @test
36  * @bug 8072645 8144262
37  * @summary tests the new methods added to LogRecord.
38  * @run main LogRecordWithNanosAPI
39  * @author danielfuchs
40  */
41 public class LogRecordWithNanosAPI {
42 
43     static final int MILLIS_IN_SECOND = 1000;
44     static final int NANOS_IN_MILLI = 1000_000;
45     static final int NANOS_IN_SECOND = 1000_000_000;
46 
47     static final boolean verbose = true;
48 
49     static final class TestAssertException extends RuntimeException {
TestAssertException(String msg)50         TestAssertException(String msg) { super(msg); }
51     }
52 
assertEquals(long expected, long received, String msg)53     private static void assertEquals(long expected, long received, String msg) {
54         if (expected != received) {
55             throw new TestAssertException("Unexpected result for " + msg
56                     + ".\n\texpected: " + expected
57                     +  "\n\tactual:   " + received);
58         } else if (verbose) {
59             System.out.println("Got expected " + msg + ": " + received);
60         }
61     }
62 
assertEquals(Object expected, Object received, String msg)63     private static void assertEquals(Object expected, Object received, String msg) {
64         if (!Objects.equals(expected, received)) {
65             throw new TestAssertException("Unexpected result for " + msg
66                     + ".\n\texpected: " + expected
67                     +  "\n\tactual:   " + received);
68         } else if (verbose) {
69             System.out.println("Got expected " + msg + ": " + received);
70         }
71     }
72 
73     // The nano second fractional part of a second, contained in a time expressed
74     // as a number of millisecond from the epoch.
nanoInSecondFromEpochMilli(long millis)75     private static long nanoInSecondFromEpochMilli(long millis) {
76         return (((millis%MILLIS_IN_SECOND) + MILLIS_IN_SECOND)%MILLIS_IN_SECOND)*NANOS_IN_MILLI;
77     }
78 
79     /**
80      * Serializes a log record, then deserializes it and check that both
81      * records match.
82      * @param record the log record to serialize & deserialize.
83      * @param hasExceedingNanos whether the record has a nano adjustment whose
84      *            value exceeds 1ms.
85      * @throws IOException Unexpected.
86      * @throws ClassNotFoundException  Unexpected.
87      */
test(LogRecord record, boolean hasExceedingNanos)88     public static void test(LogRecord record, boolean hasExceedingNanos)
89             throws IOException, ClassNotFoundException {
90 
91         // Format the given logRecord using the SimpleFormatter
92         SimpleFormatter formatter = new SimpleFormatter();
93         String str = formatter.format(record);
94 
95         // Serialize the given LogRecord
96         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
97         final ObjectOutputStream oos = new ObjectOutputStream(baos);
98         oos.writeObject(record);
99         oos.flush();
100         oos.close();
101 
102         // First checks that the log record can be deserialized
103         final ByteArrayInputStream bais =
104                 new ByteArrayInputStream(baos.toByteArray());
105         final ObjectInputStream ois = new ObjectInputStream(bais);
106         final LogRecord record2 = (LogRecord)ois.readObject();
107 
108         assertEquals(record.getMillis(), record2.getMillis(), "getMillis()");
109         assertEquals(record.getInstant().getEpochSecond(),
110                 record2.getInstant().getEpochSecond(),
111                 "getInstant().getEpochSecond()");
112         assertEquals(record.getInstant().getNano(),
113                 record2.getInstant().getNano(),
114                 "getInstant().getNano()");
115         assertEquals(record.getInstant().toEpochMilli(),
116                 record2.getInstant().toEpochMilli(),
117                 "getInstant().toEpochMilli()");
118         long millis = record.getMillis();
119         millis = hasExceedingNanos
120                 ? Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
121                         (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI
122                         + record.getInstant().getNano() % NANOS_IN_MILLI).toEpochMilli()
123                 : millis;
124         assertEquals(millis, record.getInstant().toEpochMilli(),
125                 "getMillis()/getInstant().toEpochMilli()");
126         millis = record2.getMillis();
127         millis = hasExceedingNanos
128                 ? Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
129                         (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI
130                         + record2.getInstant().getNano() % NANOS_IN_MILLI).toEpochMilli()
131                 : millis;
132         assertEquals(millis, record2.getInstant().toEpochMilli(),
133                 "getMillis()/getInstant().toEpochMilli()");
134         long nanos = nanoInSecondFromEpochMilli(record.getMillis())
135                 + record.getInstant().getNano() % NANOS_IN_MILLI;
136         assertEquals(nanos, record.getInstant().getNano(),
137                 "nanoInSecondFromEpochMilli(record.getMillis())"
138                 + " + record.getInstant().getNano() % NANOS_IN_MILLI /getInstant().getNano()");
139         nanos = nanoInSecondFromEpochMilli(record2.getMillis())
140                 + record2.getInstant().getNano() % NANOS_IN_MILLI;
141         assertEquals(nanos, record2.getInstant().getNano(),
142                 "nanoInSecondFromEpochMilli(record2.getMillis())"
143                 + " + record2.getInstant().getNano() % NANOS_IN_MILLI /getInstant().getNano()");
144 
145         // Format the deserialized LogRecord using the SimpleFormatter, and
146         // check that the string representation obtained matches the string
147         // representation of the original LogRecord
148         String str2 = formatter.format(record2);
149         if (!str.equals(str2))
150             throw new RuntimeException("Unexpected values in deserialized object:"
151                 + "\n\tExpected:  " + str
152                 + "\n\tRetrieved: "+str);
153 
154     }
155 
156 
main(String[] args)157     public static void main(String[] args) throws Exception {
158         int count=0;
159         LogRecord record = new LogRecord(Level.INFO, "Java Version: {0}");
160         record.setLoggerName("test");
161         record.setParameters(new Object[] {System.getProperty("java.version")});
162         final int nanos = record.getInstant().getNano() % NANOS_IN_MILLI;
163         final long millis = record.getMillis();
164         final Instant instant = record.getInstant();
165         if (millis != instant.toEpochMilli()) {
166             throw new RuntimeException("Unexpected millis: "
167                     + record.getMillis());
168         }
169         test(record, false);
170 
171         // nano adjustment < 1ms (canonical case)
172         int newNanos = (nanos + 111111) % NANOS_IN_MILLI;
173         record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
174                 (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newNanos));
175         assertEquals(newNanos, record.getInstant().getNano() % NANOS_IN_MILLI,
176                 "record.getInstant().getNano() % NANOS_IN_MILLI");
177         assertEquals(millis, record.getMillis(), "record.getMillis()");
178         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
179                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newNanos),
180                 record.getInstant(), "record.getInstant()");
181         test(record, false);
182         assertEquals(newNanos, record.getInstant().getNano() % NANOS_IN_MILLI,
183                 "record.getInstant().getNano() % NANOS_IN_MILLI");
184         assertEquals(millis, record.getMillis(), "record.getMillis()");
185         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
186                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newNanos),
187                 record.getInstant(), "record.getInstant()");
188 
189         // nano adjustment > 1ms - non canonical - should still work
190         int newExceedingNanos = 2111_111;
191         record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
192                 (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
193         assertEquals(newExceedingNanos % NANOS_IN_MILLI,
194                 record.getInstant().getNano() % NANOS_IN_MILLI,
195                 "record.getInstant().getNano() % NANOS_IN_MILLI");
196         assertEquals(millis + newExceedingNanos / NANOS_IN_MILLI,
197                 record.getMillis(), "record.getMillis()");
198         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
199                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
200                 record.getInstant(), "record.getInstant()");
201         test(record, true);
202         assertEquals(newExceedingNanos % NANOS_IN_MILLI,
203                 record.getInstant().getNano() % NANOS_IN_MILLI,
204                 "record.getInstant().getNano() % NANOS_IN_MILLI");
205         assertEquals(millis  + newExceedingNanos / NANOS_IN_MILLI,
206                 record.getMillis(), "record.getMillis()");
207         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
208                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
209                 record.getInstant(), "record.getInstant()");
210 
211         // nano adjustement > 1s - non canonical - should still work
212         newExceedingNanos = 1111_111_111;
213         record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
214                 (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
215         assertEquals(newExceedingNanos % NANOS_IN_MILLI,
216                 record.getInstant().getNano() % NANOS_IN_MILLI,
217                 "record.getInstant().getNano() % NANOS_IN_MILLI");
218         assertEquals(millis  + newExceedingNanos / NANOS_IN_MILLI,
219                 record.getMillis(), "record.getMillis()");
220         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
221                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
222                 record.getInstant(), "record.getInstant()");
223         test(record, true);
224         assertEquals(newExceedingNanos % NANOS_IN_MILLI,
225                 record.getInstant().getNano() % NANOS_IN_MILLI,
226                 "record.getInstant().getNano() % NANOS_IN_MILLI");
227         assertEquals(millis  + newExceedingNanos / NANOS_IN_MILLI,
228                 record.getMillis(), "record.getMillis()");
229         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
230                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
231                 record.getInstant(), "record.getInstant()");
232 
233         // nano adjustement < 0 - non canonical - should still work
234         newExceedingNanos = -1;
235         record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
236                 (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
237         assertEquals(newExceedingNanos + NANOS_IN_MILLI,
238                 record.getInstant().getNano() % NANOS_IN_MILLI,
239                 "record.getInstant().getNano() % NANOS_IN_MILLI");
240         assertEquals(millis -1, record.getMillis(), "record.getMillis()");
241         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
242                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
243                 record.getInstant(), "record.getInstant()");
244         test(record, true);
245         record.setInstant(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
246                 (millis % MILLIS_IN_SECOND) * NANOS_IN_MILLI + newExceedingNanos));
247         assertEquals(millis -1, record.getMillis(), "record.getMillis()");
248         assertEquals(Instant.ofEpochSecond(millis/MILLIS_IN_SECOND,
249                 (millis%MILLIS_IN_SECOND)*NANOS_IN_MILLI + newExceedingNanos),
250                 record.getInstant(), "record.getInstant()");
251 
252         // setMillis
253         record.setMillis(millis-1);
254         assertEquals(millis-1, record.getInstant().toEpochMilli(),
255                 "record.getInstant().toEpochMilli()");
256         assertEquals(millis-1, record.getMillis(),
257                 "record.getMillis()");
258         assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
259                 "record.getInstant().getNano() % NANOS_IN_MILLI");
260         test(record, false);
261         assertEquals(millis-1, record.getInstant().toEpochMilli(),
262                 "record.getInstant().toEpochMilli()");
263         assertEquals(millis-1, record.getMillis(),
264                 "record.getMillis()");
265         assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
266                 "record.getInstant().getNano() % NANOS_IN_MILLI");
267 
268         // setMillis to 0
269         record.setMillis(0);
270         assertEquals(0, record.getInstant().toEpochMilli(),
271                 "record.getInstant().toEpochMilli()");
272         assertEquals(0, record.getMillis(),
273                 "record.getMillis()");
274         assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
275                 "record.getInstant().getNano() % NANOS_IN_MILLI");
276         test(record, false);
277         assertEquals(0, record.getInstant().toEpochMilli(),
278                 "record.getInstant().toEpochMilli()");
279         assertEquals(0, record.getMillis(),
280                 "record.getMillis()");
281         assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
282                 "record.getInstant().getNano() % NANOS_IN_MILLI");
283 
284         // setMillis to -1
285         record.setMillis(-1);
286         assertEquals(-1, record.getInstant().toEpochMilli(),
287                 "record.getInstant().toEpochMilli()");
288         assertEquals(-1, record.getMillis(),
289                 "record.getMillis()");
290         assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
291                 "record.getInstant().getNano() % NANOS_IN_MILLI");
292         test(record, false);
293         assertEquals(-1, record.getInstant().toEpochMilli(),
294                 "record.getInstant().toEpochMilli()");
295         assertEquals(-1, record.getMillis(),
296                 "record.getMillis()");
297         assertEquals(0, record.getInstant().getNano() % NANOS_IN_MILLI,
298                 "record.getInstant().getNano() % NANOS_IN_MILLI");
299 
300         try {
301             record.setInstant(null);
302             throw new RuntimeException("Expected NullPointerException not thrown");
303         } catch (NullPointerException x) {
304             System.out.println("Got expected NPE when trying to call record.setInstant(null): " + x);
305         }
306 
307         // This instant is the biggest for which toEpochMilli will not throw...
308         final Instant max = Instant.ofEpochMilli(Long.MAX_VALUE).plusNanos(999_999L);
309         record.setInstant(max);
310         assertEquals(Long.MAX_VALUE / 1000L,
311                      record.getInstant().getEpochSecond(),
312                      "max instant seconds [record.getInstant().getEpochSecond()]");
313         assertEquals(Long.MAX_VALUE,
314                      record.getInstant().toEpochMilli(),
315                      "max instant millis [record.getInstant().toEpochMilli()]");
316         assertEquals(Long.MAX_VALUE, record.getMillis(),
317                      "max instant millis [record.getMillis()]");
318         assertEquals((Long.MAX_VALUE % 1000L)*1000_000L + 999_999L,
319                      record.getInstant().getNano(),
320                      "max instant nanos [record.getInstant().getNano()]");
321 
322         // Too big by 1 ns.
323         final Instant tooBig = max.plusNanos(1L);
324         try {
325             record.setInstant(tooBig);
326             throw new RuntimeException("Expected ArithmeticException not thrown");
327         } catch (ArithmeticException x) {
328             System.out.println("Got expected ArithmeticException when trying"
329                     + " to call record.setInstant(Instant.ofEpochMilli(Long.MAX_VALUE)"
330                     + ".plusNanos(999_999L).plusNanos(1L)): " + x);
331         }
332 
333     }
334 
335 }
336