All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.support.AbstractExchange Maven / Gradle / Ivy

The newest version!
/*
 * 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.support;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.camel.CamelContext;
import org.apache.camel.CamelExecutionException;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePattern;
import org.apache.camel.ExchangePropertyKey;
import org.apache.camel.Message;
import org.apache.camel.MessageHistory;
import org.apache.camel.SafeCopyProperty;
import org.apache.camel.spi.UnitOfWork;
import org.apache.camel.spi.VariableRepository;
import org.apache.camel.trait.message.MessageTrait;
import org.apache.camel.trait.message.RedeliveryTraitPayload;
import org.apache.camel.util.ObjectHelper;

/**
 * Base class for the two official and only implementations of {@link Exchange}, the {@link DefaultExchange} and
 * {@link DefaultPooledExchange}.
 *
 * Camel end users should use {@link DefaultExchange} if creating an {@link Exchange} manually. However that is more
 * seldom to use, as exchanges are created via {@link Endpoint}.
 *
 * @see DefaultExchange
 */
abstract class AbstractExchange implements Exchange {
    protected final EnumMap internalProperties;

    protected final CamelContext context;
    protected Map properties; // create properties on-demand as we use internal properties mostly
    protected Message in;
    protected Message out;
    protected Exception exception;
    protected String exchangeId;
    protected ExchangePattern pattern;
    protected boolean routeStop;
    protected boolean rollbackOnly;
    protected boolean rollbackOnlyLast;
    protected Map safeCopyProperties;
    protected ExchangeVariableRepository variableRepository;
    private final ExtendedExchangeExtension privateExtension;
    private RedeliveryTraitPayload externalRedelivered = RedeliveryTraitPayload.UNDEFINED_REDELIVERY;

    protected AbstractExchange(CamelContext context, EnumMap internalProperties,
                               Map properties) {
        this.context = context;
        this.internalProperties = new EnumMap<>(internalProperties);
        this.privateExtension = new ExtendedExchangeExtension(this);
        this.properties = safeCopyProperties(properties);
    }

    protected AbstractExchange(CamelContext context) {
        this(context, ExchangePattern.InOnly);
    }

    protected AbstractExchange(CamelContext context, ExchangePattern pattern) {
        this.context = context;
        this.pattern = pattern;

        internalProperties = new EnumMap<>(ExchangePropertyKey.class);
        privateExtension = new ExtendedExchangeExtension(this);
    }

    protected AbstractExchange(Exchange parent) {
        this.context = parent.getContext();
        this.pattern = parent.getPattern();

        internalProperties = new EnumMap<>(ExchangePropertyKey.class);

        privateExtension = new ExtendedExchangeExtension(this);
        privateExtension.setFromEndpoint(parent.getFromEndpoint());
        privateExtension.setFromRouteId(parent.getFromRouteId());
        privateExtension.setUnitOfWork(parent.getUnitOfWork());
    }

    @SuppressWarnings("CopyConstructorMissesField")
    protected AbstractExchange(AbstractExchange parent) {
        this.context = parent.getContext();
        this.pattern = parent.getPattern();

        this.internalProperties = new EnumMap<>(parent.internalProperties);

        privateExtension = new ExtendedExchangeExtension(this);
        privateExtension.setFromEndpoint(parent.getFromEndpoint());
        privateExtension.setFromRouteId(parent.getFromRouteId());
        privateExtension.setUnitOfWork(parent.getUnitOfWork());

        setIn(parent.getIn().copy());

        if (parent.hasOut()) {
            setOut(parent.getOut().copy());
        }

        setException(parent.exception);
        setRouteStop(parent.routeStop);
        setRollbackOnly(parent.rollbackOnly);
        setRollbackOnlyLast(parent.rollbackOnlyLast);

        privateExtension.setNotifyEvent(parent.getExchangeExtension().isNotifyEvent());
        privateExtension.setRedeliveryExhausted(parent.getExchangeExtension().isRedeliveryExhausted());
        privateExtension.setErrorHandlerHandled(parent.getExchangeExtension().getErrorHandlerHandled());
        privateExtension.setStreamCacheDisabled(parent.getExchangeExtension().isStreamCacheDisabled());

        if (parent.hasVariables()) {
            if (this.variableRepository == null) {
                this.variableRepository = new ExchangeVariableRepository(getContext());
            }
            this.variableRepository.copyFrom(parent.variableRepository);
        }
        if (parent.hasProperties()) {
            this.properties = safeCopyProperties(parent.properties);
        }
        if (parent.hasSafeCopyProperties()) {
            this.safeCopyProperties = parent.copySafeCopyProperties();
        }
    }

