001/* 002 * Copyright 2008-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.springframework.batch.core.repository.dao; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.io.OutputStream; 021import java.util.Date; 022import java.util.HashMap; 023import java.util.Map; 024 025import com.fasterxml.jackson.annotation.JsonIgnore; 026import com.fasterxml.jackson.core.JsonParser; 027import com.fasterxml.jackson.core.type.TypeReference; 028import com.fasterxml.jackson.databind.DeserializationContext; 029import com.fasterxml.jackson.databind.DeserializationFeature; 030import com.fasterxml.jackson.databind.JsonNode; 031import com.fasterxml.jackson.databind.MapperFeature; 032import com.fasterxml.jackson.databind.ObjectMapper; 033 034import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 035import com.fasterxml.jackson.databind.module.SimpleModule; 036import org.springframework.batch.core.JobParameter; 037import org.springframework.batch.core.JobParameters; 038import org.springframework.batch.core.repository.ExecutionContextSerializer; 039import org.springframework.util.Assert; 040 041/** 042 * Implementation that uses Jackson2 to provide (de)serialization. 043 * 044 * @author Marten Deinum 045 * @author Mahmoud Ben Hassine 046 * @since 3.0.7 047 * 048 * @see ExecutionContextSerializer 049 */ 050public class Jackson2ExecutionContextStringSerializer implements ExecutionContextSerializer { 051 052 private ObjectMapper objectMapper; 053 054 public Jackson2ExecutionContextStringSerializer() { 055 this.objectMapper = new ObjectMapper(); 056 this.objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false); 057 this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); 058 this.objectMapper.enableDefaultTyping(); 059 this.objectMapper.registerModule(new JobParametersModule()); 060 } 061 062 public void setObjectMapper(ObjectMapper objectMapper) { 063 Assert.notNull(objectMapper, "ObjectMapper must not be null"); 064 this.objectMapper = objectMapper; 065 } 066 067 public Map<String, Object> deserialize(InputStream in) throws IOException { 068 069 TypeReference<HashMap<String,Object>> typeRef = new TypeReference<HashMap<String,Object>>() {}; 070 return objectMapper.readValue(in, typeRef); 071 } 072 073 public void serialize(Map<String, Object> context, OutputStream out) throws IOException { 074 075 Assert.notNull(context, "A context is required"); 076 Assert.notNull(out, "An OutputStream is required"); 077 078 objectMapper.writeValue(out, context); 079 } 080 081 // BATCH-2680 082 /** 083 * Custom Jackson module to support {@link JobParameter} and {@link JobParameters} 084 * deserialization. 085 */ 086 private class JobParametersModule extends SimpleModule { 087 088 private JobParametersModule() { 089 super("Job parameters module"); 090 setMixInAnnotation(JobParameters.class, JobParametersMixIn.class); 091 addDeserializer(JobParameter.class, new JobParameterDeserializer()); 092 } 093 094 private abstract class JobParametersMixIn { 095 @JsonIgnore 096 abstract boolean isEmpty(); 097 } 098 099 private class JobParameterDeserializer extends StdDeserializer<JobParameter> { 100 101 private static final String IDENTIFYING_KEY_NAME = "identifying"; 102 private static final String TYPE_KEY_NAME = "type"; 103 private static final String VALUE_KEY_NAME = "value"; 104 105 JobParameterDeserializer() { 106 super(JobParameter.class); 107 } 108 109 @Override 110 public JobParameter deserialize(JsonParser parser, DeserializationContext context) throws IOException { 111 JsonNode node = parser.readValueAsTree(); 112 boolean identifying = node.get(IDENTIFYING_KEY_NAME).asBoolean(); 113 String type = node.get(TYPE_KEY_NAME).asText(); 114 JsonNode value = node.get(VALUE_KEY_NAME); 115 Object parameterValue; 116 switch (JobParameter.ParameterType.valueOf(type)) { 117 case STRING: { 118 parameterValue = value.asText(); 119 return new JobParameter((String) parameterValue, identifying); 120 } 121 case DATE: { 122 parameterValue = new Date(value.get(1).asLong()); 123 return new JobParameter((Date) parameterValue, identifying); 124 } 125 case LONG: { 126 parameterValue = value.get(1).asLong(); 127 return new JobParameter((Long) parameterValue, identifying); 128 } 129 case DOUBLE: { 130 parameterValue = value.asDouble(); 131 return new JobParameter((Double) parameterValue, identifying); 132 } 133 } 134 return null; 135 } 136 } 137 138 } 139 140}