/** * 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.jcache.processor.aggregate; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.cache.Cache; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.component.jcache.JCacheConfiguration; import org.apache.camel.component.jcache.JCacheHelper; import org.apache.camel.component.jcache.JCacheManager; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.DefaultExchangeHolder; import org.apache.camel.spi.OptimisticLockingAggregationRepository; import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JCacheAggregationRepository extends ServiceSupport implements OptimisticLockingAggregationRepository { private static final Logger LOG = LoggerFactory.getLogger(JCacheAggregationRepository.class); private JCacheConfiguration configuration; private Cache<String, DefaultExchangeHolder> cache; private boolean optimistic; private boolean allowSerializedHeaders; private JCacheManager<String, DefaultExchangeHolder> cacheManager; public JCacheAggregationRepository() { this.configuration = new JCacheConfiguration(); } public JCacheConfiguration getConfiguration() { return configuration; } public void setConfiguration(JCacheConfiguration configuration) { this.configuration = configuration; } public String getCacheName() { return configuration.getCacheName(); } public void setCacheName(String cacheName) { configuration.setCacheName(cacheName); } public Cache<String, DefaultExchangeHolder> getCache() { return cache; } public void setCache(Cache<String, DefaultExchangeHolder> cache) { this.cache = cache; } public boolean isOptimistic() { return optimistic; } public void setOptimistic(boolean optimistic) { this.optimistic = optimistic; } public boolean isAllowSerializedHeaders() { return allowSerializedHeaders; } public void setAllowSerializedHeaders(boolean allowSerializedHeaders) { this.allowSerializedHeaders = allowSerializedHeaders; } @Override public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, Exchange newExchange) throws OptimisticLockingException { if (!optimistic) { throw new UnsupportedOperationException(); } LOG.trace("Adding an Exchange with ID {} for key {} in an optimistic manner.", newExchange.getExchangeId(), key); if (oldExchange == null) { DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(newExchange, true, allowSerializedHeaders); DefaultExchangeHolder oldHolder = cache.getAndPut(key, newHolder); if (oldHolder != null) { Exchange exchange = unmarshallExchange(camelContext, oldHolder); LOG.error("Optimistic locking failed for exchange with key {}: IMap#putIfAbsend returned Exchange with ID {}, while it's expected no exchanges to be returned", key, exchange != null ? exchange.getExchangeId() : "<null>"); throw new OptimisticLockingException(); } } else { DefaultExchangeHolder oldHolder = DefaultExchangeHolder.marshal(oldExchange, true, allowSerializedHeaders); DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(newExchange, true, allowSerializedHeaders); if (!cache.replace(key, oldHolder, newHolder)) { LOG.error("Optimistic locking failed for exchange with key {}: IMap#replace returned no Exchanges, while it's expected to replace one", key); throw new OptimisticLockingException(); } } LOG.trace("Added an Exchange with ID {} for key {} in optimistic manner.", newExchange.getExchangeId(), key); return oldExchange; } @Override public Exchange add(CamelContext camelContext, String key, Exchange exchange) { if (optimistic) { throw new UnsupportedOperationException(); } 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.getAndPut(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) { DefaultExchangeHolder holder = DefaultExchangeHolder.marshal(exchange, true, allowSerializedHeaders); if (optimistic) { LOG.trace("Removing an exchange with ID {} for key {} in an optimistic manner.", exchange.getExchangeId(), key); if (!cache.remove(key, holder)) { LOG.error("Optimistic locking failed for exchange with key {}: IMap#remove removed no Exchanges, while it's expected to remove one.", key); throw new OptimisticLockingException(); } LOG.trace("Removed an exchange with ID {} for key {} in an optimistic manner.", exchange.getExchangeId(), key); } else { cache.remove(key); } } @Override public void confirm(CamelContext camelContext, String exchangeId) { LOG.trace("Confirming an exchange with ID {}.", exchangeId); } @Override public Set<String> getKeys() { Set<String> keys = new HashSet<>(); Iterator<Cache.Entry<String, DefaultExchangeHolder>> entries = cache.iterator(); while (entries.hasNext()) { keys.add(entries.next().getKey()); } return Collections.unmodifiableSet(keys); } @Override protected void doStart() throws Exception { if (cache != null) { cacheManager = new JCacheManager<>(cache); } else { cacheManager = JCacheHelper.createManager( ObjectHelper.notNull(configuration, "configuration") ); cache = cacheManager.getCache(); } } @Override protected void doStop() throws Exception { cacheManager.close(); } protected Exchange unmarshallExchange(CamelContext camelContext, DefaultExchangeHolder holder) { Exchange exchange = null; if (holder != null) { exchange = new DefaultExchange(camelContext); DefaultExchangeHolder.unmarshal(exchange, holder); } return exchange; } }