    @Override
    public long getCreated() {
        return getClock().getCreated();
    }

    abstract AbstractExchange newCopy();

    @Override
    public Exchange copy() {
        AbstractExchange exchange = newCopy();

        if (getContext().isMessageHistory()) {
            exchange.internalProperties.computeIfPresent(ExchangePropertyKey.MESSAGE_HISTORY,
                    (k, v) -> new CopyOnWriteArrayList<>((List) v));
        }

        return exchange;
    }

    @Override
    public CamelContext getContext() {
        return context;
    }

    @Override
    public Object getProperty(ExchangePropertyKey key) {
        return internalProperties.get(key);
    }

    @Override
    public  T getProperty(ExchangePropertyKey key, Class type) {
        Object value = getProperty(key);
        return evalPropertyValue(type, value);
    }

    @SuppressWarnings("unchecked")
    private  T evalPropertyValue(final Class type, final Object value) {
        if (value == null) {
            // let's avoid NullPointerException when converting to boolean for null values
            if (boolean.class == type) {
                return (T) Boolean.FALSE;
            }
            return null;
        }

        // eager same instance type test to avoid the overhead of invoking the type converter
        // if already is the same type
        if (type.isInstance(value)) {
            return (T) value;
        }

        return ExchangeHelper.convertToType(this, type, value);
    }

    // TODO: fix re-assignment of the value instance here.
    @SuppressWarnings("unchecked")
    private  T evalPropertyValue(final Object defaultValue, final Class type, Object value) {
        if (value == null) {
            value = defaultValue;
        }
        if (value == null) {
            // let's avoid NullPointerException when converting to boolean for null values
            if (boolean.class == type) {
                return (T) Boolean.FALSE;
            }
            return null;
        }

        // eager same instance type test to avoid the overhead of invoking the type converter
        // if already is the same type
        if (type.isInstance(value)) {
            return (T) value;
        }

        return ExchangeHelper.convertToType(this, type, value);
    }

    @Override
    public  T getProperty(ExchangePropertyKey key, Object defaultValue, Class type) {
        Object value = getProperty(key);
        return evalPropertyValue(defaultValue, type, value);
    }

    @Override
    public void setProperty(ExchangePropertyKey key, Object value) {
        internalProperties.put(key, value);
    }

    @Override
    public Object removeProperty(ExchangePropertyKey key) {
        return internalProperties.remove(key);
    }

    @Override
    public Object getProperty(String name) {
        Object answer = null;
        ExchangePropertyKey key = ExchangePropertyKey.asExchangePropertyKey(name);
        if (key != null) {
            answer = internalProperties.get(key);
            // if the property is not an internal then fallback to lookup in the properties map
        }
        if (answer == null && properties != null) {
            answer = properties.get(name);
        }
        return answer;
    }

    @Override
    public  T getProperty(String name, Class type) {
        Object value = getProperty(name);
        return evalPropertyValue(type, value);
    }

    @Override
    public  T getProperty(String name, Object defaultValue, Class type) {
        Object value = getProperty(name);
        return evalPropertyValue(defaultValue, type, value);
    }

    @Override
    public void setProperty(String name, Object value) {
        ExchangePropertyKey key = ExchangePropertyKey.asExchangePropertyKey(name);
        if (key != null) {
            setProperty(key, value);
        } else if (value != null) {
            // avoid the NullPointException
            if (properties == null) {
                this.properties = new ConcurrentHashMap<>(8);
            }
            properties.put(name, value);
        } else if (properties != null) {
            // if the value is null, we just remove the key from the map
            properties.remove(name);
        }
    }

