/*
 * Copyright 2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.stream.metrics.config;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.jackson.JsonComponent;

/**
 * @author Vinicius Carvalho
 * @author Oleg Zhurakousky
 */
@JsonComponent
public class MetricJsonSerializer {

	private final static DateFormat df;
	static {
		df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
		df.setTimeZone(TimeZone.getTimeZone("GMT"));
	}

	public static class Serializer extends JsonSerializer<Metric<?>> {

		@Override
		public void serialize(Metric<?> metric, JsonGenerator json,
				SerializerProvider serializerProvider) throws IOException {
			json.writeStartObject();
			json.writeStringField("name", metric.getName());
			json.writeNumberField("value", metric.getValue().doubleValue());
			json.writeStringField("timestamp", df.format(metric.getTimestamp()));
			json.writeEndObject();
		}
	}

	public static class Deserializer extends JsonDeserializer<Metric<?>> {

		@Override
		public Metric<?> deserialize(JsonParser p, DeserializationContext ctxt)
				throws IOException, JsonProcessingException {
			JsonNode node = p.getCodec().readTree(p);
			String name = node.get("name").asText();
			Number value = node.get("value").asDouble();
			Date timestamp = null;

			try {
				timestamp = df.parse(node.get("timestamp").asText());
			}
			catch (ParseException e) {
			}
			Metric<Number> metric = new Metric<Number>(name, value, timestamp);

			return metric;
		}

	}
}

@@ -842,16 +842,108 @@
 imeZone;
+%0Aimport java.util.concurrent.BlockingQueue;%0Aimport java.util.concurrent.LinkedBlockingQueue;
 %0A%0Aimport
@@ -1611,154 +1611,98 @@
 ate 
-final static DateFormat df;%0A%09static %7B%0A%09%09df = new SimpleDateFormat(%22yyyy-MM-dd'T'HH:mm:ss.SSSX%22);%0A%09%09df.setTimeZone(TimeZone.getTimeZone(%22GMT%22));%0A%09%7D
+static final BlockingQueue%3CDateFormat%3E formatters = new LinkedBlockingQueue%3CDateFormat%3E();
 %0A%0A%09p
@@ -2050,24 +2050,128 @@
 leValue());%0A
+%09%09%09DateFormat df = formatters.poll();%0A%09%09%09if (df == null) %7B%0A%09%09%09%09df = defaultDateFormat();%0A%09%09%09%7D%0A%09%09%09try %7B%0A%09
 %09%09%09json.writ
@@ -2230,24 +2230,25 @@
 amp()));%0A%09%09%09
+%09
 json.writeEn
@@ -2258,16 +2258,65 @@
 ject();%0A
+%09%09%09%7D%0A%09%09%09finally %7B%0A%09%09%09%09formatters.offer(df);%0A%09%09%09%7D%0A
 %09%09%7D%0A%09%7D%0A%0A
@@ -2687,16 +2687,109 @@
 = null;%0A
+%09%09%09DateFormat df = formatters.poll();%0A%09%09%09if (df == null) %7B%0A%09%09%09%09df = defaultDateFormat();%0A%09%09%09%7D
 %0A%09%09%09try 
@@ -2883,16 +2883,99 @@
 on e) %7B%0A
+%09%09%09%09// ignore timestamp parsing errors%0A%09%09%09%7D%0A%09%09%09finally %7B%0A%09%09%09%09formatters.offer(df);%0A
 %09%09%09%7D%0A%09%09%09
@@ -3009,22 +3009,16 @@
  Metric%3C
-Number
 %3E(name, 
@@ -3036,17 +3036,16 @@
 stamp);%0A
-%0A
 %09%09%09retur
@@ -3061,11 +3061,194 @@
 %0A%09%09%7D%0A%0A%09%7D
+%0A%0A%09private static DateFormat defaultDateFormat() %7B%0A%09%09DateFormat df = new SimpleDateFormat(%22yyyy-MM-dd'T'HH:mm:ss.SSSX%22);%0A%09%09df.setTimeZone(TimeZone.getTimeZone(%22GMT%22));%0A%09%09return df;%0A%09%7D
 %0A%7D%0A

Make `MetricJsonSerializer` thread-safe