/* * Copyright 2013-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.bus.jackson; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.bus.BusAutoConfiguration; import org.springframework.cloud.bus.ConditionalOnBusEnabled; import org.springframework.cloud.bus.endpoint.RefreshBusEndpoint; import org.springframework.cloud.bus.event.RemoteApplicationEvent; import org.springframework.cloud.bus.event.UnknownRemoteApplicationEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Configuration; import org.springframework.core.type.filter.AssignableTypeFilter; import org.springframework.messaging.Message; import org.springframework.messaging.converter.AbstractMessageConverter; import org.springframework.util.ClassUtils; import org.springframework.util.MimeTypeUtils; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.exc.InvalidTypeIdException; /** * @author Spencer Gibb * @author Dave Syer * @author Donovan Muller * @author Stefan Pfeiffer */ @Configuration @ConditionalOnBusEnabled @ConditionalOnClass({ RefreshBusEndpoint.class, ObjectMapper.class }) @AutoConfigureAfter(BusAutoConfiguration.class) public class BusJacksonAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "busJsonConverter") public BusJacksonMessageConverter busJsonConverter() { return new BusJacksonMessageConverter(); } } class BusJacksonMessageConverter extends AbstractMessageConverter implements InitializingBean { private static final String DEFAULT_PACKAGE = ClassUtils .getPackageName(RemoteApplicationEvent.class); private final ObjectMapper mapper = new ObjectMapper(); private String[] packagesToScan = new String[] { DEFAULT_PACKAGE }; public void setPackagesToScan(String[] packagesToScan) { List<String> packages = new ArrayList<>(Arrays.asList(packagesToScan)); if (!packages.contains(DEFAULT_PACKAGE)) { packages.add(DEFAULT_PACKAGE); } this.packagesToScan = packages.toArray(new String[0]); } public BusJacksonMessageConverter() { super(MimeTypeUtils.APPLICATION_JSON); } private Class<?>[] findSubTypes() { List<Class<?>> types = new ArrayList<>(); if (this.packagesToScan != null) { for (String pkg : this.packagesToScan) { ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider( false); provider.addIncludeFilter( new AssignableTypeFilter(RemoteApplicationEvent.class)); Set<BeanDefinition> components = provider.findCandidateComponents(pkg); for (BeanDefinition component : components) { try { types.add(Class.forName(component.getBeanClassName())); } catch (ClassNotFoundException e) { throw new IllegalStateException( "Failed to scan classpath for remote event classes", e); } } } } return types.toArray(new Class<?>[0]); } @Override protected boolean supports(Class<?> aClass) { // This converter applies only to RemoteApplicationEvent and subclasses return RemoteApplicationEvent.class.isAssignableFrom(aClass); } @Override public Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) { Object result = null; try { Object payload = message.getPayload(); if (payload instanceof byte[]) { try { result = this.mapper.readValue((byte[]) payload, targetClass); } catch (InvalidTypeIdException e) { return new UnknownRemoteApplicationEvent(new Object(), e.getTypeId(), (byte[]) payload); } } else if (payload instanceof String) { try { result = this.mapper.readValue((String) payload, targetClass); } catch (InvalidTypeIdException e) { return new UnknownRemoteApplicationEvent(new Object(), e.getTypeId(), ((String) payload).getBytes()); } } } catch (Exception e) { this.logger.error(e.getMessage(), e); return null; } return result; } @Override public void afterPropertiesSet() throws Exception { this.mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); this.mapper.registerModule(new SubtypeModule(findSubTypes())); } }