    void setProperties(Map properties) {
        if (this.properties == null) {
            this.properties = new ConcurrentHashMap<>(8);
        } else {
            this.properties.clear();
        }
        this.properties.putAll(properties);
    }

    @Override
    public Object removeProperty(String name) {
        ExchangePropertyKey key = ExchangePropertyKey.asExchangePropertyKey(name);
        if (key != null) {
            return removeProperty(key);
        }
        if (!hasProperties()) {
            return null;
        }
        return properties.remove(name);
    }

    @Override
    public boolean removeProperties(String pattern) {
        return removeProperties(pattern, (String[]) null);
    }

    @Override
    public boolean removeProperties(String pattern, String... excludePatterns) {
        // special optimized
        if (excludePatterns == null && "*".equals(pattern)) {
            if (properties != null) {
                properties.clear();
            }
            internalProperties.clear();
            return true;
        }

        boolean matches = false;
        for (ExchangePropertyKey epk : ExchangePropertyKey.values()) {
            String key = epk.getName();
            if (PatternHelper.matchPattern(key, pattern)) {
                if (excludePatterns != null && PatternHelper.isExcludePatternMatch(key, excludePatterns)) {
                    continue;
                }
                matches = true;
                internalProperties.remove(epk);
            }
        }

        // store keys to be removed as we cannot loop and remove at the same time in implementations such as HashMap
        if (properties != null) {
            Set toBeRemoved = PatternHelper.matchingSet(properties, pattern, excludePatterns);

            if (toBeRemoved != null) {
                matches = true;
                if (toBeRemoved.size() == properties.size()) {
                    // special optimization when all should be removed
                    properties.clear();
                } else {
                    for (String key : toBeRemoved) {
                        properties.remove(key);
                    }
                }
            }
        }

        return matches;
    }

    @Override
    public Map getProperties() {
        if (properties == null) {
            this.properties = new ConcurrentHashMap<>(8);
        }
        return properties;
    }

    private Map copySafeCopyProperties() {
        Map copy = new ConcurrentHashMap<>();
        for (Map.Entry entry : this.safeCopyProperties.entrySet()) {
            copy.put(entry.getKey(), entry.getValue().safeCopy());
        }

        return copy;
    }

    @Override
    public Map getAllProperties() {
        // include also internal properties (creates a new map)
        Map map = getInternalProperties();
        if (properties != null && !properties.isEmpty()) {
            map.putAll(properties);
        }
        return map;
    }

    @Override
    public boolean hasProperties() {
        return properties != null && !properties.isEmpty();
    }

    private boolean hasSafeCopyProperties() {
        return safeCopyProperties != null && !safeCopyProperties.isEmpty();
    }

    @Override
    public Object getVariable(String name) {
        VariableRepository repo = null;
        final String id = ExchangeHelper.getVariableRepositoryId(name);
        if (id != null) {
            repo = ExchangeHelper.getVariableRepository(this, id);
            name = ExchangeHelper.resolveVariableRepositoryName(this, name, id);
        }
        if (repo != null && name != null) {
            return repo.getVariable(name);
        } else if (variableRepository != null) {
            return variableRepository.getVariable(name);
        }
        return null;
    }

    @Override
    public  T getVariable(String name, Class type) {
        Object value = getVariable(name);
        return evalPropertyValue(type, value);
    }

    @Override
    public  T getVariable(String name, Object defaultValue, Class type) {
        Object value = getVariable(name);
        return evalPropertyValue(defaultValue, type, value);
    }

