blob: e314b776a4ef3d3e665bc803b457aa23f7b9000d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.camel.processor;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.ValidationException;
import org.apache.camel.spi.CamelInternalProcessorAdvice;
import org.apache.camel.spi.Contract;
import org.apache.camel.spi.DataType;
import org.apache.camel.spi.DataTypeAware;
import org.apache.camel.spi.Transformer;
import org.apache.camel.spi.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A {@link CamelInternalProcessorAdvice} which applies {@link Transformer} and {@link Validator}
* according to the data type Contract.
* <p/>
* The default camel {@link Message} implements {@link DataTypeAware} which
* holds a {@link DataType} to indicate current message type. If the input type
* declared by {@link org.apache.camel.model.InputTypeDefinition} is different from current IN message type,
* camel internal processor look for a Transformer which transforms from the current
* message type to the expected message type before routing.
* After routing, if the output type declared by {@link org.apache.camel.model.OutputTypeDefinition} is different
* from current OUT message (or IN message if no OUT), camel look for a Transformer and apply.
*
* @see Transformer
* @see Validator
* @see org.apache.camel.model.InputTypeDefinition
* @see org.apache.camel.model.OutputTypeDefinition
*/
public class ContractAdvice implements CamelInternalProcessorAdvice {
private static final Logger LOG = LoggerFactory.getLogger(ContractAdvice.class);
private Contract contract;
public ContractAdvice(Contract contract) {
this.contract = contract;
}
@Override
public Object before(Exchange exchange) throws Exception {
if (!(exchange.getIn() instanceof DataTypeAware)) {
return null;
}
try {
DataType to = contract.getInputType();
if (to != null) {
DataTypeAware target = (DataTypeAware) exchange.getIn();
DataType from = target.getDataType();
if (!to.equals(from)) {
LOG.debug("Looking for transformer for INPUT: from='{}', to='{}'", from, to);
doTransform(exchange.getIn(), from, to);
target.setDataType(to);
}
if (contract.isValidateInput()) {
doValidate(exchange.getIn(), to);
}
}
} catch (Exception e) {
exchange.setException(e);
}
return null;
}
@Override
public void after(Exchange exchange, Object data) throws Exception {
if (exchange.isFailed()) {
// TODO can we add FAULT_TYPE processing?
return;
}
Message target = exchange.getMessage();
if (!(target instanceof DataTypeAware)) {
return;
}
try {
DataType to = contract.getOutputType();
if (to != null) {
DataTypeAware typeAwareTarget = (DataTypeAware)target;
DataType from = typeAwareTarget.getDataType();
if (!to.equals(from)) {
LOG.debug("Looking for transformer for OUTPUT: from='{}', to='{}'", from, to);
doTransform(target, from, to);
typeAwareTarget.setDataType(to);
}
if (contract.isValidateOutput()) {
doValidate(target, to);
}
}
} catch (Exception e) {
exchange.setException(e);
}
}
private void doTransform(Message message, DataType from, DataType to) throws Exception {
if (from == null) {
// If 'from' is null, only Java-Java convertion is performed.
// It means if 'to' is other than Java, it's assumed to be already in expected type.
convertIfRequired(message, to);
return;
}
// transform into 'from' type before performing declared transformation
convertIfRequired(message, from);
if (applyMatchedTransformer(message, from, to)) {
// Found matched transformer. Java-Java transformer is also allowed.
return;
} else if (from.isJavaType()) {
// Try TypeConverter as a fallback for Java->Java transformation
convertIfRequired(message, to);
// If Java->Other transformation required but no transformer matched,
// then assume it's already in expected type, i.e. do nothing.
return;
} else if (applyTransformerChain(message, from, to)) {
// Other->Other transformation - found a transformer chain
return;
}
throw new IllegalArgumentException("No Transformer found for [from='" + from + "', to='" + to + "']");
}
private boolean convertIfRequired(Message message, DataType type) throws Exception {
// TODO for better performance it may be better to add TypeConverterTransformer
// into transformer registry automatically to avoid unnecessary scan in transformer registry
if (type != null && type.isJavaType() && type.getName() != null && message != null && message.getBody() != null) {
CamelContext context = message.getExchange().getContext();
Class<?> typeJava = getClazz(type.getName(), context);
if (!typeJava.isAssignableFrom(message.getBody().getClass())) {
LOG.debug("Converting to '{}'", typeJava.getName());
message.setBody(message.getMandatoryBody(typeJava));
return true;
}
}
return false;
}
private boolean applyTransformer(Transformer transformer, Message message, DataType from, DataType to) throws Exception {
if (transformer != null) {
LOG.debug("Applying transformer: from='{}', to='{}', transformer='{}'", from, to, transformer);
transformer.transform(message, from, to);
return true;
}
return false;
}
private boolean applyMatchedTransformer(Message message, DataType from, DataType to) throws Exception {
Transformer transformer = message.getExchange().getContext().resolveTransformer(from, to);
return applyTransformer(transformer, message, from, to);
}
private boolean applyTransformerChain(Message message, DataType from, DataType to) throws Exception {
CamelContext context = message.getExchange().getContext();
Transformer fromTransformer = context.resolveTransformer(from.getModel());
Transformer toTransformer = context.resolveTransformer(to.getModel());
if (fromTransformer != null && toTransformer != null) {
LOG.debug("Applying transformer 1/2: from='{}', to='{}', transformer='{}'", from, to, fromTransformer);
fromTransformer.transform(message, from, new DataType(Object.class));
LOG.debug("Applying transformer 2/2: from='{}', to='{}', transformer='{}'", from, to, toTransformer);
toTransformer.transform(message, new DataType(Object.class), to);
return true;
}
return false;
}
private Class<?> getClazz(String type, CamelContext context) throws Exception {
return context.getClassResolver().resolveMandatoryClass(type);
}
private void doValidate(Message message, DataType type) throws ValidationException {
Validator validator = message.getExchange().getContext().resolveValidator(type);
if (validator != null) {
LOG.debug("Applying validator: type='{}', validator='{}'", type, validator);
validator.validate(message, type);
} else {
throw new ValidationException(message.getExchange(), String.format("No Validator found for '%s'", type));
}
}
}