View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.audit.util;
18  
19  import java.io.IOException;
20  import java.time.DateTimeException;
21  import java.time.LocalDate;
22  import java.time.LocalDateTime;
23  import java.time.ZonedDateTime;
24  import java.time.format.DateTimeFormatter;
25  
26  import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
27  
28  import com.fasterxml.jackson.annotation.JsonInclude;
29  import com.fasterxml.jackson.core.JsonGenerator;
30  import com.fasterxml.jackson.core.JsonParser;
31  import com.fasterxml.jackson.core.JsonProcessingException;
32  import com.fasterxml.jackson.databind.DeserializationContext;
33  import com.fasterxml.jackson.databind.JsonDeserializer;
34  import com.fasterxml.jackson.databind.JsonMappingException;
35  import com.fasterxml.jackson.databind.JsonSerializer;
36  import com.fasterxml.jackson.databind.ObjectMapper;
37  import com.fasterxml.jackson.databind.SerializationFeature;
38  import com.fasterxml.jackson.databind.SerializerProvider;
39  import com.fasterxml.jackson.databind.module.SimpleModule;
40  
41  /**
42   *  Extends Jackson ObjectMapper to support Java LocalDateTime.
43   */
44  public final class JsonObjectMapperFactory {
45  
46      /**
47       * Date/Time format.
48       */
49      public static final String LOCAL_DATE_TIME_FORMAT = "yyyyMMddHHmmss.SSS";
50  
51      /**
52       * LocalDateTime formatter that converts to and from a format usable in REST requests.
53       */
54      public static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(LOCAL_DATE_TIME_FORMAT);
55  
56      /**
57       * Date/Time format.
58       */
59      public static final String LOCAL_DATE_FORMAT = "yyyyMMdd";
60  
61      /**
62       * LocalDateTime formatter that converts to and from a format usable in REST requests.
63       */
64      public static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofPattern(LOCAL_DATE_FORMAT);
65  
66      /**
67       * Date/Time format.
68       */
69      public static final String ZONED_DATE_TIME_FORMAT = "yyyyMMddHHmmss.SSSZ";
70  
71      /**
72       * LocalDateTime formatter that converts to and from a format usable in REST requests.
73       */
74      public static final DateTimeFormatter ZONED_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(ZONED_DATE_TIME_FORMAT);
75  
76      private JsonObjectMapperFactory() {
77      }
78  
79      /**
80       * Create an ObjectMapper using the standard LocalDateTime format.
81       * @return The ObjectMapper.
82       */
83      public static ObjectMapper createMapper() {
84          ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().build();
85          DateTimeFormatter dateTimeFormatter = LOCAL_DATE_TIME_FORMATTER;
86          DateTimeFormatter dateFormatter = LOCAL_DATE_FORMATTER;
87          DateTimeFormatter zonedTimeFormatter = ZONED_DATE_TIME_FORMATTER;
88          SimpleModule module = new SimpleModule();
89          module.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
90              @Override
91              public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator,
92                                    SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
93                  jsonGenerator.writeString(dateTimeFormatter.format(localDateTime));
94              }
95          });
96          module.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
97              @Override
98              public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
99                  String string = parser.getText().trim();
100                 if (string.length() == 0) {
101                     return null;
102                 }
103                 try {
104                     return LocalDateTime.parse(string, dateTimeFormatter);
105                 } catch (DateTimeException e) {
106                     throw JsonMappingException.from(parser,
107                             String.format("Failed to deserialize %s: (%s) %s",
108                                     handledType().getName(), e.getClass().getName(), e.getMessage()), e);
109                 }
110             }
111         });
112         module.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
113             @Override
114             public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator,
115                                   SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
116                 jsonGenerator.writeString(zonedTimeFormatter.format(zonedDateTime));
117             }
118         });
119         module.addDeserializer(ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
120             @Override
121             public ZonedDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
122                 String string = parser.getText().trim();
123                 if (string.length() == 0) {
124                     return null;
125                 }
126                 try {
127                     return ZonedDateTime.parse(string, zonedTimeFormatter);
128                 } catch (DateTimeException e) {
129                     throw JsonMappingException.from(parser,
130                             String.format("Failed to deserialize %s: (%s) %s",
131                                     handledType().getName(), e.getClass().getName(), e.getMessage()), e);
132                 }
133             }
134         });
135         module.addSerializer(LocalDate.class, new JsonSerializer<LocalDate>() {
136             @Override
137             public void serialize(LocalDate localDate, JsonGenerator jsonGenerator,
138                                   SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
139                 jsonGenerator.writeString(dateFormatter.format(localDate));
140             }
141         });
142         module.addDeserializer(LocalDate.class, new JsonDeserializer<LocalDate>() {
143             @Override
144             public LocalDate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
145                 String string = parser.getText().trim();
146                 if (string.length() == 0) {
147                     return null;
148                 }
149                 try {
150                     return LocalDate.parse(string, dateFormatter);
151                 } catch (DateTimeException e) {
152                     throw JsonMappingException.from(parser,
153                             String.format("Failed to deserialize %s: (%s) %s",
154                                     handledType().getName(), e.getClass().getName(), e.getMessage()), e);
155                 }
156             }
157         });
158         mapper.registerModule(module);
159         mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
160         mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
161         return mapper;
162     }
163 
164 }