    @Override
    public void setVariable(String name, Object value) {
        VariableRepository repo = null;
        final String id = ExchangeHelper.getVariableRepositoryId(name);
        if (id != null) {
            repo = ExchangeHelper.getVariableRepository(this, id);
            name = ExchangeHelper.resolveVariableRepositoryName(this, name, id);
        }
        if (repo != null) {
            repo.setVariable(name, value);
        } else {
            if (variableRepository == null) {
                variableRepository = new ExchangeVariableRepository(getContext());
            }
            variableRepository.setVariable(name, value);
        }
    }

    @Override
    public Object removeVariable(String name) {
        VariableRepository repo = null;
        final String id = ExchangeHelper.getVariableRepositoryId(name);
        if (id != null) {
            repo = ExchangeHelper.getVariableRepository(this, id);
            name = ExchangeHelper.resolveVariableRepositoryName(this, name, id);
        }
        if (repo != null) {
            return repo.removeVariable(name);
        } else if (variableRepository != null) {
            if ("*".equals(name)) {
                variableRepository.clear();
                return null;
            }
            return variableRepository.removeVariable(name);
        }
        return null;
    }

    @Override
    public Map getVariables() {
        if (variableRepository == null) {
            // force creating variables
            variableRepository = new ExchangeVariableRepository(getContext());
        }
        return variableRepository.getVariables();
    }

    @Override
    public boolean hasVariables() {
        if (variableRepository != null) {
            return variableRepository.hasVariables();
        }
        return false;
    }

    @Override
    public Message getIn() {
        if (in == null) {
            in = new DefaultMessage(getContext());
            configureMessage(in);
        }
        return in;
    }

    @Override
    public  T getIn(Class type) {
        Message in = getIn();

        // eager same instance type test to avoid the overhead of invoking the type converter
        // if already same type
        if (type.isInstance(in)) {
            return type.cast(in);
        }

        // fallback to use type converter
        return context.getTypeConverter().convertTo(type, this, in);
    }

     T getInOrNull(Class type) {
        if (in == null) {
            return null;
        }
        if (type.isInstance(in)) {
            return type.cast(in);
        }

        return null;
    }

    @Override
    public void setIn(Message in) {
        this.in = in;
        configureMessage(in);
    }

    @Override
    public Message getOut() {
        // lazy create
        if (out == null) {
            out = newOutMessage();
            configureMessage(out);
        }
        return out;
    }

    private Message newOutMessage() {
        if (in instanceof MessageSupport messageSupport) {
            return messageSupport.newInstance();
        }
        return new DefaultMessage(getContext());
    }

    @SuppressWarnings("deprecated")
    @Override
    public  T getOut(Class type) {
        if (!hasOut()) {
            return null;
        }

        Message out = getOut();

        // eager same instance type test to avoid the overhead of invoking the type converter
        // if already same type
        if (type.isInstance(out)) {
            return type.cast(out);
        }

        // fallback to use type converter
        return context.getTypeConverter().convertTo(type, this, out);
    }

    @SuppressWarnings("deprecated")
    @Override
    public boolean hasOut() {
        return out != null;
    }

    @SuppressWarnings("deprecated")
    @Override
    public void setOut(Message out) {
        this.out = out;
        configureMessage(out);
    }

    @Override
    public Message getMessage() {
        return hasOut() ? getOut() : getIn();
    }

    @Override
    public  T getMessage(Class type) {
        return hasOut() ? getOut(type) : getIn(type);
    }

    @Override
    public void setMessage(Message message) {
        if (hasOut()) {
            setOut(message);
        } else {
            setIn(message);
        }
    }

    @Override
    public Exception getException() {
        return exception;
    }

    @Override
    public  T getException(Class type) {
        return ObjectHelper.getException(type, exception);
    }

    @Override
    public void setException(Throwable t) {
        if (t == null) {
            this.exception = null;
        } else if (t instanceof Exception exception) {
            this.exception = exception;
        } else {
            // wrap throwable into an exception
            this.exception = CamelExecutionException.wrapCamelExecutionException(this, t);
        }
        if (t instanceof InterruptedException) {
            // mark the exchange as interrupted due to the interrupt exception
            privateExtension.setInterrupted(true);
        }
    }

