/** * 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.component.infinispan.processor.aggregate; import java.util.Collections; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.DefaultExchangeHolder; import org.apache.camel.spi.RecoverableAggregationRepository; import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.ObjectHelper; import org.infinispan.client.hotrod.RemoteCacheManager; import org.infinispan.client.hotrod.configuration.Configuration; import org.infinispan.commons.api.BasicCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class InfinispanRemoteAggregationRepository extends ServiceSupport implements RecoverableAggregationRepository { private static final Logger LOG = LoggerFactory.getLogger(InfinispanRemoteAggregationRepository.class.getName()); private boolean useRecovery = true; private RemoteCacheManager manager; private String cacheName; private String deadLetterChannel; private long recoveryInterval = 5000; private int maximumRedeliveries = 3; private boolean allowSerializedHeaders; private BasicCache<String, DefaultExchangeHolder> cache; private Configuration configuration; /** * Creates new {@link InfinispanRemoteAggregationRepository} that defaults to non-optimistic locking * with recoverable behavior and a local Infinispan cache. */ public InfinispanRemoteAggregationRepository() { } /** * Creates new {@link InfinispanRemoteAggregationRepository} that defaults to non-optimistic locking * with recoverable behavior and a local Infinispan cache. * @param cacheName cache name */ public InfinispanRemoteAggregationRepository(final String cacheName) { this.cacheName = cacheName; } @Override public Exchange add(final CamelContext camelContext, final String key, final Exchange exchange) { LOG.trace("Adding an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(exchange, true, allowSerializedHeaders); DefaultExchangeHolder oldHolder = cache.put(key, newHolder); return unmarshallExchange(camelContext, oldHolder); } @Override public Exchange get(CamelContext camelContext, String key) { return unmarshallExchange(camelContext, cache.get(key)); } @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { LOG.trace("Removing an exchange with ID {} for key {} ", exchange.getExchangeId(), key); cache.remove(key); } @Override public void confirm(CamelContext camelContext, String exchangeId) { LOG.trace("Confirming an exchange with ID {}.", exchangeId); cache.remove(exchangeId); } @Override public Set<String> getKeys() { return Collections.unmodifiableSet(cache.keySet()); } @Override public Set<String> scan(CamelContext camelContext) { LOG.trace("Scanning for exchanges to recover in {} context", camelContext.getName()); Set<String> scanned = Collections.unmodifiableSet(cache.keySet()); LOG.trace("Found {} keys for exchanges to recover in {} context", scanned.size(), camelContext.getName()); return scanned; } @Override public Exchange recover(CamelContext camelContext, String exchangeId) { LOG.trace("Recovering an Exchange with ID {}.", exchangeId); return useRecovery ? unmarshallExchange(camelContext, cache.get(exchangeId)) : null; } @Override public void setRecoveryInterval(long interval, TimeUnit timeUnit) { this.recoveryInterval = timeUnit.toMillis(interval); } @Override public void setRecoveryInterval(long interval) { this.recoveryInterval = interval; } @Override public long getRecoveryIntervalInMillis() { return recoveryInterval; } @Override public void setUseRecovery(boolean useRecovery) { this.useRecovery = useRecovery; } @Override public boolean isUseRecovery() { return useRecovery; } @Override public void setDeadLetterUri(String deadLetterUri) { this.deadLetterChannel = deadLetterUri; } @Override public String getDeadLetterUri() { return deadLetterChannel; } @Override public void setMaximumRedeliveries(int maximumRedeliveries) { this.maximumRedeliveries = maximumRedeliveries; } @Override public int getMaximumRedeliveries() { return maximumRedeliveries; } @Override protected void doStart() throws Exception { if (maximumRedeliveries < 0) { throw new IllegalArgumentException("Maximum redelivery retries must be zero or a positive integer."); } if (recoveryInterval < 0) { throw new IllegalArgumentException("Recovery interval must be zero or a positive integer."); } if (ObjectHelper.isEmpty(configuration)) { manager = new RemoteCacheManager(); manager.start(); } else { manager = new RemoteCacheManager(configuration); manager.start(); } if (ObjectHelper.isEmpty(cacheName)) { cache = manager.getCache(); } else { cache = manager.getCache(cacheName); } } @Override protected void doStop() throws Exception { manager.stop(); } protected Exchange unmarshallExchange(CamelContext camelContext, DefaultExchangeHolder holder) { Exchange exchange = null; if (holder != null) { exchange = new DefaultExchange(camelContext); DefaultExchangeHolder.unmarshal(exchange, holder); } return exchange; } public RemoteCacheManager getManager() { return manager; } public void setManager(RemoteCacheManager manager) { this.manager = manager; } public String getCacheName() { return cacheName; } public void setCacheName(String cacheName) { this.cacheName = cacheName; } public String getDeadLetterChannel() { return deadLetterChannel; } public void setDeadLetterChannel(String deadLetterChannel) { this.deadLetterChannel = deadLetterChannel; } public boolean isAllowSerializedHeaders() { return allowSerializedHeaders; } public void setAllowSerializedHeaders(boolean allowSerializedHeaders) { this.allowSerializedHeaders = allowSerializedHeaders; } public BasicCache<String, DefaultExchangeHolder> getCache() { return cache; } public void setCache(BasicCache<String, DefaultExchangeHolder> cache) { this.cache = cache; } public Configuration getConfiguration() { return configuration; } public void setConfiguration(Configuration configuration) { this.configuration = configuration; } }