/** * Return the Jackson {@link JavaType} for the specified type and context class. * * <p>The default implementation returns {@code typeFactory.constructType(type, contextClass)}, * but this can be overridden in subclasses, to allow for custom generic collection handling. For * instance: * * <pre class="code"> * protected JavaType getJavaType(Type type) { * if (type instanceof Class && List.class.isAssignableFrom((Class)type)) { * return TypeFactory.collectionType(ArrayList.class, MyBean.class); * } else { * return super.getJavaType(type); * } * } * </pre> * * @param type the generic type to return the Jackson JavaType for * @param contextClass a context class for the target type, for example a class in which the * target type appears in a method signature (can be {@code null}) * @return the Jackson JavaType */ protected JavaType getJavaType(Type type, Class<?> contextClass) { TypeFactory typeFactory = this.objectMapper.getTypeFactory(); if (type instanceof TypeVariable && contextClass != null) { ResolvableType resolvedType = resolveVariable((TypeVariable<?>) type, ResolvableType.forClass(contextClass)); if (resolvedType != ResolvableType.NONE) { return typeFactory.constructType(resolvedType.resolve()); } } return typeFactory.constructType(type); }
private Flux<Object> decodeInternal( JsonObjectDecoder objectDecoder, Publisher<DataBuffer> inputStream, ResolvableType elementType, MimeType mimeType, Object[] hints) { Assert.notNull(inputStream, "'inputStream' must not be null"); Assert.notNull(elementType, "'elementType' must not be null"); TypeFactory typeFactory = this.mapper.getTypeFactory(); JavaType javaType = typeFactory.constructType(elementType.getType()); ObjectReader reader = this.mapper.readerFor(javaType); return objectDecoder .decode(inputStream, elementType, mimeType, hints) .map( dataBuffer -> { try { Object value = reader.readValue(dataBuffer.asInputStream()); DataBufferUtils.release(dataBuffer); return value; } catch (IOException e) { return Flux.error(new CodecException("Error while reading the data", e)); } }); }
/** * Invokes all {@link ResourceProcessor} instances registered for the type of the given value and * reference type. * * @param value must not be {@literal null}. * @param referenceType must not be {@literal null}. * @return */ @SuppressWarnings("unchecked") public <T extends ResourceSupport> T invokeProcessorsFor(T value, ResolvableType referenceType) { Assert.notNull(value, "Value must not be null!"); Assert.notNull(referenceType, "Reference type must not be null!"); // For Resources implementations, process elements first if (ResourceProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom( referenceType)) { Resources<?> resources = (Resources<?>) value; ResolvableType elementTargetType = ResolvableType.forClass(Resources.class, referenceType.getRawClass()).getGeneric(0); List<Object> result = new ArrayList<Object>(resources.getContent().size()); for (Object element : resources) { ResolvableType elementType = ResolvableType.forClass(element.getClass()); if (!getRawType(elementTargetType).equals(elementType.getRawClass())) { elementTargetType = elementType; } result.add(invokeProcessorsFor(element, elementTargetType)); } ReflectionUtils.setField( ResourceProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD, resources, result); } return (T) invokeProcessorsFor((Object) value, referenceType); }
@Test public void canDecode() { assertTrue( this.decoder.canDecode( ResolvableType.forClass(ByteBuffer.class), MimeTypeUtils.TEXT_PLAIN)); assertFalse( this.decoder.canDecode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN)); assertTrue( this.decoder.canDecode( ResolvableType.forClass(ByteBuffer.class), MimeTypeUtils.APPLICATION_JSON)); }
/** * Resolve the method arguments to use for the specified {@link ApplicationEvent}. * * <p>These arguments will be used to invoke the method handled by this instance. Can return * {@code null} to indicate that no suitable arguments could be resolved and therefore the method * should not be invoked at all for the specified event. */ protected Object[] resolveArguments(ApplicationEvent event) { ResolvableType declaredEventType = getResolvableType(event); if (declaredEventType == null) { return null; } if (this.method.getParameterCount() == 0) { return new Object[0]; } if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass()) && event instanceof PayloadApplicationEvent) { return new Object[] {((PayloadApplicationEvent) event).getPayload()}; } else { return new Object[] {event}; } }
protected void publishEvent(Object event, ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); if (logger.isTraceEnabled()) { logger.trace("Publishing event in " + getDisplayName() + ": " + event); } // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<Object>(this, event); if (eventType == null) { eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass()); } } // Multicast right now if possible - or lazily once the multicaster is initialized if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }
/** * Returns whether the given {@link Resource} matches the given target {@link ResolvableType}. * We inspect the {@link Resource}'s value to determine the match. * * @param resource * @param target must not be {@literal null}. * @return whether the given {@link Resource} can be assigned to the given target {@link * ResolvableType} */ private static boolean isValueTypeMatch(Resource<?> resource, ResolvableType target) { if (resource == null || !isRawTypeAssignable(target, resource.getClass())) { return false; } Object content = resource.getContent(); if (content == null) { return false; } ResolvableType type = findGenericType(target, Resource.class); return type != null && type.getGeneric(0).isAssignableFrom(ResolvableType.forClass(content.getClass())); }
@Test public void canWrite() { assertTrue( this.encoder.canEncode(ResolvableType.forClass(String.class), MimeTypeUtils.TEXT_PLAIN)); assertTrue( this.encoder.canEncode( ResolvableType.forClass(StringBuilder.class), MimeTypeUtils.TEXT_PLAIN)); assertTrue( this.encoder.canEncode( ResolvableType.forClass(StringBuffer.class), MimeTypeUtils.TEXT_PLAIN)); assertFalse( this.encoder.canEncode(ResolvableType.forClass(Integer.class), MimeTypeUtils.TEXT_PLAIN)); assertFalse( this.encoder.canEncode( ResolvableType.forClass(String.class), MimeTypeUtils.APPLICATION_JSON)); }
protected ResolvableType getGenericApplicationEventType(String fieldName) { try { return ResolvableType.forField(TestEvents.class.getField(fieldName)); } catch (NoSuchFieldException e) { throw new IllegalStateException("No such field on Events '" + fieldName + "'"); } }
@Override public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) { ResolvableType returnType = result.getReturnType(); MethodParameter bodyType; Mono<?> returnValueMono; Optional<Object> optionalValue = result.getReturnValue(); Class<?> rawClass = returnType.getRawClass(); ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(rawClass, optionalValue); if (adapter != null) { returnValueMono = adapter.toMono(optionalValue); bodyType = new MethodParameter(result.getReturnTypeSource()); bodyType.increaseNestingLevel(); bodyType.increaseNestingLevel(); } else { returnValueMono = Mono.justOrEmpty(optionalValue); bodyType = new MethodParameter(result.getReturnTypeSource()); bodyType.increaseNestingLevel(); } return returnValueMono.then( returnValue -> { Assert.isInstanceOf(HttpEntity.class, returnValue); HttpEntity<?> httpEntity = (HttpEntity<?>) returnValue; if (httpEntity instanceof ResponseEntity) { ResponseEntity<?> responseEntity = (ResponseEntity<?>) httpEntity; exchange.getResponse().setStatusCode(responseEntity.getStatusCode()); } HttpHeaders entityHeaders = httpEntity.getHeaders(); HttpHeaders responseHeaders = exchange.getResponse().getHeaders(); if (!entityHeaders.isEmpty()) { entityHeaders .entrySet() .stream() .filter(entry -> !responseHeaders.containsKey(entry.getKey())) .forEach(entry -> responseHeaders.put(entry.getKey(), entry.getValue())); } return writeBody(httpEntity.getBody(), bodyType, exchange); }); }
/** * Creates a new {@link DefaultProcessorWrapper} with the given {@link ResourceProcessor}. * * @param processor must not be {@literal null}. */ public DefaultProcessorWrapper(ResourceProcessor<?> processor) { Assert.notNull(processor); this.processor = processor; this.targetType = ResolvableType.forClass(ResourceProcessor.class, processor.getClass()).getGeneric(0); }
@Override public boolean supportsEventType(ResolvableType resolvableType) { Class<?> type = resolvableType.getRawClass(); if (type == null) { return false; } return ApplicationStartedEvent.class.isAssignableFrom(type) || ApplicationFailedEvent.class.isAssignableFrom(type); }
@Override public boolean supports(HandlerResult result) { Class<?> returnType = result.getReturnType().getRawClass(); if (isSupportedType(returnType)) { return true; } else { ReactiveAdapter adapter = getAdapterRegistry().getAdapterFrom(returnType, result.getReturnValue()); if (adapter != null && !adapter.getDescriptor().isMultiValue() && !adapter.getDescriptor().isNoValue()) { ResolvableType genericType = result.getReturnType().getGeneric(0); return isSupportedType(genericType.getRawClass()); } } return false; }
@Override public boolean supportsReturnType(MethodParameter returnType) { if (ResponseBodyEmitter.class.isAssignableFrom(returnType.getParameterType())) { return true; } else if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) { Class<?> bodyType = ResolvableType.forMethodParameter(returnType).getGeneric(0).resolve(); return (bodyType != null && ResponseBodyEmitter.class.isAssignableFrom(bodyType)); } return false; }
private ResolvableType getResolvableType(ApplicationEvent event) { ResolvableType payloadType = null; if (event instanceof PayloadApplicationEvent) { PayloadApplicationEvent<?> payloadEvent = (PayloadApplicationEvent<?>) event; payloadType = payloadEvent.getResolvableType().as(PayloadApplicationEvent.class).getGeneric(0); } for (ResolvableType declaredEventType : this.declaredEventTypes) { if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass()) && payloadType != null) { if (declaredEventType.isAssignableFrom(payloadType)) { return declaredEventType; } } if (declaredEventType.getRawClass().isAssignableFrom(event.getClass())) { return declaredEventType; } } return null; }
private Object createHttpEntity( Object body, ResolvableType entityType, ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); if (RequestEntity.class == entityType.getRawClass()) { return new RequestEntity<>(body, headers, request.getMethod(), request.getURI()); } else { return new HttpEntity<>(body, headers); } }
@Test public void decode() throws InterruptedException { Stream<ByteBuffer> source = Streams.just(Buffer.wrap("{\"foo\": \"foofoo\", \"bar\": \"barbar\"}").byteBuffer()); List<Object> results = Streams.wrap(decoder.decode(source, ResolvableType.forClass(Pojo.class), null)) .toList() .await(); assertEquals(1, results.size()); assertEquals("foofoo", ((Pojo) results.get(0)).getFoo()); }
private List<ResolvableType> resolveDeclaredEventTypes() { int count = this.method.getParameterCount(); if (count > 1) { throw new IllegalStateException( "Maximum one parameter is allowed for event listener method: " + this.method); } EventListener ann = getEventListener(); if (ann != null && ann.classes().length > 0) { List<ResolvableType> types = new ArrayList<>(); for (Class<?> eventType : ann.classes()) { types.add(ResolvableType.forClass(eventType)); } return types; } else { if (count == 0) { throw new IllegalStateException( "Event parameter is mandatory for event listener method: " + this.method); } return Collections.singletonList(ResolvableType.forMethodParameter(this.method, 0)); } }
@Override public Mono<Object> resolveArgument( MethodParameter param, BindingContext bindingContext, ServerWebExchange exchange) { ResolvableType entityType = ResolvableType.forMethodParameter(param); MethodParameter bodyParameter = new MethodParameter(param); bodyParameter.increaseNestingLevel(); return readBody(bodyParameter, false, bindingContext, exchange) .map(body -> createHttpEntity(body, entityType, exchange)) .defaultIfEmpty(createHttpEntity(null, entityType, exchange)); }
/** * Returns whether the given {@link Resources} instance matches the given {@link * ResolvableType}. We predict this by inspecting the first element of the content of the {@link * Resources}. * * @param resources the {@link Resources} to inspect. * @param target that target {@link ResolvableType}. * @return */ static boolean isValueTypeMatch(Resources<?> resources, ResolvableType target) { if (resources == null) { return false; } Collection<?> content = resources.getContent(); if (content.isEmpty()) { return false; } ResolvableType superType = null; for (Class<?> resourcesType : Arrays.<Class<?>>asList(resources.getClass(), Resources.class)) { superType = ResolvableType.forClass(resourcesType, getRawType(target)); if (superType != null) { break; } } if (superType == null) { return false; } Object element = content.iterator().next(); ResolvableType resourceType = superType.getGeneric(0); if (element instanceof Resource) { return ResourceProcessorWrapper.isValueTypeMatch((Resource<?>) element, resourceType); } else if (element instanceof EmbeddedWrapper) { return isRawTypeAssignable(resourceType, ((EmbeddedWrapper) element).getRelTargetType()); } return false; }
@SuppressWarnings("unchecked") protected H createHealthIndicator(S source) { Class<?>[] generics = ResolvableType.forClass(CompositeHealthIndicatorConfiguration.class, getClass()) .resolveGenerics(); Class<H> indicatorClass = (Class<H>) generics[0]; Class<S> sourceClass = (Class<S>) generics[1]; try { return indicatorClass.getConstructor(sourceClass).newInstance(source); } catch (Exception ex) { throw new IllegalStateException( "Unable to create indicator " + indicatorClass + " for source " + sourceClass, ex); } }
/** * Creates a new {@link ResourceProcessorInvoker} to consider the given {@link ResourceProcessor} * to post-process the controller methods return value to before invoking the delegate. * * @param processors the {@link ResourceProcessor}s to be considered, must not be {@literal null}. */ public ResourceProcessorInvoker(Collection<ResourceProcessor<?>> processors) { Assert.notNull(processors, "ResourceProcessors must not be null!"); this.processors = new ArrayList<ProcessorWrapper>(); for (ResourceProcessor<?> processor : processors) { ResolvableType processorType = ResolvableType.forClass(ResourceProcessor.class, processor.getClass()); Class<?> rawType = processorType.getGeneric(0).resolve(); if (Resource.class.isAssignableFrom(rawType)) { this.processors.add(new ResourceProcessorWrapper(processor)); } else if (Resources.class.isAssignableFrom(rawType)) { this.processors.add(new ResourcesProcessorWrapper(processor)); } else { this.processors.add(new DefaultProcessorWrapper(processor)); } } Collections.sort(this.processors, AnnotationAwareOrderComparator.INSTANCE); }
private static ResolvableType findGenericType(ResolvableType source, Class<?> type) { Class<?> rawType = getRawType(source); if (Object.class.equals(rawType)) { return null; } if (rawType.equals(type)) { return source; } return findGenericType(source.getSuperType(), type); }
@Test public void decode() { DataBuffer fooBuffer = stringBuffer("foo"); DataBuffer barBuffer = stringBuffer("bar"); Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer); Flux<ByteBuffer> output = this.decoder.decode( source, ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class), null, Collections.emptyMap()); StepVerifier.create(output) .expectNext(ByteBuffer.wrap("foo".getBytes()), ByteBuffer.wrap("bar".getBytes())) .expectComplete() .verify(); }
private ResolvableType resolveVariable(TypeVariable<?> typeVariable, ResolvableType contextType) { ResolvableType resolvedType; if (contextType.hasGenerics()) { resolvedType = ResolvableType.forType(typeVariable, contextType); if (resolvedType.resolve() != null) { return resolvedType; } } resolvedType = resolveVariable(typeVariable, contextType.getSuperType()); if (resolvedType.resolve() != null) { return resolvedType; } for (ResolvableType ifc : contextType.getInterfaces()) { resolvedType = resolveVariable(typeVariable, ifc); if (resolvedType.resolve() != null) { return resolvedType; } } return ResolvableType.NONE; }
@Override public boolean supportsEventType(ResolvableType eventType) { for (ResolvableType declaredEventType : this.declaredEventTypes) { if (declaredEventType.isAssignableFrom(eventType)) { return true; } else if (PayloadApplicationEvent.class.isAssignableFrom(eventType.getRawClass())) { ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric(); if (declaredEventType.isAssignableFrom(payloadType)) { return true; } } } return eventType.hasUnresolvableGenerics(); }
public SmartGenericTestEvent(Object source, T payload) { super(source, payload); this.resolvableType = ResolvableType.forClassWithGenerics(getClass(), payload.getClass()); }
/** * Invokes all {@link ResourceProcessor} instances registered for the type of the given value. * * @param value must not be {@literal null}. * @return */ public <T extends ResourceSupport> T invokeProcessorsFor(T value) { Assert.notNull(value, "Value must not be null!"); return invokeProcessorsFor(value, ResolvableType.forClass(value.getClass())); }
private static Class<?> getRawType(ResolvableType type) { Class<?> rawType = type.getRawClass(); return rawType == null ? Object.class : rawType; }
/** * Tests for {@link MockDefinition}. * * @author Phillip Webb */ public class MockDefinitionTests { private static final ResolvableType EXAMPLE_SERVICE_TYPE = ResolvableType.forClass(ExampleService.class); @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void classToMockMustNotBeNull() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("TypeToMock must not be null"); new MockDefinition(null, null, null, null, false, null, null); } @Test public void createWithDefaults() throws Exception { MockDefinition definition = new MockDefinition(null, EXAMPLE_SERVICE_TYPE, null, null, false, null, null); assertThat(definition.getName()).isNull(); assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getExtraInterfaces()).isEmpty(); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS); assertThat(definition.isSerializable()).isFalse(); assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); assertThat(definition.getQualifier()).isNull(); } @Test public void createExplicit() throws Exception { QualifierDefinition qualifier = mock(QualifierDefinition.class); MockDefinition definition = new MockDefinition( "name", EXAMPLE_SERVICE_TYPE, new Class<?>[] {ExampleExtraInterface.class}, Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, qualifier); assertThat(definition.getName()).isEqualTo("name"); assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getExtraInterfaces()).containsExactly(ExampleExtraInterface.class); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); assertThat(definition.isSerializable()).isTrue(); assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); assertThat(definition.isProxyTargetAware()).isFalse(); assertThat(definition.getQualifier()).isEqualTo(qualifier); } @Test public void createMock() throws Exception { MockDefinition definition = new MockDefinition( "name", EXAMPLE_SERVICE_TYPE, new Class<?>[] {ExampleExtraInterface.class}, Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, null); ExampleService mock = definition.createMock(); MockCreationSettings<?> settings = SpringBootMockUtil.getMockSettings(mock); assertThat(mock).isInstanceOf(ExampleService.class); assertThat(mock).isInstanceOf(ExampleExtraInterface.class); assertThat(settings.getMockName().toString()).isEqualTo("name"); assertThat(settings.getDefaultAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS.get()); assertThat(settings.isSerializable()).isTrue(); assertThat(MockReset.get(mock)).isEqualTo(MockReset.BEFORE); } }