    @Override
    public ExchangePattern getPattern() {
        return pattern;
    }

    @Override
    public void setPattern(ExchangePattern pattern) {
        this.pattern = pattern;
    }

    @Override
    public Endpoint getFromEndpoint() {
        return privateExtension.getFromEndpoint();
    }

    @Override
    public String getFromRouteId() {
        return privateExtension.getFromRouteId();
    }

    @Override
    public String getExchangeId() {
        if (exchangeId == null) {
            exchangeId = createExchangeId();
        }
        return exchangeId;
    }

    @Override
    public void setExchangeId(String id) {
        this.exchangeId = id;
    }

    @Override
    public boolean isFailed() {
        return exception != null;
    }

    @Override
    public boolean isTransacted() {
        return privateExtension.isTransacted();
    }

    @Override
    public boolean isRouteStop() {
        return routeStop;
    }

    @Override
    public void setRouteStop(boolean routeStop) {
        this.routeStop = routeStop;
    }

    @Override
    public boolean isExternalRedelivered() {
        if (externalRedelivered == RedeliveryTraitPayload.UNDEFINED_REDELIVERY) {
            Message message = getIn();

            externalRedelivered = (RedeliveryTraitPayload) message.getPayloadForTrait(MessageTrait.REDELIVERY);
        }

        return externalRedelivered == RedeliveryTraitPayload.IS_REDELIVERY;
    }

    @Override
    public boolean isRollbackOnly() {
        return rollbackOnly;
    }

    @Override
    public void setRollbackOnly(boolean rollbackOnly) {
        this.rollbackOnly = rollbackOnly;
    }

    @Override
    public boolean isRollbackOnlyLast() {
        return rollbackOnlyLast;
    }

    @Override
    public void setRollbackOnlyLast(boolean rollbackOnlyLast) {
        this.rollbackOnlyLast = rollbackOnlyLast;
    }

    @Override
    public UnitOfWork getUnitOfWork() {
        return privateExtension.getUnitOfWork();
    }

    /**
     * Configures the message after it has been set on the exchange
     */
    protected void configureMessage(Message message) {
        if (message instanceof MessageSupport messageSupport) {
            messageSupport.setExchange(this);
            messageSupport.setCamelContext(getContext());
        }
    }

    void copyInternalProperties(Exchange target) {
        ((AbstractExchange) target).internalProperties.putAll(internalProperties);
    }

    Map getInternalProperties() {
        Map map = new HashMap<>();
        for (ExchangePropertyKey key : ExchangePropertyKey.values()) {
            Object value = internalProperties.get(key);
            if (value != null) {
                map.put(key.getName(), value);
            }
        }
        return map;
    }

    protected String createExchangeId() {
        return context.getUuidGenerator().generateExchangeUuid();
    }

    @Override
    public final String toString() {
        // do not output information about the message as it may contain sensitive information
        if (exchangeId != null) {
            return "Exchange[" + exchangeId + "]";
        } else {
            return "Exchange[]";
        }
    }

    void setSafeCopyProperty(String key, SafeCopyProperty value) {
        if (value != null) {
            // avoid the NullPointException
            if (safeCopyProperties == null) {
                this.safeCopyProperties = new ConcurrentHashMap<>(2);
            }
            safeCopyProperties.put(key, value);
        } else if (safeCopyProperties != null) {
            // if the value is null, we just remove the key from the map
            safeCopyProperties.remove(key);
        }

    }

    @SuppressWarnings("unchecked")
     T getSafeCopyProperty(String key, Class type) {
        if (!hasSafeCopyProperties()) {
            return null;
        }
        Object value = safeCopyProperties.get(key);

        if (type.isInstance(value)) {
            return (T) value;
        }

        return ExchangeHelper.convertToType(this, type, value);
    }

    public ExtendedExchangeExtension getExchangeExtension() {
        return privateExtension;
    }

    private static Map safeCopyProperties(Map properties) {
        if (properties == null) {
            return null;
        }
        return new ConcurrentHashMap<>(properties);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy