/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.module.extension.internal.runtime.operation;
import static org.mule.runtime.api.message.NullAttributes.NULL_ATTRIBUTES;
import static org.mule.runtime.api.metadata.MediaType.ANY;
import static org.mule.runtime.core.util.SystemUtils.getDefaultEncoding;
import static org.mule.runtime.core.util.message.MessageUtils.toMessageCollection;
import static org.mule.runtime.core.util.message.MessageUtils.toMessageIterator;
import static org.mule.runtime.core.util.message.MessageUtils.streamingContent;
import static org.mule.runtime.module.extension.internal.ExtensionProperties.ENCODING_PARAMETER_NAME;
import static org.mule.runtime.module.extension.internal.ExtensionProperties.MIME_TYPE_PARAMETER_NAME;
import static org.mule.runtime.module.extension.internal.util.MuleExtensionUtils.returnsListOfMessages;
import org.mule.runtime.api.message.Message;
import org.mule.runtime.api.meta.model.ComponentModel;
import org.mule.runtime.api.metadata.DataType;
import org.mule.runtime.api.metadata.MediaType;
import org.mule.runtime.api.streaming.CursorProvider;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.MuleContext;
import org.mule.runtime.core.streaming.CursorProviderFactory;
import org.mule.runtime.core.util.message.MessageUtils;
import org.mule.runtime.extension.api.runtime.operation.Result;
import org.mule.runtime.module.extension.internal.runtime.ExecutionContextAdapter;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Iterator;
import java.util.Optional;
/**
* Base class for {@link ReturnDelegate} implementations.
* <p/>
* Contains the logic for taking an operation's output value and turn it into a {@link Message} which not only contains the
* updated payload but also the proper {@link DataType} and attributes.
* <p>
* It also consider the case in which the value is a {@code List<Result>} which should be turned into a {@code List<Message>}.
* For any of this cases, it also allows specifying a {@link CursorProviderFactory} which will transform the streaming payload
* values into {@link CursorProvider} instances. As said before, this is also applied then the value is a message or list of
* them
*
* @since 4.0
*/
abstract class AbstractReturnDelegate implements ReturnDelegate {
protected final MuleContext muleContext;
private final boolean returnsListOfMessages;
private final CursorProviderFactory cursorProviderFactory;
/**
* Creates a new instance
*
* @param componentModel the component which produces the return value
* @param cursorProviderFactory the {@link CursorProviderFactory} to use when a message is doing cursor based streaming. Can be {@code null}
* @param muleContext the {@link MuleContext} of the owning application
*/
protected AbstractReturnDelegate(ComponentModel componentModel,
CursorProviderFactory cursorProviderFactory,
MuleContext muleContext) {
returnsListOfMessages = returnsListOfMessages(componentModel);
this.muleContext = muleContext;
this.cursorProviderFactory = cursorProviderFactory;
}
protected Message toMessage(Object value, ExecutionContextAdapter operationContext) {
final MediaType mediaType = resolveMediaType(value, operationContext);
final Event event = operationContext.getEvent();
if (value instanceof Result) {
return MessageUtils.toMessage((Result) value, mediaType, cursorProviderFactory, event);
} else {
if (value instanceof Collection && returnsListOfMessages) {
value = toMessageCollection((Collection<Result>) value, mediaType, cursorProviderFactory, event);
} else if (value instanceof Iterator && returnsListOfMessages) {
value = toMessageIterator((Iterator<Result>) value, mediaType, cursorProviderFactory, event);
}
return Message.builder()
.payload(streamingContent(value, cursorProviderFactory, event))
.mediaType(mediaType)
.attributes(NULL_ATTRIBUTES).build();
}
}
/**
* If provided, mimeType and encoding configured as operation parameters will take precedence over what comes with the message's
* {@link DataType}.
*
* @param value
* @param operationContext
* @return
*/
private MediaType resolveMediaType(Object value, ExecutionContextAdapter<ComponentModel> operationContext) {
Charset existingEncoding = getDefaultEncoding(muleContext);
MediaType mediaType = null;
if (value instanceof Result) {
final Optional<MediaType> optionalMediaType = ((Result) value).getMediaType();
if (optionalMediaType.isPresent()) {
mediaType = optionalMediaType.get();
if (mediaType.getCharset().isPresent()) {
existingEncoding = mediaType.getCharset().get();
}
}
}
if (mediaType == null) {
mediaType = ANY;
}
if (operationContext.hasParameter(MIME_TYPE_PARAMETER_NAME)) {
mediaType = MediaType.parse(operationContext.getParameter(MIME_TYPE_PARAMETER_NAME));
}
if (operationContext.hasParameter(ENCODING_PARAMETER_NAME)) {
mediaType =
mediaType.withCharset(Charset.forName(operationContext.getParameter(ENCODING_PARAMETER_NAME)));
} else {
mediaType = mediaType.withCharset(existingEncoding);
}
return mediaType;
}
}