From 8716d70081ba42e862ea67a4c3ee45664ea35185 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Wed, 16 Oct 2013 01:04:42 +0400 Subject: [PATCH 01/53] Created. --- .../HazelcastAggregationRepository.java | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java new file mode 100644 index 0000000000000..fe72d04a1595f --- /dev/null +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -0,0 +1,237 @@ +package org.apache.camel.processor.aggregate.hazelcast; + + +import com.hazelcast.config.Config; +import com.hazelcast.config.XmlConfigBuilder; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.IMap; +import com.hazelcast.core.TransactionalMap; +import com.hazelcast.transaction.TransactionContext; +import com.hazelcast.transaction.TransactionOptions; +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.spi.OptimisticLockingAggregationRepository; +import org.apache.camel.spi.RecoverableAggregationRepository; +import org.apache.camel.support.ServiceSupport; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class HazelcastAggregationRepository extends ServiceSupport + implements RecoverableAggregationRepository, + OptimisticLockingAggregationRepository { + private boolean optimistic; + private boolean localHzInstance; + private boolean useRecovery; + private IMap cache; + private IMap persistedCache; + private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; + private HazelcastInstance hzInstance; + private String mapName; + private String completedSuffix = "-completed"; + private String deadLetterChannel; + private long recoveryInterval; + private int maximumRedeliveries; + + public HazelcastAggregationRepository(final String repositoryName) { + mapName = repositoryName; + optimistic = false; + localHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, boolean optimistic) { + this(repositoryName); + this.optimistic = optimistic; + localHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, HazelcastInstance hzInstanse) { + this (repositoryName, false); + this.hzInstance = hzInstanse; + localHzInstance = false; + } + + public HazelcastAggregationRepository(final String repositoryName, boolean optimistic, HazelcastInstance hzInstance) { + this(repositoryName, optimistic); + this.hzInstance = hzInstance; + } + + @Override + public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, Exchange newExchange) throws OptimisticLockingException { + if (!optimistic) { + throw new UnsupportedOperationException(); + } + if (oldExchange == null) { + if (cache.putIfAbsent(key, newExchange) != null) { + throw new OptimisticLockingException(); + } + } else { + if (!cache.replace(key, oldExchange, newExchange)) { + throw new OptimisticLockingException(); + } + } + return oldExchange; + } + + @Override + public Exchange add(CamelContext camelContext, String key, Exchange exchange) { + if (optimistic){ + throw new UnsupportedOperationException(); + } + Lock l = hzInstance.getLock(mapName); + try { + l.lock(); + return cache.put(key, exchange); + } finally { + l.unlock(); + } + } + + @Override + public Set scan(CamelContext camelContext) { + if (useRecovery) + return Collections.unmodifiableSet(persistedCache.keySet()); + else + return Collections.emptySet(); + } + + @Override + public Exchange recover(CamelContext camelContext, String exchangeId) { + return useRecovery ? persistedCache.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 + public Exchange get(CamelContext camelContext, String key) { + return cache.get(key); + } + + @Override + public void remove(CamelContext camelContext, String key, Exchange exchange) { + if (optimistic) { + if (!cache.remove(key, exchange)) { + throw new OptimisticLockingException(); + } + if (useRecovery) { + persistedCache.put(key, exchange); + } + } else { + if (useRecovery) { + // The only considerable case for transaction usage is fault tolerance: + // the transaction will be rolled back automatically (default timeout is 2 minutes) + // if no commit occurs during the timeout. So we are still consistent whether local node crashes. + + TransactionOptions tOpts = new TransactionOptions(); + + tOpts.setTransactionType(TransactionOptions.TransactionType.LOCAL); + TransactionContext tCtx = hzInstance.newTransactionContext(tOpts); + tCtx.beginTransaction(); + + TransactionalMap tCache = tCtx.getMap(cache.getName()); + TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); + + tCache.remove(key); + tPersistentCache.put(key, exchange); + + tCtx.commitTransaction(); + } else { + cache.remove(key); + } + } + } + + @Override + public void confirm(CamelContext camelContext, String exchangeId) { + persistedCache.remove(exchangeId); + } + + @Override + public Set getKeys() { + return Collections.unmodifiableSet(cache.keySet()); + } + + public void setCompletedSuffix(final String completedSuffix) { + this.completedSuffix = completedSuffix; + } + + public String getCompletedSuffix() { + return completedSuffix; + } + + @Override + protected void doStart() throws Exception { + if (localHzInstance) { + Config cfg = new XmlConfigBuilder().build(); + cfg.setProperty("hazelcast.version.check.enabled", "false"); + hzInstance = Hazelcast.newHazelcastInstance(cfg); + } + cache = hzInstance.getMap(mapName); + if (useRecovery) { + persistedCache = hzInstance.getMap(String.format("%s%s", mapName, completedSuffix)); + } + } + + @Override + protected void doStop() throws Exception { + if (useRecovery) { + persistedCache.clear(); + } + + cache.clear(); + + if (localHzInstance) { + hzInstance.getLifecycleService().shutdown(); + } + } +} From 3cbaec76b9db3551f8e86f83442e0140f4f8d918 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Wed, 16 Oct 2013 14:29:05 +0400 Subject: [PATCH 02/53] Persistent repository name handling updated to achieve robustness and consistency. --- .../HazelcastAggregationRepository.java | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index fe72d04a1595f..b1d498d29e751 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; +import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,34 +28,55 @@ public final class HazelcastAggregationRepository extends ServiceSupport implements RecoverableAggregationRepository, OptimisticLockingAggregationRepository { private boolean optimistic; - private boolean localHzInstance; + private boolean useLocalHzInstance; private boolean useRecovery; private IMap cache; private IMap persistedCache; private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; private HazelcastInstance hzInstance; private String mapName; - private String completedSuffix = "-completed"; + private String persistenceMapName; + private static final String COMPLETED_SUFFIX = "-completed"; private String deadLetterChannel; private long recoveryInterval; private int maximumRedeliveries; public HazelcastAggregationRepository(final String repositoryName) { mapName = repositoryName; + persistenceMapName = String.format("%s%s", mapName, COMPLETED_SUFFIX); optimistic = false; - localHzInstance = true; + useLocalHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName) { + mapName = repositoryName; + persistenceMapName = persistentRepositoryName; + optimistic = false; + useLocalHzInstance = true; } public HazelcastAggregationRepository(final String repositoryName, boolean optimistic) { this(repositoryName); this.optimistic = optimistic; - localHzInstance = true; + useLocalHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic) { + this(repositoryName, persistenRepositoryName); + this.optimistic = optimistic; + useLocalHzInstance = true; } public HazelcastAggregationRepository(final String repositoryName, HazelcastInstance hzInstanse) { this (repositoryName, false); this.hzInstance = hzInstanse; - localHzInstance = false; + useLocalHzInstance = false; + } + + public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, HazelcastInstance hzInstanse) { + this (repositoryName, persistenRepositoryName, false); + this.hzInstance = hzInstanse; + useLocalHzInstance = false; } public HazelcastAggregationRepository(final String repositoryName, boolean optimistic, HazelcastInstance hzInstance) { @@ -62,6 +84,11 @@ public HazelcastAggregationRepository(final String repositoryName, boolean optim this.hzInstance = hzInstance; } + public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic, HazelcastInstance hzInstance) { + this(repositoryName, persistenRepositoryName, optimistic); + this.hzInstance = hzInstance; + } + @Override public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, Exchange newExchange) throws OptimisticLockingException { if (!optimistic) { @@ -171,7 +198,6 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { // The only considerable case for transaction usage is fault tolerance: // the transaction will be rolled back automatically (default timeout is 2 minutes) // if no commit occurs during the timeout. So we are still consistent whether local node crashes. - TransactionOptions tOpts = new TransactionOptions(); tOpts.setTransactionType(TransactionOptions.TransactionType.LOCAL); @@ -201,27 +227,30 @@ public Set getKeys() { return Collections.unmodifiableSet(cache.keySet()); } - public void setCompletedSuffix(final String completedSuffix) { - this.completedSuffix = completedSuffix; - } - - public String getCompletedSuffix() { - return completedSuffix; + public String getPersistentRepositoryName() { + return getPersistentMapName(); } @Override protected void doStart() throws Exception { - if (localHzInstance) { + ObjectHelper.notEmpty(mapName, "repositoryName"); + if (useLocalHzInstance) { Config cfg = new XmlConfigBuilder().build(); cfg.setProperty("hazelcast.version.check.enabled", "false"); hzInstance = Hazelcast.newHazelcastInstance(cfg); + } else { + ObjectHelper.notNull(hzInstance, "hzInstanse"); } cache = hzInstance.getMap(mapName); if (useRecovery) { - persistedCache = hzInstance.getMap(String.format("%s%s", mapName, completedSuffix)); + persistedCache = hzInstance.getMap(persistenceMapName); } } + private String getPersistentMapName() { + return persistenceMapName; + } + @Override protected void doStop() throws Exception { if (useRecovery) { @@ -230,7 +259,7 @@ protected void doStop() throws Exception { cache.clear(); - if (localHzInstance) { + if (useLocalHzInstance) { hzInstance.getLifecycleService().shutdown(); } } From 76207ee8735c1deaa3e4ba6718ff0e8565bcca4c Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Mon, 21 Oct 2013 01:07:24 +0400 Subject: [PATCH 03/53] Lots of trace logging. --- .../HazelcastAggregationRepository.java | 71 ++++++++++++++----- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index b1d498d29e751..e53ea40f8a6a0 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -38,7 +38,7 @@ public final class HazelcastAggregationRepository extends ServiceSupport private String persistenceMapName; private static final String COMPLETED_SUFFIX = "-completed"; private String deadLetterChannel; - private long recoveryInterval; + private long recoveryInterval = 5000; private int maximumRedeliveries; public HazelcastAggregationRepository(final String repositoryName) { @@ -94,15 +94,22 @@ public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, if (!optimistic) { throw new UnsupportedOperationException(); } + LOG.trace("Adding an Exchange with ID {} for key {} in an optimistic manner.", newExchange.getExchangeId(), key); if (oldExchange == null) { - if (cache.putIfAbsent(key, newExchange) != null) { + final Exchange misbehaviorEx = cache.putIfAbsent(key, newExchange); + if (misbehaviorEx != null) { + 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, misbehaviorEx.getExchangeId()); throw new OptimisticLockingException(); } } else { if (!cache.replace(key, oldExchange, newExchange)) { + 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; } @@ -111,25 +118,35 @@ 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); Lock l = hzInstance.getLock(mapName); try { l.lock(); return cache.put(key, exchange); } finally { + LOG.trace("Adding an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); l.unlock(); } } @Override public Set scan(CamelContext camelContext) { - if (useRecovery) - return Collections.unmodifiableSet(persistedCache.keySet()); - else + if (useRecovery) { + LOG.trace("Scanning for exchanges to recover in {} context", camelContext.getName()); + Set scanned = Collections.unmodifiableSet(persistedCache.keySet()); + LOG.trace("Found {} keys for exchanges to recover in {} context", scanned.size(),camelContext.getName()); + return scanned; + } + else { + LOG.warn("What for to run recovery scans in {} context while repository {} is running in non-recoverable aggregation repository mode?!", + camelContext.getName(), mapName); return Collections.emptySet(); + } } @Override public Exchange recover(CamelContext camelContext, String exchangeId) { + LOG.trace("Recovering an Exchange with ID {}.", exchangeId); return useRecovery ? persistedCache.get(exchangeId) : null; } @@ -187,14 +204,23 @@ public Exchange get(CamelContext camelContext, String key) { @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { if (optimistic) { + LOG.trace("Removing an exchange with ID {} for key {} in an optimistic manner.", exchange.getExchangeId(), key); if (!cache.remove(key, exchange)) { + 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); if (useRecovery) { + LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", + exchange.getExchangeId(), key); persistedCache.put(key, exchange); + LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", + exchange.getExchangeId(), key); } } else { if (useRecovery) { + LOG.trace("Removing an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); // The only considerable case for transaction usage is fault tolerance: // the transaction will be rolled back automatically (default timeout is 2 minutes) // if no commit occurs during the timeout. So we are still consistent whether local node crashes. @@ -202,15 +228,31 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { tOpts.setTransactionType(TransactionOptions.TransactionType.LOCAL); TransactionContext tCtx = hzInstance.newTransactionContext(tOpts); - tCtx.beginTransaction(); - TransactionalMap tCache = tCtx.getMap(cache.getName()); - TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); + try { + + tCtx.beginTransaction(); + + TransactionalMap tCache = tCtx.getMap(cache.getName()); + TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); - tCache.remove(key); - tPersistentCache.put(key, exchange); + tCache.remove(key); + LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", + exchange.getExchangeId(), key); + tPersistentCache.put(key, exchange); - tCtx.commitTransaction(); + tCtx.commitTransaction(); + LOG.trace("Removed an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); + LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", + exchange.getExchangeId(), key); + } catch (Throwable throwable) { + tCtx.rollbackTransaction(); + + final String msg = String.format("Transaction with ID %s was rolled back for remove operation with a key %s and an Exchange ID %s.", + tCtx.getTxnId(), key, exchange.getExchangeId()); + LOG.warn(msg, throwable); + throw new RuntimeException(msg, throwable); + } } else { cache.remove(key); } @@ -219,6 +261,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { @Override public void confirm(CamelContext camelContext, String exchangeId) { + LOG.trace("Confirming an exchange with ID {}.", exchangeId); persistedCache.remove(exchangeId); } @@ -228,7 +271,7 @@ public Set getKeys() { } public String getPersistentRepositoryName() { - return getPersistentMapName(); + return persistenceMapName; } @Override @@ -247,10 +290,6 @@ protected void doStart() throws Exception { } } - private String getPersistentMapName() { - return persistenceMapName; - } - @Override protected void doStop() throws Exception { if (useRecovery) { From 0e807ce7ecbc5b6ee6ac56b05638ecf1606dd95c Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Mon, 21 Oct 2013 23:19:12 +0400 Subject: [PATCH 04/53] Tons of javadoc. --- .../HazelcastAggregationRepository.java | 100 ++++++++++++++++-- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index e53ea40f8a6a0..8bb0e5503fb2d 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -24,6 +24,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * A Hazelcast-based AggregationRepository implementing + * {@link RecoverableAggregationRepository} and {@link OptimisticLockingAggregationRepository}. + * Defaults to thread-safe (non-optimistic) locking and recoverable strategy. + * Hazelcast settings are given to an end-user and can be controlled with repositoryName and persistentRespositoryName, + * both are {@link com.hazelcast.core.IMap} <String, Exchange>. However HazelcastAggregationRepository + * can run it's own Hazelcast instance, but obviously no benefits of Hazelcast clustering are gained this way. + * If the {@link HazelcastAggregationRepository} uses it's own local {@link HazelcastInstance} it will destroy this + * instance on {@link #doStop()}. You should control {@link HazelcastInstance} lifecycle yourself whenever you instantiate + * {@link HazelcastAggregationRepository} passing a reference to the instance. + * + */ public final class HazelcastAggregationRepository extends ServiceSupport implements RecoverableAggregationRepository, OptimisticLockingAggregationRepository { @@ -39,15 +51,28 @@ public final class HazelcastAggregationRepository extends ServiceSupport private static final String COMPLETED_SUFFIX = "-completed"; private String deadLetterChannel; private long recoveryInterval = 5000; - private int maximumRedeliveries; - + private int maximumRedeliveries = 3; + + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior and a local Hazelcast instance. Recoverable repository name defaults to + * {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + */ public HazelcastAggregationRepository(final String repositoryName) { mapName = repositoryName; persistenceMapName = String.format("%s%s", mapName, COMPLETED_SUFFIX); optimistic = false; useLocalHzInstance = true; + useRecovery = true; } + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior and a local Hazelcast instance. + * @param repositoryName {@link IMap} repository name; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + */ public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName) { mapName = repositoryName; persistenceMapName = persistentRepositoryName; @@ -55,38 +80,80 @@ public HazelcastAggregationRepository(final String repositoryName, final String useLocalHzInstance = true; } + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior and a local Hazelcast instance. + * Recoverable repository name defaults to {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + * @param optimistic whether to use optimistic locking manner. + */ public HazelcastAggregationRepository(final String repositoryName, boolean optimistic) { this(repositoryName); this.optimistic = optimistic; useLocalHzInstance = true; } - public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic) { - this(repositoryName, persistenRepositoryName); + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior and a local Hazelcast instance. + * @param repositoryName {@link IMap} repository name; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + * @param optimistic whether to use optimistic locking manner. + */ + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName, boolean optimistic) { + this(repositoryName, persistentRepositoryName); this.optimistic = optimistic; useLocalHzInstance = true; } + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior. Recoverable repository name defaults to + * {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + * @param hzInstanse externally configured {@link HazelcastInstance}. + */ public HazelcastAggregationRepository(final String repositoryName, HazelcastInstance hzInstanse) { this (repositoryName, false); this.hzInstance = hzInstanse; useLocalHzInstance = false; } - public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, HazelcastInstance hzInstanse) { - this (repositoryName, persistenRepositoryName, false); + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior. + * @param repositoryName {@link IMap} repository name; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + * @param hzInstanse externally configured {@link HazelcastInstance}. + */ + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName, HazelcastInstance hzInstanse) { + this (repositoryName, persistentRepositoryName, false); this.hzInstance = hzInstanse; useLocalHzInstance = false; } + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior. + * Recoverable repository name defaults to {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + * @param optimistic whether to use optimistic locking manner; + * @param hzInstance externally configured {@link HazelcastInstance}. + */ public HazelcastAggregationRepository(final String repositoryName, boolean optimistic, HazelcastInstance hzInstance) { this(repositoryName, optimistic); this.hzInstance = hzInstance; + useLocalHzInstance = false; } - public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic, HazelcastInstance hzInstance) { - this(repositoryName, persistenRepositoryName, optimistic); + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior. + * @param repositoryName {@link IMap} repository name; + * @param optimistic whether to use optimistic locking manner; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + * @param hzInstance externally configured {@link HazelcastInstance}. + */ + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName, boolean optimistic, HazelcastInstance hzInstance) { + this(repositoryName, persistentRepositoryName, optimistic); this.hzInstance = hzInstance; + useLocalHzInstance = false; } @Override @@ -201,6 +268,14 @@ public Exchange get(CamelContext camelContext, String key) { return cache.get(key); } + /** + * This method performs transactional operation on removing the {@code exchange} + * from the operational storage and moving it into the persistent one if the {@link HazelcastAggregationRepository} + * runs in recoverable mode and {@code optimistic} is false. It will act at your own risk otherwise. + * @param camelContext the current CamelContext + * @param key the correlation key + * @param exchange the exchange to remove + */ @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { if (optimistic) { @@ -270,12 +345,21 @@ public Set getKeys() { return Collections.unmodifiableSet(cache.keySet()); } + /** + * @return Persistent repository {@link IMap} name; + */ public String getPersistentRepositoryName() { return persistenceMapName; } @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."); + } ObjectHelper.notEmpty(mapName, "repositoryName"); if (useLocalHzInstance) { Config cfg = new XmlConfigBuilder().build(); From 63f3523c2fb9bcf94aa36836f19de96ca52f5102 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 01:09:06 +0400 Subject: [PATCH 05/53] DefaultExchageHolders introduced. --- .../HazelcastAggregationRepository.java | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index 8bb0e5503fb2d..54dbb94953495 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -11,6 +11,9 @@ import com.hazelcast.transaction.TransactionOptions; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; +import org.apache.camel.builder.xml.DomResultHandlerFactory; +import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.impl.DefaultExchangeHolder; import org.apache.camel.spi.OptimisticLockingAggregationRepository; import org.apache.camel.spi.RecoverableAggregationRepository; import org.apache.camel.support.ServiceSupport; @@ -42,8 +45,8 @@ public final class HazelcastAggregationRepository extends ServiceSupport private boolean optimistic; private boolean useLocalHzInstance; private boolean useRecovery; - private IMap cache; - private IMap persistedCache; + private IMap cache; + private IMap persistedCache; private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; private HazelcastInstance hzInstance; private String mapName; @@ -163,14 +166,18 @@ public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, } LOG.trace("Adding an Exchange with ID {} for key {} in an optimistic manner.", newExchange.getExchangeId(), key); if (oldExchange == null) { - final Exchange misbehaviorEx = cache.putIfAbsent(key, newExchange); - if (misbehaviorEx != null) { + DefaultExchangeHolder holder = DefaultExchangeHolder.marshal(newExchange); + final DefaultExchangeHolder misbehaviorHolder = cache.putIfAbsent(key, holder); + if (misbehaviorHolder != null) { + Exchange misbehaviorEx = unmarshallExchange(camelContext, misbehaviorHolder); 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, misbehaviorEx.getExchangeId()); + key, misbehaviorEx != null ? misbehaviorEx.getExchangeId() : ""); throw new OptimisticLockingException(); } } else { - if (!cache.replace(key, oldExchange, newExchange)) { + DefaultExchangeHolder oldHolder = DefaultExchangeHolder.marshal(oldExchange); + DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(newExchange); + 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(); @@ -189,9 +196,11 @@ public Exchange add(CamelContext camelContext, String key, Exchange exchange) { Lock l = hzInstance.getLock(mapName); try { l.lock(); - return cache.put(key, exchange); + DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(exchange); + DefaultExchangeHolder oldHolder = cache.put(key, newHolder); + return unmarshallExchange(camelContext, oldHolder); } finally { - LOG.trace("Adding an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); + LOG.trace("Added an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); l.unlock(); } } @@ -214,8 +223,7 @@ public Set scan(CamelContext camelContext) { @Override public Exchange recover(CamelContext camelContext, String exchangeId) { LOG.trace("Recovering an Exchange with ID {}.", exchangeId); - return useRecovery ? persistedCache.get(exchangeId) : null; - + return useRecovery ? unmarshallExchange(camelContext, persistedCache.get(exchangeId)) : null; } @Override @@ -265,7 +273,7 @@ public int getMaximumRedeliveries() { @Override public Exchange get(CamelContext camelContext, String key) { - return cache.get(key); + return unmarshallExchange(camelContext, cache.get(key)); } /** @@ -278,9 +286,10 @@ public Exchange get(CamelContext camelContext, String key) { */ @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { + DefaultExchangeHolder holder = DefaultExchangeHolder.marshal(exchange); if (optimistic) { LOG.trace("Removing an exchange with ID {} for key {} in an optimistic manner.", exchange.getExchangeId(), key); - if (!cache.remove(key, exchange)) { + 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(); @@ -289,7 +298,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { if (useRecovery) { LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); - persistedCache.put(key, exchange); + persistedCache.put(key, holder); LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); } @@ -308,13 +317,13 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { tCtx.beginTransaction(); - TransactionalMap tCache = tCtx.getMap(cache.getName()); - TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); + TransactionalMap tCache = tCtx.getMap(cache.getName()); + TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); - tCache.remove(key); + DefaultExchangeHolder removedHolder = tCache.remove(key); LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", exchange.getExchangeId(), key); - tPersistentCache.put(key, exchange); + tPersistentCache.put(key, removedHolder); tCtx.commitTransaction(); LOG.trace("Removed an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); @@ -379,11 +388,18 @@ protected void doStop() throws Exception { if (useRecovery) { persistedCache.clear(); } - cache.clear(); - if (useLocalHzInstance) { hzInstance.getLifecycleService().shutdown(); } } + + private Exchange unmarshallExchange(CamelContext camelContext, DefaultExchangeHolder holder) { + Exchange exchange = null; + if (holder != null) { + exchange = new DefaultExchange(camelContext); + DefaultExchangeHolder.unmarshal(exchange, holder); + } + return exchange; + } } From 359d4549a74b135bffa7e460f67048544a67c9a7 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 01:09:57 +0400 Subject: [PATCH 06/53] HazelcastAggregationRepository Constructor Tests were implemented. --- ...AggregationRepositoryConstructorsTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java new file mode 100644 index 0000000000000..393828f6a9efb --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java @@ -0,0 +1,75 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.Exchange; +import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Before; +import org.junit.Test; + + +public class HazelcastAggregationRepositoryConstructorsTest extends CamelTestSupport { + private static final String HAZELCAST_INSTANCE_NAME = "HazelcastAggregationRepositoryTestInstance"; + private HazelcastInstance hzInstance; + + @Before + public void start() { + /*Config cfg = new XmlConfigBuilder().build(); + cfg.setProperty("hazelcast.version.check.enabled", "false"); + hzInstance = Hazelcast.newHazelcastInstance(cfg);*/ + } + + @Test(expected = UnsupportedOperationException.class) + public void nonOptimisticRepoFailsOnOptimisticAdd() throws Exception { + final String repoName = "hzRepoMap"; + HazelcastAggregationRepository repo = new HazelcastAggregationRepository(repoName); + repo.doStart(); + + try { + Exchange oldOne = new DefaultExchange(context()); + Exchange newOne = new DefaultExchange(context()); + final String key = "abrakadabra"; + repo.add(context(), key, oldOne, newOne); + fail("OptimisticLockingException should have been thrown"); + } finally { + repo.doStop(); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void optimisticRepoFailsForNonOptimisticAdd() throws Exception { + final String repoName = "hzRepoMap"; + HazelcastAggregationRepository repo = new HazelcastAggregationRepository(repoName, true); + repo.doStart(); + + try { + Exchange ex = new DefaultExchange(context()); + final String key = "abrakadabra"; + repo.add(context(), key, ex); + } finally { + repo.doStop(); + } + } + + @Test(expected = IllegalArgumentException.class) + public void uninitializedHazelcastInstanceThrows() throws Exception { + final String repoName = "hzRepoMap"; + HazelcastAggregationRepository repo = new HazelcastAggregationRepository(repoName, (HazelcastInstance) null); + repo.doStart(); + } + + @Test + public void locallyInitializedHazelcastInstanceAdd() throws Exception { + HazelcastAggregationRepository repo = new HazelcastAggregationRepository("hzRepoMap"); + try { + repo.doStart(); + Exchange ex = new DefaultExchange(context()); + repo.add(context(), "somedefaultkey", ex); + //} catch (Throwable e) { + //fail(e.getMessage()); + } + finally { + repo.doStop(); + } + } +} From be172ed2290bbe65264ff424686613c0af23736c Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 01:10:57 +0400 Subject: [PATCH 07/53] Removed unused imports and variables. --- ...HazelcastAggregationRepositoryConstructorsTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java index 393828f6a9efb..99eb0cd6ec766 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java @@ -4,20 +4,10 @@ import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.Before; import org.junit.Test; public class HazelcastAggregationRepositoryConstructorsTest extends CamelTestSupport { - private static final String HAZELCAST_INSTANCE_NAME = "HazelcastAggregationRepositoryTestInstance"; - private HazelcastInstance hzInstance; - - @Before - public void start() { - /*Config cfg = new XmlConfigBuilder().build(); - cfg.setProperty("hazelcast.version.check.enabled", "false"); - hzInstance = Hazelcast.newHazelcastInstance(cfg);*/ - } @Test(expected = UnsupportedOperationException.class) public void nonOptimisticRepoFailsOnOptimisticAdd() throws Exception { From 9d392307c6313b9987bb1dfd3c1e16553502b817 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 02:11:05 +0400 Subject: [PATCH 08/53] useRecovery always default to true. --- .../hazelcast/HazelcastAggregationRepository.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index 54dbb94953495..4c5909e256c7a 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -44,7 +44,7 @@ public final class HazelcastAggregationRepository extends ServiceSupport OptimisticLockingAggregationRepository { private boolean optimistic; private boolean useLocalHzInstance; - private boolean useRecovery; + private boolean useRecovery = true; private IMap cache; private IMap persistedCache; private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; @@ -67,7 +67,6 @@ public HazelcastAggregationRepository(final String repositoryName) { persistenceMapName = String.format("%s%s", mapName, COMPLETED_SUFFIX); optimistic = false; useLocalHzInstance = true; - useRecovery = true; } /** @@ -298,7 +297,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { if (useRecovery) { LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); - persistedCache.put(key, holder); + persistedCache.put(exchange.getExchangeId(), holder); LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); } @@ -323,7 +322,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { DefaultExchangeHolder removedHolder = tCache.remove(key); LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", exchange.getExchangeId(), key); - tPersistentCache.put(key, removedHolder); + tPersistentCache.put(exchange.getExchangeId(), removedHolder); tCtx.commitTransaction(); LOG.trace("Removed an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); From 09840431c4dfab9c2e2b6d6e96979618d83d8316 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 02:18:02 +0400 Subject: [PATCH 09/53] Initial commit (test created). --- ...stAggregationRepositoryOperationsTest.java | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java new file mode 100644 index 0000000000000..cbc2d6c2d5f9d --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java @@ -0,0 +1,171 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.Exchange; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import java.util.Set; + + +public class HazelcastAggregationRepositoryOperationsTest extends CamelTestSupport { + + private static HazelcastInstance hzOne = null; + private static HazelcastInstance hzTwo = null; + private static final String THREAD_SAFE_REPO = "threadSafeRepo"; + private static final String OPTIMISTIC_REPO = "optimisticRepo"; + + @BeforeClass + public static void setUpHazelcastCluster() { + hzOne = Hazelcast.newHazelcastInstance(); + hzTwo = Hazelcast.newHazelcastInstance(); + } + + @AfterClass + public static void shutDownHazelcastCluster() { + hzOne.getLifecycleService().shutdown(); + hzTwo.getLifecycleService().shutdown(); + } + + @Test + public void checkOptimisticAddOfNewExchange() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is an optimistic test body. Sincerely yours, Captain Obvious."; + final String key = "optimisticKey"; + Exchange newEx = createExchangeWithBody(testBody); + Exchange oldEx = repoOne.add(context(), key, null, newEx); + + assertNull("Old exchange should be null.", oldEx); + + final String theNewestBody = "This is the newest test body."; + Exchange theNewestEx = createExchangeWithBody(theNewestBody); + + oldEx = repoTwo.add(context(), key, newEx, theNewestEx); + assertEquals(newEx.getIn().getBody(), oldEx.getIn().getBody()); + + } finally { + repoOne.stop(); + repoTwo.stop(); + } + } + + @Test + public void checkThreadSafeAddOfNewExchange() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is a thread-safe test body. Sincerely yours, Captain Obvious."; + final String key = "threadSafeKey"; + Exchange newEx = createExchangeWithBody(testBody); + Exchange oldEx = repoOne.add(context(), key, newEx); + + assertNull("Old exchange should be null.", oldEx); + + final String theNewestBody = "This is the newest test body."; + Exchange theNewestEx = createExchangeWithBody(theNewestBody); + + oldEx = repoTwo.add(context(), key, theNewestEx); + assertEquals(newEx.getIn().getBody(), oldEx.getIn().getBody()); + + } finally { + repoOne.stop(); + repoTwo.stop(); + } + } + + @Test + public void checkOptimisticGet() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzTwo); + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is an optimistic test body. Sincerely yours, Captain Obvious."; + final String key = "optimisticKey"; + + Exchange ex = createExchangeWithBody(testBody); + repoOne.add(context(), key, null, ex); + + Exchange gotEx = repoTwo.get(context(), key); + assertEquals("ex and gotEx should be equal", gotEx.getIn().getBody(), ex.getIn().getBody()); + } finally { + repoOne.doStop(); + repoTwo.doStop(); + } + } + + @Test + public void checkThreadSafeGet() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + + final String testBody = "This is a thread-safe test body. Sincerely yours, Captain Obvious."; + final String key = "threadSafeKey"; + + Exchange ex = createExchangeWithBody(testBody); + repoOne.add(context(), key, ex); + + Exchange gotEx = repoTwo.get(context(), key); + assertEquals("ex and gotEx should be equal", gotEx.getIn().getBody(), ex.getIn().getBody()); + } finally { + repoOne.doStop(); + repoTwo.doStop(); + } + } + + @Test + public void checkOptimisticPersistentRemove() throws Exception { + final String persistentRepoName = String.format("%s-completed", OPTIMISTIC_REPO); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is an optimistic test body. Sincerely yours, Captain Obvious."; + final String key = "optimisticKey"; + + Exchange ex = createExchangeWithBody(testBody); + + repoOne.add(context(), key, null, ex); + + Exchange getBackEx = repoTwo.get(context(), key); + assertNotNull("getBackEx should not be null.", getBackEx); + + repoTwo.remove(context(), key, ex); + + getBackEx = repoOne.get(context(), key); + assertNull("getBackEx should be null here.", getBackEx); + + Set keys = repoTwo.scan(context()); + assertCollectionSize(keys, 1); + + getBackEx = repoOne.recover(context(), keys.iterator().next()); + assertNotNull("getBackEx got from persistent repo should not be null.", getBackEx); + + + } finally { + repoOne.doStop(); + repoTwo.doStop(); + } + } +} From 86a7bfe2c45df7943b1ec0745ad2c0251c2cc15e Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Fri, 13 Dec 2013 18:20:15 +0400 Subject: [PATCH 10/53] useRecovery always default to true. --- .../aggregate/hazelcast/HazelcastAggregationRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index 4c5909e256c7a..3b493cac24735 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -11,7 +11,6 @@ import com.hazelcast.transaction.TransactionOptions; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; -import org.apache.camel.builder.xml.DomResultHandlerFactory; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.DefaultExchangeHolder; import org.apache.camel.spi.OptimisticLockingAggregationRepository; From 242633bb1c093118dc55a5152cd02d6031325f73 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 02:30:56 +0400 Subject: [PATCH 11/53] Typo fix. --- .../HazelcastAggregationRepositoryConstructorsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java index 99eb0cd6ec766..52581291f7b6b 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java @@ -20,7 +20,7 @@ public void nonOptimisticRepoFailsOnOptimisticAdd() throws Exception { Exchange newOne = new DefaultExchange(context()); final String key = "abrakadabra"; repo.add(context(), key, oldOne, newOne); - fail("OptimisticLockingException should have been thrown"); + fail("OptimisticLockingException should has been thrown"); } finally { repo.doStop(); } From 586fe239b6d1cf832d8be38171254b5030f3cbf3 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 03:03:56 +0400 Subject: [PATCH 12/53] Superclass was extracted --- ...AggregationRepositoryCamelTestSupport.java | 45 +++++++++++++++++++ ...stAggregationRepositoryOperationsTest.java | 42 ++++++----------- 2 files changed, 58 insertions(+), 29 deletions(-) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java new file mode 100644 index 0000000000000..166c8a0b09f02 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java @@ -0,0 +1,45 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * @author Alexander Lomov + * Date: 04.01.14 + * Time: 3:00 + */ +public class HazelcastAggregationRepositoryCamelTestSupport extends CamelTestSupport { + private static HazelcastInstance hzOne = null; + private static HazelcastInstance hzTwo = null; + + protected static void doInitializeHazelcastInstances() { + hzOne = Hazelcast.newHazelcastInstance(); + hzTwo = Hazelcast.newHazelcastInstance(); + } + + protected static void doDestroyHazelcastInstances() { + hzOne.getLifecycleService().shutdown(); + hzTwo.getLifecycleService().shutdown(); + } + + protected static HazelcastInstance getFirstInstance() { + return hzOne; + } + + protected static HazelcastInstance getSecondInstance() { + return hzTwo; + } + + @BeforeClass + public static void setUpHazelcastCluster() { + doInitializeHazelcastInstances(); + } + + @AfterClass + public static void shutDownHazelcastCluster() { + doDestroyHazelcastInstances(); + } +} diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java index cbc2d6c2d5f9d..fea8176a2d6e9 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java @@ -1,38 +1,22 @@ package org.apache.camel.processor.aggregate.hazelcast; -import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; import org.apache.camel.Exchange; -import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; + import java.util.Set; -public class HazelcastAggregationRepositoryOperationsTest extends CamelTestSupport { +public class HazelcastAggregationRepositoryOperationsTest extends HazelcastAggregationRepositoryCamelTestSupport { + - private static HazelcastInstance hzOne = null; - private static HazelcastInstance hzTwo = null; private static final String THREAD_SAFE_REPO = "threadSafeRepo"; private static final String OPTIMISTIC_REPO = "optimisticRepo"; - @BeforeClass - public static void setUpHazelcastCluster() { - hzOne = Hazelcast.newHazelcastInstance(); - hzTwo = Hazelcast.newHazelcastInstance(); - } - - @AfterClass - public static void shutDownHazelcastCluster() { - hzOne.getLifecycleService().shutdown(); - hzTwo.getLifecycleService().shutdown(); - } @Test public void checkOptimisticAddOfNewExchange() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, getSecondInstance()); try { repoOne.doStart(); @@ -59,8 +43,8 @@ public void checkOptimisticAddOfNewExchange() throws Exception { @Test public void checkThreadSafeAddOfNewExchange() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, getSecondInstance()); try { repoOne.doStart(); @@ -87,8 +71,8 @@ public void checkThreadSafeAddOfNewExchange() throws Exception { @Test public void checkOptimisticGet() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, getSecondInstance()); try { repoOne.doStart(); repoTwo.doStart(); @@ -109,8 +93,8 @@ public void checkOptimisticGet() throws Exception { @Test public void checkThreadSafeGet() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, getSecondInstance()); try { repoOne.doStart(); @@ -134,8 +118,8 @@ public void checkThreadSafeGet() throws Exception { @Test public void checkOptimisticPersistentRemove() throws Exception { final String persistentRepoName = String.format("%s-completed", OPTIMISTIC_REPO); - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, getSecondInstance()); try { repoOne.doStart(); From f68fc41c0eafdba10ce1a8a1b554d4c53d876071 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 04:24:36 +0400 Subject: [PATCH 13/53] More working tests. --- ...elcastAggregationRepositoryRoutesTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java new file mode 100644 index 0000000000000..e22abe58faa82 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java @@ -0,0 +1,100 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Exchange; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.processor.aggregate.AggregationStrategy; +import org.junit.Test; + +/** + * @author Alexander Lomov + * Date: 04.01.14 + * Time: 2:40 + */ + +public class HazelcastAggregationRepositoryRoutesTest extends HazelcastAggregationRepositoryCamelTestSupport { + + private static final String REPO_NAME = "routeTestRepo"; + private static final String MOCK_GOTCHA = "mock:gotcha"; + private static final String DIRECT_ONE = "direct:one"; + private static final String DIRECT_TWO = "direct:two"; + + @EndpointInject(uri = MOCK_GOTCHA) + private MockEndpoint mock; + + @Produce(uri = DIRECT_ONE) + private ProducerTemplate produceOne; + + @Produce(uri = DIRECT_TWO) + private ProducerTemplate produceTwo; + + + @Test + public void checkAggregationFromTwoRoutes() throws Exception { + final HazelcastAggregationRepository repoOne = + new HazelcastAggregationRepository(REPO_NAME, false, getFirstInstance()); + + final HazelcastAggregationRepository repoTwo = + new HazelcastAggregationRepository(REPO_NAME, false, getSecondInstance()); + + final int completionSize = 4; + final String correlator = "CORRELATOR"; + RouteBuilder rbOne = new RouteBuilder() { + @Override + public void configure() throws Exception { + + from(DIRECT_ONE).routeId("AggregatingRouteOne") + .aggregate(header(correlator)) + .aggregationRepository(repoOne) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_GOTCHA); + } + }; + + RouteBuilder rbTwo = new RouteBuilder() { + @Override + public void configure() throws Exception { + + from(DIRECT_TWO).routeId("AggregatingRouteTwo") + .aggregate(header(correlator)) + .aggregationRepository(repoTwo) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_GOTCHA); + } + }; + + context().addRoutes(rbOne); + context().addRoutes(rbTwo); + context().start(); + + mock.expectedMessageCount(1); + mock.expectedBodiesReceived(1 + 2 + 3 + 4); + + produceOne.sendBodyAndHeader(1, correlator, correlator); + produceTwo.sendBodyAndHeader(2, correlator, correlator); + produceOne.sendBodyAndHeader(3, correlator, correlator); + produceOne.sendBodyAndHeader(4, correlator, correlator); + + mock.assertIsSatisfied(); + } + + private static class SumOfIntsAggregationStrategy implements AggregationStrategy { + @Override + public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { + if (oldExchange == null) { + return newExchange; + } else { + Integer n = newExchange.getIn().getBody(Integer.class); + Integer o = oldExchange.getIn().getBody(Integer.class); + Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); + oldExchange.getIn().setBody(v, Integer.class); + return oldExchange; + } + } + } +} From d13ee37491acb5764697c7346be723481a970c45 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 13:28:34 +0400 Subject: [PATCH 14/53] Moved SumOfIntsAggregationStrategy class from inner level. --- ...elcastAggregationRepositoryRoutesTest.java | 16 ------------- .../SumOfIntsAggregationStrategy.java | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java index e22abe58faa82..2b16a5d5c5e6d 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java @@ -1,12 +1,10 @@ package org.apache.camel.processor.aggregate.hazelcast; import org.apache.camel.EndpointInject; -import org.apache.camel.Exchange; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; -import org.apache.camel.processor.aggregate.AggregationStrategy; import org.junit.Test; /** @@ -83,18 +81,4 @@ public void configure() throws Exception { mock.assertIsSatisfied(); } - private static class SumOfIntsAggregationStrategy implements AggregationStrategy { - @Override - public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { - if (oldExchange == null) { - return newExchange; - } else { - Integer n = newExchange.getIn().getBody(Integer.class); - Integer o = oldExchange.getIn().getBody(Integer.class); - Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); - oldExchange.getIn().setBody(v, Integer.class); - return oldExchange; - } - } - } } diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java new file mode 100644 index 0000000000000..bf88b6322abe2 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java @@ -0,0 +1,24 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import org.apache.camel.Exchange; +import org.apache.camel.processor.aggregate.AggregationStrategy; + +/** +* @author Alexander Lomov +* Date: 04.01.14 +* Time: 13:25 +*/ +class SumOfIntsAggregationStrategy implements AggregationStrategy { + @Override + public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { + if (oldExchange == null) { + return newExchange; + } else { + Integer n = newExchange.getIn().getBody(Integer.class); + Integer o = oldExchange.getIn().getBody(Integer.class); + Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); + oldExchange.getIn().setBody(v, Integer.class); + return oldExchange; + } + } +} From 22d44ea081dfd25d82aa7bdf007a050bee69c333 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 14:03:25 +0400 Subject: [PATCH 15/53] Created a test case for recoverable HazelcastAggregationRepository. --- ...gationRepositoryRecoverableRoutesTest.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java new file mode 100644 index 0000000000000..cea590394ac40 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java @@ -0,0 +1,133 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.Test; + +/** + * @author Alexander Lomov + * Date: 04.01.14 + * Time: 4:37 + */ + +public class HazelcastAggregationRepositoryRecoverableRoutesTest extends HazelcastAggregationRepositoryCamelTestSupport { + + private static final String REPO_NAME = "routeTestRepo"; + private static final String MOCK_GOTCHA = "mock:gotcha"; + private static final String MOCK_FAILURE = "mock:failure"; + private static final String DIRECT_ONE = "direct:one"; + private static final String DIRECT_TWO = "direct:two"; + + @EndpointInject(uri = MOCK_GOTCHA) + private MockEndpoint mockGotcha; + + @EndpointInject(uri = MOCK_FAILURE) + private MockEndpoint mockFailure; + + @Produce(uri = DIRECT_ONE) + private ProducerTemplate produceOne; + + @Produce(uri = DIRECT_TWO) + private ProducerTemplate produceTwo; + + @Test + public void checkAggregationFromTwoRoutesWithRecovery() throws Exception { + final HazelcastAggregationRepository repoOne = + new HazelcastAggregationRepository(REPO_NAME, false, getFirstInstance()); + + final HazelcastAggregationRepository repoTwo = + new HazelcastAggregationRepository(REPO_NAME, false, getSecondInstance()); + + final int completionSize = 4; + final String correlator = "CORRELATOR"; + + RouteBuilder rbOne = new RouteBuilder() { + @Override + public void configure() throws Exception { + + onException(EverythingIsLostException.class) + .handled(true) + .useOriginalMessage() + .to(MOCK_GOTCHA) + .end(); + + interceptSendToEndpoint(MOCK_FAILURE) + .throwException(new EverythingIsLostException("The field is lost... everything is lost")) + .end(); + + from(DIRECT_ONE) + .aggregate(header(correlator)) + .aggregationRepository(repoOne) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_FAILURE); + + } + }; + + + RouteBuilder rbTwo = new RouteBuilder() { + @Override + public void configure() throws Exception { + + onException(EverythingIsLostException.class) + .handled(true) + .useOriginalMessage() + .to(MOCK_GOTCHA) + .end(); + + interceptSendToEndpoint(MOCK_FAILURE) + .throwException(new EverythingIsLostException("The field is lost... everything is lost")) + .end(); + + from(DIRECT_TWO) + .aggregate(header(correlator)) + .aggregationRepository(repoTwo) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_FAILURE); + } + }; + + context().addRoutes(rbOne); + context().addRoutes(rbTwo); + context().start(); + + mockFailure.expectedMessageCount(0); + mockGotcha.expectedMessageCount(1); + mockGotcha.expectedBodiesReceived(1 + 2 + 3 + 4); + + produceOne.sendBodyAndHeader(4, correlator, correlator); + produceTwo.sendBodyAndHeader(2, correlator, correlator); + produceOne.sendBodyAndHeader(3, correlator, correlator); + produceTwo.sendBodyAndHeader(1, correlator, correlator); + + mockFailure.assertIsSatisfied(); + mockFailure.assertIsSatisfied(); + } + + @SuppressWarnings("unused") + private static class EverythingIsLostException extends Exception { + private EverythingIsLostException() { + } + + private EverythingIsLostException(String message) { + super(message); + } + + private EverythingIsLostException(String message, Throwable cause) { + super(message, cause); + } + + private EverythingIsLostException(Throwable cause) { + super(cause); + } + + private EverythingIsLostException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } +} From 0f3539682b54900fdcfb815fbc717659d3a6d2a2 Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Sat, 29 Mar 2014 14:33:04 -0400 Subject: [PATCH 16/53] CAMEL-6458: Add renameUsingCopy option to File Component The new renameUsingCopy option controls whether rename operations are performed using a copy and delete strategy. This is primarily used in environments where the regular rename operation is unreliable (e.g. across different file systems and networks). Signed-off-by: Gregor Zurowski --- .../camel/component/file/FileEndpoint.java | 10 ++++++ .../camel/component/file/FileOperations.java | 9 ++++- .../java/org/apache/camel/util/FileUtil.java | 36 +++++++++++++++---- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java b/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java index 21f7fdb8f9bb1..4e0a920fae50b 100644 --- a/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java +++ b/camel-core/src/main/java/org/apache/camel/component/file/FileEndpoint.java @@ -41,6 +41,8 @@ public class FileEndpoint extends GenericFileEndpoint { @UriParam private boolean copyAndDeleteOnRenameFail = true; @UriParam + private boolean renameUsingCopy; + @UriParam private boolean forceWrites = true; public FileEndpoint() { @@ -174,6 +176,14 @@ public void setCopyAndDeleteOnRenameFail(boolean copyAndDeleteOnRenameFail) { this.copyAndDeleteOnRenameFail = copyAndDeleteOnRenameFail; } + public boolean isRenameUsingCopy() { + return renameUsingCopy; + } + + public void setRenameUsingCopy(boolean renameUsingCopy) { + this.renameUsingCopy = renameUsingCopy; + } + public boolean isForceWrites() { return forceWrites; } diff --git a/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java b/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java index a1d34ae07ae39..2d9f72d06b195 100644 --- a/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java +++ b/camel-core/src/main/java/org/apache/camel/component/file/FileOperations.java @@ -64,13 +64,20 @@ public boolean deleteFile(String name) throws GenericFileOperationFailedExceptio } public boolean renameFile(String from, String to) throws GenericFileOperationFailedException { + boolean renamed = false; File file = new File(from); File target = new File(to); try { - return FileUtil.renameFile(file, target, endpoint.isCopyAndDeleteOnRenameFail()); + if (endpoint.isRenameUsingCopy()) { + renamed = FileUtil.renameFileUsingCopy(file, target); + } else { + renamed = FileUtil.renameFile(file, target, endpoint.isCopyAndDeleteOnRenameFail()); + } } catch (IOException e) { throw new GenericFileOperationFailedException("Error renaming file from " + from + " to " + to, e); } + + return renamed; } public boolean existsFile(String name) throws GenericFileOperationFailedException { diff --git a/camel-core/src/main/java/org/apache/camel/util/FileUtil.java b/camel-core/src/main/java/org/apache/camel/util/FileUtil.java index d51330f68932d..6376c4405b5a8 100644 --- a/camel-core/src/main/java/org/apache/camel/util/FileUtil.java +++ b/camel-core/src/main/java/org/apache/camel/util/FileUtil.java @@ -425,12 +425,7 @@ public static boolean renameFile(File from, File to, boolean copyAndDeleteOnRena if (!renamed && copyAndDeleteOnRenameFail) { // now do a copy and delete as all rename attempts failed LOG.debug("Cannot rename file from: {} to: {}, will now use a copy/delete approach instead", from, to); - copyFile(from, to); - if (!deleteFile(from)) { - throw new IOException("Renaming file from: " + from + " to: " + to + " failed due cannot delete from file: " + from + " after copy succeeded"); - } else { - renamed = true; - } + renameFileUsingCopy(from, to); } if (LOG.isDebugEnabled() && count > 0) { @@ -439,6 +434,35 @@ public static boolean renameFile(File from, File to, boolean copyAndDeleteOnRena return renamed; } + /** + * Rename file using copy and delete strategy. This is primarily used in + * environments where the regular rename operation is unreliable. + * + * @param from the file to be renamed + * @param to the new target file + * @return true if the file was renamed successfully, otherwise false + * @throws IOException If an I/O error occurs during copy or delete operations. + */ + public static boolean renameFileUsingCopy(File from, File to) throws IOException { + // do not try to rename non existing files + if (!from.exists()) { + return false; + } + + boolean renamed = false; + + LOG.debug("Rename file '{}' to '{}' using copy/delete strategy.", from, to); + + copyFile(from, to); + if (!deleteFile(from)) { + throw new IOException("Renaming file from '" + from + "' to '" + to + "' failed: Cannot delete file '" + from + "' after copy succeeded"); + } else { + renamed = true; + } + + return renamed; + } + public static void copyFile(File from, File to) throws IOException { FileChannel in = new FileInputStream(from).getChannel(); FileChannel out = new FileOutputStream(to).getChannel(); From daed640eb32c40a291482fb2a1b5015aeffb07f6 Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Sat, 29 Mar 2014 14:48:10 -0400 Subject: [PATCH 17/53] CAMEL-6458: Add tests for renameUsingCopy option Signed-off-by: Gregor Zurowski --- .../file/FileProducerRenameUsingCopyTest.java | 56 +++++++++++++++++++ .../org/apache/camel/util/FileUtilTest.java | 11 ++++ 2 files changed, 67 insertions(+) create mode 100644 camel-core/src/test/java/org/apache/camel/component/file/FileProducerRenameUsingCopyTest.java diff --git a/camel-core/src/test/java/org/apache/camel/component/file/FileProducerRenameUsingCopyTest.java b/camel-core/src/test/java/org/apache/camel/component/file/FileProducerRenameUsingCopyTest.java new file mode 100644 index 0000000000000..c5dbfb65d5ae7 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/component/file/FileProducerRenameUsingCopyTest.java @@ -0,0 +1,56 @@ +/** + * 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.file; + +import java.io.File; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; + +public class FileProducerRenameUsingCopyTest extends ContextTestSupport { + + @Override + protected void setUp() throws Exception { + deleteDirectory("target/file"); + super.setUp(); + } + + public void testMove() throws Exception { + final String body = "Hello Camel"; + template.sendBodyAndHeader("file://target/file", body, Exchange.FILE_NAME, "hello.txt"); + + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(1); + mock.expectedFileExists("target/file/done/hello.txt", body); + assertMockEndpointsSatisfied(); + + assertTrue("File not copied", new File("target/file/done/hello.txt").exists()); + assertFalse("File not deleted", new File("target/file/hello.txt").exists()); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("file://target/file?renameUsingCopy=true&move=done").convertBodyTo(String.class).to("mock:result"); + } + }; + } +} \ No newline at end of file diff --git a/camel-core/src/test/java/org/apache/camel/util/FileUtilTest.java b/camel-core/src/test/java/org/apache/camel/util/FileUtilTest.java index 1ef3b1e160595..89446e98dbb75 100644 --- a/camel-core/src/test/java/org/apache/camel/util/FileUtilTest.java +++ b/camel-core/src/test/java/org/apache/camel/util/FileUtilTest.java @@ -221,4 +221,15 @@ public void testShutdown() throws Exception { assertFalse(tmpDir.exists()); } + public void testRenameUsingDelete() throws Exception { + File file = new File("target/foo.txt"); + if (!file.exists()) { + FileUtil.createNewFile(file); + } + + File target = new File("target/bar.txt"); + FileUtil.renameFileUsingCopy(file, target); + assertTrue("File not copied", target.exists()); + assertFalse("File not deleted", file.exists()); + } } From b73edad4df10722dfdece1edebe5d3bdff8a89d0 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Thu, 3 Apr 2014 17:26:25 +0800 Subject: [PATCH 18/53] CAMEL-7033 take clustering into consideration when stop the consumer with thanks Arne --- .../component/quartz2/QuartzEndpoint.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java index 94a207767ee34..1aeb9d0cc11e9 100644 --- a/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java +++ b/components/camel-quartz2/src/main/java/org/apache/camel/component/quartz2/QuartzEndpoint.java @@ -66,8 +66,8 @@ public class QuartzEndpoint extends DefaultEndpoint { private long triggerStartDelay = 500; // in millis second // An internal variables to track whether a job has been in scheduler or not, and has it paused or not. - private AtomicBoolean jobAdded = new AtomicBoolean(false); - private AtomicBoolean jobPaused = new AtomicBoolean(false); + private final AtomicBoolean jobAdded = new AtomicBoolean(false); + private final AtomicBoolean jobPaused = new AtomicBoolean(false); public QuartzEndpoint(String uri, QuartzComponent quartzComponent) { super(uri, quartzComponent); @@ -210,13 +210,7 @@ private void removeJobInScheduler() throws Exception { jobAdded.set(false); } } else if (pauseJob) { - boolean isClustered = scheduler.getMetaData().isJobStoreClustered(); - if (!scheduler.isShutdown() && !isClustered) { - LOG.info("Pausing job {}", triggerKey); - scheduler.pauseTrigger(triggerKey); - - jobAdded.set(false); - } + pauseTrigger(); } // Decrement camel job count for this endpoint @@ -374,13 +368,15 @@ public QuartzComponent getComponent() { } public void pauseTrigger() throws Exception { - if (jobPaused.get()) { + Scheduler scheduler = getComponent().getScheduler(); + boolean isClustered = scheduler.getMetaData().isJobStoreClustered(); + + if (jobPaused.get() || isClustered) { return; } + jobPaused.set(true); - - Scheduler scheduler = getComponent().getScheduler(); - if (scheduler != null && !scheduler.isShutdown()) { + if (!scheduler.isShutdown()) { LOG.info("Pausing trigger {}", triggerKey); scheduler.pauseTrigger(triggerKey); } From 5f6477c6f14b90aac58b9938e597e71cf9889b79 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Fri, 4 Apr 2014 11:32:11 +0800 Subject: [PATCH 19/53] CAMEL-7341 Fixed the issue that cxfrs:InInterceptor defined in Spring is ignored --- .../component/cxf/jaxrs/CxfRsEndpoint.java | 17 +++++++--- .../cxf/jaxrs/CxfRsSpringEndpointTest.java | 3 ++ .../cxf/jaxrs/TestInInterceptor.java | 33 +++++++++++++++++++ .../jaxrs/CxfRsSpringEndpointBeans-2.6.xml | 9 ++++- 4 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/TestInInterceptor.java diff --git a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsEndpoint.java b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsEndpoint.java index f5360b8fd3602..8c3ff1b166b2e 100644 --- a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsEndpoint.java +++ b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/jaxrs/CxfRsEndpoint.java @@ -222,10 +222,19 @@ protected void setupCommonFactoryProperties(AbstractJAXRSFactoryBean factory) { factory.getFeatures().addAll(getFeatures()); } - factory.setInInterceptors(interceptorHolder.getInInterceptors()); - factory.setOutInterceptors(interceptorHolder.getOutInterceptors()); - factory.setOutFaultInterceptors(interceptorHolder.getOutFaultInterceptors()); - factory.setInFaultInterceptors(interceptorHolder.getInFaultInterceptors()); + // we need to avoid flushing the setting from spring or blueprint + if (!interceptorHolder.getInInterceptors().isEmpty()) { + factory.setInInterceptors(interceptorHolder.getInInterceptors()); + } + if (!interceptorHolder.getOutInterceptors().isEmpty()) { + factory.setOutInterceptors(interceptorHolder.getOutInterceptors()); + } + if (!interceptorHolder.getOutFaultInterceptors().isEmpty()) { + factory.setOutFaultInterceptors(interceptorHolder.getOutFaultInterceptors()); + } + if (!interceptorHolder.getInFaultInterceptors().isEmpty()) { + factory.setInFaultInterceptors(interceptorHolder.getInFaultInterceptors()); + } if (getProperties() != null) { if (factory.getProperties() != null) { diff --git a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java index a84fc20fc80ec..b5a435487c42d 100644 --- a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java +++ b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointTest.java @@ -41,6 +41,7 @@ public void testCreateCxfRsServerFactoryBean() { assertEquals("Get a wrong resource class", sfb.getResourceClasses().get(0), CustomerService.class); assertEquals("Got the wrong loggingFeatureEnabled", true, sfb.isLoggingFeatureEnabled()); assertEquals("Got the wrong loggingSizeLimit", 200, sfb.getLoggingSizeLimit()); + assertEquals("Got a wrong size of interceptors", 1, sfb.getInInterceptors().size()); Map endpointProps = sfb.getProperties(); // The beanId key is put by the AbstractCxfBeanDefinitionParser, so the size is 2 @@ -57,6 +58,8 @@ public void testCreateCxfRsClientFactoryBean() { assertTrue("Get a wrong resource class instance", cfb.create() instanceof CustomerService); assertEquals("Got the wrong loggingFeatureEnabled", false, cfb.isLoggingFeatureEnabled()); assertEquals("Got the wrong loggingSizeLimit", 0, cfb.getLoggingSizeLimit()); + assertEquals("Got a wrong size of interceptors", 1, cfb.getInInterceptors().size()); + } @Override diff --git a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/TestInInterceptor.java b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/TestInInterceptor.java new file mode 100644 index 0000000000000..c96d0a567e46b --- /dev/null +++ b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/jaxrs/TestInInterceptor.java @@ -0,0 +1,33 @@ +/** + * 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.cxf.jaxrs; + +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; + +public class TestInInterceptor extends AbstractPhaseInterceptor { + public TestInInterceptor() { + super(Phase.RECEIVE); + } + + @Override + public void handleMessage(Message message) throws Fault { + // do thing here + } +} diff --git a/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointBeans-2.6.xml b/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointBeans-2.6.xml index 1290c715dd355..09bf7bda5f946 100644 --- a/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointBeans-2.6.xml +++ b/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/jaxrs/CxfRsSpringEndpointBeans-2.6.xml @@ -33,10 +33,17 @@ + + + + serviceClass="org.apache.camel.component.cxf.jaxrs.testbean.CustomerService"> + + + + From fc91c5883e4387b3e6d393de7f0a7fc311faeaa2 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Fri, 4 Apr 2014 08:38:19 +0200 Subject: [PATCH 20/53] CAMEL-6694: Fixed CS --- .../camel/component/log/LogCustomLoggerTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/camel-core/src/test/java/org/apache/camel/component/log/LogCustomLoggerTest.java b/camel-core/src/test/java/org/apache/camel/component/log/LogCustomLoggerTest.java index 7114f53781dae..908b8c8defbac 100644 --- a/camel-core/src/test/java/org/apache/camel/component/log/LogCustomLoggerTest.java +++ b/camel-core/src/test/java/org/apache/camel/component/log/LogCustomLoggerTest.java @@ -16,21 +16,22 @@ */ package org.apache.camel.component.log; +import java.io.StringWriter; + import org.apache.camel.CamelContext; import org.apache.camel.ContextTestSupport; import org.apache.camel.ResolveEndpointFailedException; import org.apache.camel.impl.DefaultCamelContext; -import org.apache.camel.impl.JndiRegistry; import org.apache.camel.impl.PropertyPlaceholderDelegateRegistry; import org.apache.camel.impl.SimpleRegistry; -import org.apache.log4j.*; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; import org.apache.log4j.spi.LoggingEvent; import org.junit.Before; import org.junit.Test; import org.slf4j.LoggerFactory; -import java.io.StringWriter; - import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; @@ -44,7 +45,7 @@ public class LogCustomLoggerTest extends ContextTestSupport { // to capture the warnings from LogComponent private static StringWriter sw2; - private static class CapturingAppender extends AppenderSkeleton { + private static final class CapturingAppender extends AppenderSkeleton { private StringWriter sw; private CapturingAppender(StringWriter sw) { From dba21eb2c27d936f0d903981d8dba0343335b300 Mon Sep 17 00:00:00 2001 From: Grzegorz Grzybek Date: Fri, 4 Apr 2014 13:11:32 +0200 Subject: [PATCH 21/53] [CAMEL-6694] Allow log EIP to use provided or registry logger --- .../org/apache/camel/model/LogDefinition.java | 59 ++++++++- .../camel/model/ProcessorDefinition.java | 37 ++++++ .../LogProcessorWithProvidedLoggerTest.java | 120 ++++++++++++++++++ .../src/test/resources/log4j.properties | 7 + ...ingLogProcessorWithProvidedLoggerTest.java | 31 +++++ .../logProcessorWithProvidedLoggerTest.xml | 45 +++++++ 6 files changed, 294 insertions(+), 5 deletions(-) create mode 100644 camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java create mode 100644 components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringLogProcessorWithProvidedLoggerTest.java create mode 100644 components/camel-spring/src/test/resources/org/apache/camel/spring/processor/logProcessorWithProvidedLoggerTest.xml diff --git a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java index 8bc3a2caaa5e0..085d0ce2ad7ed 100644 --- a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java @@ -20,14 +20,20 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import org.apache.camel.Expression; import org.apache.camel.LoggingLevel; import org.apache.camel.Processor; import org.apache.camel.processor.LogProcessor; import org.apache.camel.spi.RouteContext; +import org.apache.camel.util.CamelContextHelper; import org.apache.camel.util.CamelLogger; import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; /** * Represents an XML <log/> element @@ -45,6 +51,10 @@ public class LogDefinition extends NoOutputDefinition { private String logName; @XmlAttribute private String marker; + @XmlAttribute + private String loggerRef; + @XmlTransient + private Logger logger; public LogDefinition() { } @@ -75,15 +85,38 @@ public Processor createProcessor(RouteContext routeContext) throws Exception { // use simple language for the message string to give it more power Expression exp = routeContext.getCamelContext().resolveLanguage("simple").createExpression(message); - String name = getLogName(); - if (name == null) { - name = routeContext.getRoute().getId(); + // get logger explicitely set in the definition + Logger logger = this.getLogger(); + + // get logger which may be set in XML definition + if (logger == null && ObjectHelper.isNotEmpty(loggerRef)) { + logger = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), loggerRef, Logger.class); + } + + if (logger == null) { + // first - try to lookup single instance in the registry, just like LogComponent + Map availableLoggers = routeContext.lookupByType(Logger.class); + if (availableLoggers.size() == 1) { + logger = availableLoggers.values().iterator().next(); + } else if (availableLoggers.size() > 1) { + // we should log about this somewhere... + //LOG.info("More than one {} instance found in the registry. Falling back to creating logger by name.", Logger.class.getName()); + } + } + + if (logger == null) { + String name = getLogName(); + if (name == null) { + name = routeContext.getRoute().getId(); + } + logger = LoggerFactory.getLogger(name); } + // should be INFO by default LoggingLevel level = getLoggingLevel() != null ? getLoggingLevel() : LoggingLevel.INFO; - CamelLogger logger = new CamelLogger(name, level, getMarker()); + CamelLogger camelLogger = new CamelLogger(logger, level, getMarker()); - return new LogProcessor(exp, logger); + return new LogProcessor(exp, camelLogger); } @Override @@ -123,4 +156,20 @@ public String getMarker() { public void setMarker(String marker) { this.marker = marker; } + + public String getLoggerRef() { + return loggerRef; + } + + public void setLoggerRef(String loggerRef) { + this.loggerRef = loggerRef; + } + + public Logger getLogger() { + return logger; + } + + public void setLogger(Logger logger) { + this.logger = logger; + } } \ No newline at end of file diff --git a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java index 7e817ea0ba0cd..833457c04f335 100644 --- a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java @@ -1539,6 +1539,23 @@ public Type log(LoggingLevel loggingLevel, String logName, String message) { return (Type) this; } + /** + * Creates a log message to be logged at the given level using provided logger. + * + * @param loggingLevel the logging level to use + * @param logger the logger to use + * @param message the log message, (you can use {@link org.apache.camel.language.simple.SimpleLanguage} syntax) + * @return the builder + */ + @SuppressWarnings("unchecked") + public Type log(LoggingLevel loggingLevel, Logger logger, String message) { + LogDefinition answer = new LogDefinition(message); + answer.setLoggingLevel(loggingLevel); + answer.setLogger(logger); + addOutput(answer); + return (Type) this; + } + /** * Creates a log message to be logged at the given level and name. * @@ -1559,6 +1576,26 @@ public Type log(LoggingLevel loggingLevel, String logName, String marker, String return (Type) this; } + /** + * Creates a log message to be logged at the given level using provided logger. + * + * + * @param loggingLevel the logging level to use + * @param logger the logger to use + * @param marker log marker name + * @param message the log message, (you can use {@link org.apache.camel.language.simple.SimpleLanguage} syntax) + * @return the builder + */ + @SuppressWarnings("unchecked") + public Type log(LoggingLevel loggingLevel, Logger logger, String marker, String message) { + LogDefinition answer = new LogDefinition(message); + answer.setLoggingLevel(loggingLevel); + answer.setLogger(logger); + answer.setMarker(marker); + addOutput(answer); + return (Type) this; + } + /** * Content Based Router EIP: * Creates a choice of one or more predicates with an otherwise clause diff --git a/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java b/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java new file mode 100644 index 0000000000000..4aa000ea664a5 --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java @@ -0,0 +1,120 @@ +/** + * 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.ContextTestSupport; +import org.apache.camel.LoggingLevel; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.log.LogComponent; +import org.apache.camel.impl.DefaultCamelContext; +import org.apache.camel.impl.SimpleRegistry; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggingEvent; +import org.junit.Before; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.StringWriter; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +/** + * @version + */ +public class LogProcessorWithProvidedLoggerTest extends ContextTestSupport { + + // to capture the logs + private static StringWriter sw; + // to capture the warnings from LogComponent + private static StringWriter sw2; + + private static final class CapturingAppender extends AppenderSkeleton { + private StringWriter sw; + + private CapturingAppender(StringWriter sw) { + this.sw = sw; + } + + @Override + protected void append(LoggingEvent event) { + this.sw.append(event.getLoggerName() + " " + event.getLevel().toString() + " " + event.getMessage()); + } + + @Override + public void close() { + } + + @Override + public boolean requiresLayout() { + return false; + } + } + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + sw = new StringWriter(); + Logger.getLogger("org.apache.camel.customlogger").removeAllAppenders(); + Logger.getLogger("org.apache.camel.customlogger").addAppender(new CapturingAppender(sw)); + Logger.getLogger("org.apache.camel.customlogger").setLevel(Level.TRACE); + } + + public void testLogProcessorWithRegistryLogger() throws Exception { + getMockEndpoint("mock:foo").expectedMessageCount(1); + + template.sendBody("direct:foo", "Bye World"); + + assertMockEndpointsSatisfied(); + + assertThat(sw.toString(), equalTo("org.apache.camel.customlogger INFO Got Bye World")); + } + + public void testLogProcessorWithProvidedLogger() throws Exception { + getMockEndpoint("mock:bar").expectedMessageCount(1); + + template.sendBody("direct:bar", "Bye World"); + + assertMockEndpointsSatisfied(); + + assertThat(sw.toString(), equalTo("org.apache.camel.customlogger INFO Also got Bye World")); + } + + @Override + protected CamelContext createCamelContext() throws Exception { + SimpleRegistry registry = new SimpleRegistry(); + registry.put("mylogger1", LoggerFactory.getLogger("org.apache.camel.customlogger")); + CamelContext context = new DefaultCamelContext(registry); + return context; + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:foo").routeId("foo").log(LoggingLevel.INFO, "Got ${body}").to("mock:foo"); + from("direct:bar").routeId("bar").log(LoggingLevel.INFO, LoggerFactory.getLogger("org.apache.camel.customlogger"), "Also got ${body}").to("mock:bar"); + } + }; + } + +} diff --git a/camel-core/src/test/resources/log4j.properties b/camel-core/src/test/resources/log4j.properties index 55b083405f475..c3eb9f77dcbe4 100644 --- a/camel-core/src/test/resources/log4j.properties +++ b/camel-core/src/test/resources/log4j.properties @@ -19,6 +19,7 @@ # The logging properties used during tests.. # log4j.rootLogger=INFO, file +log4j.logger.org.apache.camel.customlogger=TRACE, file2 #log4j.logger.org.apache.camel.impl.converter=WARN #log4j.logger.org.apache.camel.management=WARN @@ -70,3 +71,9 @@ log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m # MDC #log4j.appender.file.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %-10.10X{camel.breadcrumbId} - %-10.10X{camel.exchangeId} - %-10.10X{camel.correlationId} - %-10.10X{camel.routeId} - %m%n +# File appender for logging with provided logger +log4j.appender.file2=org.apache.log4j.FileAppender +log4j.appender.file2.layout=org.apache.log4j.PatternLayout +log4j.appender.file2.file=target/custom-logger-test.log +log4j.appender.file2.append=false +log4j.appender.file2.layout.ConversionPattern=%-5p %c{1} %m%n diff --git a/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringLogProcessorWithProvidedLoggerTest.java b/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringLogProcessorWithProvidedLoggerTest.java new file mode 100644 index 0000000000000..6b4ac3381c15f --- /dev/null +++ b/components/camel-spring/src/test/java/org/apache/camel/spring/processor/SpringLogProcessorWithProvidedLoggerTest.java @@ -0,0 +1,31 @@ +/** + * 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.spring.processor; + +import org.apache.camel.CamelContext; +import org.apache.camel.processor.LogProcessorTest; +import org.apache.camel.processor.LogProcessorWithProvidedLoggerTest; + +import static org.apache.camel.spring.processor.SpringTestHelper.createSpringCamelContext; + +public class SpringLogProcessorWithProvidedLoggerTest extends LogProcessorWithProvidedLoggerTest { + + protected CamelContext createCamelContext() throws Exception { + return createSpringCamelContext(this, + "org/apache/camel/spring/processor/logProcessorWithProvidedLoggerTest.xml"); + } +} \ No newline at end of file diff --git a/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/logProcessorWithProvidedLoggerTest.xml b/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/logProcessorWithProvidedLoggerTest.xml new file mode 100644 index 0000000000000..603965071543e --- /dev/null +++ b/components/camel-spring/src/test/resources/org/apache/camel/spring/processor/logProcessorWithProvidedLoggerTest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From 2ab36a3db9520001d05c87cd779433165d0c6168 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Fri, 4 Apr 2014 21:36:25 +0800 Subject: [PATCH 22/53] CAMEL-6694 Fixed CS error and Polish the code --- .../org/apache/camel/component/log/LogComponent.java | 2 -- .../java/org/apache/camel/model/LogDefinition.java | 10 +++++++--- .../processor/LogProcessorWithProvidedLoggerTest.java | 6 ++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java b/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java index 5eebc253f7629..c24189c9f4e6d 100644 --- a/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java +++ b/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java @@ -22,13 +22,11 @@ import org.apache.camel.Endpoint; import org.apache.camel.LoggingLevel; import org.apache.camel.Processor; -import org.apache.camel.ResolveEndpointFailedException; import org.apache.camel.impl.UriEndpointComponent; import org.apache.camel.processor.CamelLogProcessor; import org.apache.camel.processor.DefaultExchangeFormatter; import org.apache.camel.processor.ThroughputLogger; import org.apache.camel.spi.ExchangeFormatter; -import org.apache.camel.util.CamelContextHelper; import org.apache.camel.util.CamelLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java index 085d0ce2ad7ed..a49c5bb7e8905 100644 --- a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java @@ -16,6 +16,8 @@ */ package org.apache.camel.model; +import java.util.Map; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; @@ -26,6 +28,7 @@ import org.apache.camel.LoggingLevel; import org.apache.camel.Processor; import org.apache.camel.processor.LogProcessor; +import org.apache.camel.processor.aggregate.AggregateProcessor; import org.apache.camel.spi.RouteContext; import org.apache.camel.util.CamelContextHelper; import org.apache.camel.util.CamelLogger; @@ -33,8 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Map; - /** * Represents an XML <log/> element * @@ -43,6 +44,8 @@ @XmlRootElement(name = "log") @XmlAccessorType(XmlAccessType.FIELD) public class LogDefinition extends NoOutputDefinition { + @XmlTransient + private static final Logger LOG = LoggerFactory.getLogger(LogDefinition.class); @XmlAttribute(required = true) private String message; @XmlAttribute @@ -100,7 +103,7 @@ public Processor createProcessor(RouteContext routeContext) throws Exception { logger = availableLoggers.values().iterator().next(); } else if (availableLoggers.size() > 1) { // we should log about this somewhere... - //LOG.info("More than one {} instance found in the registry. Falling back to creating logger by name.", Logger.class.getName()); + LOG.info("More than one {} instance found in the registry. Falling back to create logger by name.", Logger.class.getName()); } } @@ -108,6 +111,7 @@ public Processor createProcessor(RouteContext routeContext) throws Exception { String name = getLogName(); if (name == null) { name = routeContext.getRoute().getId(); + LOG.info("The LogName is null. Falling back to create logger by using the route id {}.", name); } logger = LoggerFactory.getLogger(name); } diff --git a/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java b/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java index 4aa000ea664a5..ae93b545c6cb3 100644 --- a/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java +++ b/camel-core/src/test/java/org/apache/camel/processor/LogProcessorWithProvidedLoggerTest.java @@ -16,11 +16,12 @@ */ package org.apache.camel.processor; +import java.io.StringWriter; + import org.apache.camel.CamelContext; import org.apache.camel.ContextTestSupport; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.component.log.LogComponent; import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.impl.SimpleRegistry; import org.apache.log4j.AppenderSkeleton; @@ -30,9 +31,6 @@ import org.junit.Before; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.StringWriter; - import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; From 8f24ca30794b11becf61e54a2b0908f22c886609 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Fri, 4 Apr 2014 21:45:30 +0800 Subject: [PATCH 23/53] Fixed some Eclipse warning of aggregater --- .../aggregate/AbstractListAggregationStrategy.java | 6 ++++-- .../aggregate/GroupedExchangeAggregationStrategy.java | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/camel-core/src/main/java/org/apache/camel/processor/aggregate/AbstractListAggregationStrategy.java b/camel-core/src/main/java/org/apache/camel/processor/aggregate/AbstractListAggregationStrategy.java index ae382dc08073d..ad2ec8ed6af4d 100644 --- a/camel-core/src/main/java/org/apache/camel/processor/aggregate/AbstractListAggregationStrategy.java +++ b/camel-core/src/main/java/org/apache/camel/processor/aggregate/AbstractListAggregationStrategy.java @@ -100,7 +100,7 @@ public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { private List getList(Exchange exchange) { List list = exchange.getProperty(Exchange.GROUPED_EXCHANGE, List.class); if (list == null) { - list = new GroupedExchangeList(); + list = new GroupedExchangeList(); exchange.setProperty(Exchange.GROUPED_EXCHANGE, list); } return list; @@ -109,7 +109,9 @@ private List getList(Exchange exchange) { /** * A list to contains grouped {@link Exchange}s. */ - private static final class GroupedExchangeList extends ArrayList { + private static final class GroupedExchangeList extends ArrayList { + + private static final long serialVersionUID = 1L; @Override public String toString() { diff --git a/camel-core/src/main/java/org/apache/camel/processor/aggregate/GroupedExchangeAggregationStrategy.java b/camel-core/src/main/java/org/apache/camel/processor/aggregate/GroupedExchangeAggregationStrategy.java index 587746c567bc9..84b375dd78813 100644 --- a/camel-core/src/main/java/org/apache/camel/processor/aggregate/GroupedExchangeAggregationStrategy.java +++ b/camel-core/src/main/java/org/apache/camel/processor/aggregate/GroupedExchangeAggregationStrategy.java @@ -29,12 +29,12 @@ public class GroupedExchangeAggregationStrategy extends AbstractListAggregationStrategy { @Override - @SuppressWarnings("unchecked") + public void onCompletion(Exchange exchange) { if (isStoreAsBodyOnCompletion()) { // lets be backwards compatible // TODO: Remove this method in Camel 3.0 - List list = (List) exchange.getProperty(Exchange.GROUPED_EXCHANGE); + List list = (List) exchange.getProperty(Exchange.GROUPED_EXCHANGE); if (list != null) { exchange.getIn().setBody(list); } From 44f881e0c1d04546a0e0df734dee3c50b98646e5 Mon Sep 17 00:00:00 2001 From: Willem Jiang Date: Fri, 4 Apr 2014 21:47:25 +0800 Subject: [PATCH 24/53] CAMEL-6694 Fixed some other warning in eclipse --- .../src/main/java/org/apache/camel/model/LogDefinition.java | 1 - .../java/org/apache/camel/model/RouteContextRefDefinition.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java index a49c5bb7e8905..883c7a3370bec 100644 --- a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java @@ -28,7 +28,6 @@ import org.apache.camel.LoggingLevel; import org.apache.camel.Processor; import org.apache.camel.processor.LogProcessor; -import org.apache.camel.processor.aggregate.AggregateProcessor; import org.apache.camel.spi.RouteContext; import org.apache.camel.util.CamelContextHelper; import org.apache.camel.util.CamelLogger; diff --git a/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java b/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java index 87667a3527026..f1093dc9bc2d3 100644 --- a/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/RouteContextRefDefinition.java @@ -50,8 +50,7 @@ public String getRef() { public void setRef(String ref) { this.ref = ref; } - - @SuppressWarnings({"unchecked", "rawtypes"}) + public List lookupRoutes(CamelContext camelContext) { return RouteContextRefDefinitionHelper.lookupRoutes(camelContext, ref); } From 134c433157956311d944bf48f33d3e42d858f3fa Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Fri, 4 Apr 2014 17:23:47 +0200 Subject: [PATCH 25/53] Upgraded rx --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 69511eb60754b..ce405024ca63f 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -351,7 +351,7 @@ 1.7R2 1.0_3 1.0 - 0.17.2 + 0.17.4 1.3.2_2 9.5.1-4_1 9.5.1-4 From 4eca6cac3568a2bac7dec67d52094054ffccf61d Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Sat, 5 Apr 2014 07:54:00 +0200 Subject: [PATCH 26/53] Upgraded rx --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index ce405024ca63f..97ad7a1ee5516 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -351,7 +351,7 @@ 1.7R2 1.0_3 1.0 - 0.17.4 + 0.17.5 1.3.2_2 9.5.1-4_1 9.5.1-4 From db4a55797a838a6f3f963f3e0f143dbae357f218 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Sat, 5 Apr 2014 09:34:01 +0200 Subject: [PATCH 27/53] Upgrade to AMQ 5.9.1 --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 97ad7a1ee5516..68a48ab1449d1 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -36,7 +36,7 @@ 1.1.3 - 5.9.0 + 5.9.1 1.8.3 1.7.0_6 3.4_1 From 56d1139fce92952174103794941d7508afd47ece Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Sat, 5 Apr 2014 09:36:24 +0200 Subject: [PATCH 28/53] Upgraded AHC --- parent/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parent/pom.xml b/parent/pom.xml index 68a48ab1449d1..f308ea76744ee 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -37,7 +37,7 @@ 1.1.3 5.9.1 - 1.8.3 + 1.8.5 1.7.0_6 3.4_1 3.4_2 From 1fce4a3aea55fb5deeaf4c59cba837664bcd368c Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Sat, 5 Apr 2014 09:36:32 +0200 Subject: [PATCH 29/53] Polished --- .../apache/camel/component/ahc/ws/WsProducerConsumerTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/camel-ahc-ws/src/test/java/org/apache/camel/component/ahc/ws/WsProducerConsumerTest.java b/components/camel-ahc-ws/src/test/java/org/apache/camel/component/ahc/ws/WsProducerConsumerTest.java index efefa1f940e0c..cc60a15a527a5 100644 --- a/components/camel-ahc-ws/src/test/java/org/apache/camel/component/ahc/ws/WsProducerConsumerTest.java +++ b/components/camel-ahc-ws/src/test/java/org/apache/camel/component/ahc/ws/WsProducerConsumerTest.java @@ -44,9 +44,7 @@ public void startTestServer() throws Exception { context.addServlet(servletHolder, "/*"); server.start(); - System.out.println("started"); - assertTrue(server.isStarted()); - + assertTrue(server.isStarted()); } public void stopTestServer() throws Exception { From 1f2b83aff8f866708267a6113da35c3e57dd0563 Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Sat, 5 Apr 2014 09:56:58 +0100 Subject: [PATCH 30/53] CAMEL-5539 Circuit Breaker EIP Implemented circuit breaker as load balancer policy --- .../camel/model/LoadBalanceDefinition.java | 22 ++- .../CircuitBreakerLoadBalancerDefinition.java | 108 +++++++++++++ .../CircuitBreakerLoadBalancer.java | 145 ++++++++++++++++++ .../CircuitBreakerLoadBalancerTest.java | 109 +++++++++++++ 4 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 camel-core/src/main/java/org/apache/camel/model/loadbalancer/CircuitBreakerLoadBalancerDefinition.java create mode 100644 camel-core/src/main/java/org/apache/camel/processor/loadbalancer/CircuitBreakerLoadBalancer.java create mode 100644 camel-core/src/test/java/org/apache/camel/processor/CircuitBreakerLoadBalancerTest.java diff --git a/camel-core/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java b/camel-core/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java index 973e9e54bc21d..985db35054c19 100644 --- a/camel-core/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/LoadBalanceDefinition.java @@ -31,6 +31,7 @@ import org.apache.camel.Expression; import org.apache.camel.Processor; +import org.apache.camel.model.loadbalancer.CircuitBreakerLoadBalancerDefinition; import org.apache.camel.model.loadbalancer.CustomLoadBalancerDefinition; import org.apache.camel.model.loadbalancer.FailoverLoadBalancerDefinition; import org.apache.camel.model.loadbalancer.RandomLoadBalancerDefinition; @@ -38,6 +39,7 @@ import org.apache.camel.model.loadbalancer.StickyLoadBalancerDefinition; import org.apache.camel.model.loadbalancer.TopicLoadBalancerDefinition; import org.apache.camel.model.loadbalancer.WeightedLoadBalancerDefinition; +import org.apache.camel.processor.loadbalancer.CircuitBreakerLoadBalancer; import org.apache.camel.processor.loadbalancer.FailOverLoadBalancer; import org.apache.camel.processor.loadbalancer.LoadBalancer; import org.apache.camel.processor.loadbalancer.RandomLoadBalancer; @@ -66,7 +68,8 @@ public class LoadBalanceDefinition extends ProcessorDefinition... exceptions) { + CircuitBreakerLoadBalancer breakerLoadBalancer = new CircuitBreakerLoadBalancer(Arrays.asList(exceptions)); + breakerLoadBalancer.setThreshold(threshold); + breakerLoadBalancer.setHalfOpenAfter(halfOpenAfter); + + setLoadBalancerType(new LoadBalancerDefinition(breakerLoadBalancer)); + return this; + } /** * Uses weighted load balancer diff --git a/camel-core/src/main/java/org/apache/camel/model/loadbalancer/CircuitBreakerLoadBalancerDefinition.java b/camel-core/src/main/java/org/apache/camel/model/loadbalancer/CircuitBreakerLoadBalancerDefinition.java new file mode 100644 index 0000000000000..94c990bb90c4c --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/model/loadbalancer/CircuitBreakerLoadBalancerDefinition.java @@ -0,0 +1,108 @@ +/** + * 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.model.loadbalancer; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import org.apache.camel.model.LoadBalancerDefinition; +import org.apache.camel.processor.loadbalancer.CircuitBreakerLoadBalancer; +import org.apache.camel.processor.loadbalancer.LoadBalancer; +import org.apache.camel.spi.RouteContext; +import org.apache.camel.util.ObjectHelper; + +/** + * Represents an XML <circuitbreaker/> element + */ +@XmlRootElement(name = "circuitbreaker") +@XmlAccessorType(XmlAccessType.FIELD) +public class CircuitBreakerLoadBalancerDefinition extends LoadBalancerDefinition { + @XmlElement(name = "exception") + private List exceptions = new ArrayList(); + @XmlAttribute + private Long halfOpenAfter; + @XmlAttribute + private Integer threshold; + + public CircuitBreakerLoadBalancerDefinition() { + } + + @Override + protected LoadBalancer createLoadBalancer(RouteContext routeContext) { + CircuitBreakerLoadBalancer answer; + + if (!exceptions.isEmpty()) { + List> classes = new ArrayList>(); + for (String name : exceptions) { + Class type = routeContext.getCamelContext().getClassResolver().resolveClass(name); + if (type == null) { + throw new IllegalArgumentException("Cannot find class: " + name + " in the classpath"); + } + if (!ObjectHelper.isAssignableFrom(Throwable.class, type)) { + throw new IllegalArgumentException("Class is not an instance of Throwable: " + type); + } + classes.add(type); + } + answer = new CircuitBreakerLoadBalancer(classes); + } else { + answer = new CircuitBreakerLoadBalancer(); + } + + if (getHalfOpenAfter() != null) { + answer.setHalfOpenAfter(getHalfOpenAfter()); + } + if (getThreshold() != null) { + answer.setThreshold(getThreshold()); + } + return answer; + } + + public Long getHalfOpenAfter() { + return halfOpenAfter; + } + + public void setHalfOpenAfter(Long halfOpenAfter) { + this.halfOpenAfter = halfOpenAfter; + } + + public Integer getThreshold() { + return threshold; + } + + public void setThreshold(Integer threshold) { + this.threshold = threshold; + } + + public List getExceptions() { + return exceptions; + } + + public void setExceptions(List exceptions) { + this.exceptions = exceptions; + } + + + @Override + public String toString() { + return "CircuitBreakerLoadBalancer"; + } +} diff --git a/camel-core/src/main/java/org/apache/camel/processor/loadbalancer/CircuitBreakerLoadBalancer.java b/camel-core/src/main/java/org/apache/camel/processor/loadbalancer/CircuitBreakerLoadBalancer.java new file mode 100644 index 0000000000000..b8e23b4b9ecc0 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/processor/loadbalancer/CircuitBreakerLoadBalancer.java @@ -0,0 +1,145 @@ +/** + * 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.loadbalancer; + +import java.util.List; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.camel.AsyncCallback; +import org.apache.camel.AsyncProcessor; +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.Traceable; +import org.apache.camel.util.AsyncProcessorConverterHelper; + +public class CircuitBreakerLoadBalancer extends LoadBalancerSupport implements Traceable, CamelContextAware { + private final List> exceptions; + private CamelContext camelContext; + private int threshold; + private long halfOpenAfter; + private long lastFailure; + private AtomicInteger failures = new AtomicInteger(); + + public CircuitBreakerLoadBalancer(List> exceptions) { + this.exceptions = exceptions; + } + public CircuitBreakerLoadBalancer() { + this.exceptions = null; + } + + public void setHalfOpenAfter(long halfOpenAfter) { + this.halfOpenAfter = halfOpenAfter; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + @Override + public CamelContext getCamelContext() { + return camelContext; + } + + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + + public List> getExceptions() { + return exceptions; + } + + protected boolean hasFailed(Exchange exchange) { + boolean answer = false; + + if (exchange.getException() != null) { + if (exceptions == null || exceptions.isEmpty()) { + answer = true; + } else { + for (Class exception : exceptions) { + if (exchange.getException(exception) != null) { + answer = true; + break; + } + } + } + } + return answer; + } + + @Override + public boolean isRunAllowed() { + boolean forceShutdown = camelContext.getShutdownStrategy().forceShutdown(this); + if (forceShutdown) { + log.trace("Run not allowed as ShutdownStrategy is forcing shutting down"); + } + return !forceShutdown && super.isRunAllowed(); + } + + public boolean process(final Exchange exchange, final AsyncCallback callback) { + + // can we still run + if (!isRunAllowed()) { + log.trace("Run not allowed, will reject executing exchange: {}", exchange); + if (exchange.getException() == null) { + exchange.setException(new RejectedExecutionException("Run is not allowed")); + } + callback.done(true); + return true; + } + + if (failures.get() >= threshold && System.currentTimeMillis() - lastFailure < halfOpenAfter) { + exchange.setException(new RejectedExecutionException("CircuitBreaker Open: failures: " + failures + ", lastFailure: " + lastFailure)); + } + Processor processor = getProcessors().get(0); + if (processor == null) { + throw new IllegalStateException("No processors could be chosen to process CircuitBreaker"); + } + + AsyncProcessor albp = AsyncProcessorConverterHelper.convert(processor); + boolean sync = albp.process(exchange, callback); + + boolean failed = hasFailed(exchange); + + if (!failed) { + failures.set(0); + } else { + failures.incrementAndGet(); + lastFailure = System.currentTimeMillis(); + } + + if (!sync) { + log.trace("Processing exchangeId: {} is continued being processed asynchronously", exchange.getExchangeId()); + return false; + } + + log.trace("Processing exchangeId: {} is continued being processed synchronously", exchange.getExchangeId()); + callback.done(true); + return true; + } + + public String toString() { + return "CircuitBreakerLoadBalancer[" + getProcessors() + "]"; + } + + public String getTraceLabel() { + return "circuitbreaker"; + } +} diff --git a/camel-core/src/test/java/org/apache/camel/processor/CircuitBreakerLoadBalancerTest.java b/camel-core/src/test/java/org/apache/camel/processor/CircuitBreakerLoadBalancerTest.java new file mode 100644 index 0000000000000..124a1e1e4544f --- /dev/null +++ b/camel-core/src/test/java/org/apache/camel/processor/CircuitBreakerLoadBalancerTest.java @@ -0,0 +1,109 @@ +/** + * 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 java.util.concurrent.RejectedExecutionException; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import static org.apache.camel.component.mock.MockEndpoint.expectsMessageCount; + +public class CircuitBreakerLoadBalancerTest extends ContextTestSupport { + + private static class MyExceptionProcessor extends RuntimeException { + } + + private MockEndpoint result; + + @Override + protected void setUp() throws Exception { + super.setUp(); + result = getMockEndpoint("mock:result"); + } + + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("direct:start").loadBalance() + .circuitBreaker(2, 1000L, MyExceptionProcessor.class) + .to("mock:result"); + } + }; + } + + public void testClosedCircuitPassesMessages() throws Exception { + expectsMessageCount(3, result); + sendMessage("direct:start", "message one"); + sendMessage("direct:start", "message two"); + sendMessage("direct:start", "message three"); + assertMockEndpointsSatisfied(); + } + + public void testFailedMessagesOpenCircuitToPreventMessageThree() throws Exception { + expectsMessageCount(2, result); + + result.whenAnyExchangeReceived(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.setException(new MyExceptionProcessor()); + } + }); + + Exchange exchangeOne = sendMessage("direct:start", "message one"); + Exchange exchangeTwo = sendMessage("direct:start", "message two"); + Exchange exchangeThree = sendMessage("direct:start", "message three"); + assertMockEndpointsSatisfied(); + + assertTrue(exchangeOne.getException() instanceof MyExceptionProcessor); + assertTrue(exchangeTwo.getException() instanceof MyExceptionProcessor); + assertTrue(exchangeThree.getException() instanceof RejectedExecutionException); + } + + public void testHalfOpenCircuitClosesAfterTimeout() throws Exception { + expectsMessageCount(2, result); + result.whenAnyExchangeReceived(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.setException(new MyExceptionProcessor()); + } + }); + + sendMessage("direct:start", "message one"); + sendMessage("direct:start", "message two"); + sendMessage("direct:start", "message three"); + assertMockEndpointsSatisfied(); + + result.reset(); + expectsMessageCount(1, result); + + Thread.sleep(1000); + sendMessage("direct:start", "message four"); + assertMockEndpointsSatisfied(); + } + + protected Exchange sendMessage(final String endpoint, final Object body) throws Exception { + return template.send(endpoint, new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + exchange.getIn().setBody(body); + } + }); + } +} From 9ed7d85f66284ba8b9e39eb47c35d257900ad13c Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Sat, 5 Apr 2014 15:36:05 +0100 Subject: [PATCH 31/53] CAMEL-5539 Circuit Breaker EIP Added circuitbreaker to jaxb and scala --- .../resources/org/apache/camel/model/loadbalancer/jaxb.index | 1 + .../org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala | 3 +++ 2 files changed, 4 insertions(+) diff --git a/camel-core/src/main/resources/org/apache/camel/model/loadbalancer/jaxb.index b/camel-core/src/main/resources/org/apache/camel/model/loadbalancer/jaxb.index index 23a7651e83c6f..0f31bc77783ad 100644 --- a/camel-core/src/main/resources/org/apache/camel/model/loadbalancer/jaxb.index +++ b/camel-core/src/main/resources/org/apache/camel/model/loadbalancer/jaxb.index @@ -14,6 +14,7 @@ ## See the License for the specific language governing permissions and ## limitations under the License. ## ------------------------------------------------------------------------ +CircuitBreakerLoadBalancerDefinition CustomLoadBalancerDefinition FailoverLoadBalancerDefinition RandomLoadBalancerDefinition diff --git a/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala b/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala index 339b7fcc5b131..70b9ab8434c7d 100644 --- a/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala +++ b/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala @@ -25,6 +25,9 @@ import org.apache.camel.Exchange */ case class SLoadBalanceDefinition(override val target: LoadBalanceDefinition)(implicit val builder: RouteBuilder) extends SAbstractDefinition[LoadBalanceDefinition] { + def circuitbreaker(threshold: Int, halfOpenAfter: Long, exceptions: Class[_]*) + = wrap(target.circuitBreaker(threshold, halfOpenAfter, exceptions)) + def failover(classes: Class[_]*) = wrap(target.failover(classes: _*)) def failover = wrap(target.failover) From 82ef800fd47f1bb02f91f907f6040e8b722e2a78 Mon Sep 17 00:00:00 2001 From: Bilgin Ibryam Date: Sun, 6 Apr 2014 07:46:38 +0100 Subject: [PATCH 32/53] CAMEL-5539 Circuit Breaker EIP Fixed broken build --- .../org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala b/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala index 70b9ab8434c7d..c2b31c39ad5b1 100644 --- a/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala +++ b/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala @@ -25,8 +25,8 @@ import org.apache.camel.Exchange */ case class SLoadBalanceDefinition(override val target: LoadBalanceDefinition)(implicit val builder: RouteBuilder) extends SAbstractDefinition[LoadBalanceDefinition] { - def circuitbreaker(threshold: Int, halfOpenAfter: Long, exceptions: Class[_]*) - = wrap(target.circuitBreaker(threshold, halfOpenAfter, exceptions)) + def circuitbreaker(threshold: Int, halfOpenAfter: Long, classes: Class[_]*) + = wrap(target.circuitBreaker(threshold, halfOpenAfter, classesb: _*)) def failover(classes: Class[_]*) = wrap(target.failover(classes: _*)) From 29dee17af39cf335f9c067e8d0280424ffb848b5 Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Sun, 6 Apr 2014 17:26:39 -0400 Subject: [PATCH 33/53] CAMEL-7346: Remove redundant groupId definition Signed-off-by: Gregor Zurowski --- components/camel-apns/pom.xml | 1 - components/camel-avro/pom.xml | 1 - components/camel-aws/pom.xml | 1 - components/camel-krati/pom.xml | 1 - components/camel-mongodb/pom.xml | 1 - components/camel-protobuf/pom.xml | 1 - components/camel-rabbitmq/pom.xml | 1 - components/camel-restlet/pom.xml | 1 - components/camel-rss/pom.xml | 1 - components/camel-sap-netweaver/pom.xml | 1 - components/camel-servlet/pom.xml | 1 - components/camel-servletlistener/pom.xml | 1 - components/camel-solr/pom.xml | 1 - components/camel-spring-integration/pom.xml | 1 - components/camel-spring-javaconfig/pom.xml | 1 - components/camel-spring-redis/pom.xml | 1 - components/camel-stream/pom.xml | 1 - components/camel-weather/pom.xml | 1 - parent/pom.xml | 1 - platforms/karaf/pom.xml | 1 - 20 files changed, 20 deletions(-) diff --git a/components/camel-apns/pom.xml b/components/camel-apns/pom.xml index 4b09f1ecce152..95feba0648cb0 100644 --- a/components/camel-apns/pom.xml +++ b/components/camel-apns/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-apns bundle diff --git a/components/camel-avro/pom.xml b/components/camel-avro/pom.xml index cc0b5969fa6d1..fd2d996e35f2a 100644 --- a/components/camel-avro/pom.xml +++ b/components/camel-avro/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-avro bundle Camel :: Avro diff --git a/components/camel-aws/pom.xml b/components/camel-aws/pom.xml index c6a25435dfebb..004b229f837f3 100644 --- a/components/camel-aws/pom.xml +++ b/components/camel-aws/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-aws bundle diff --git a/components/camel-krati/pom.xml b/components/camel-krati/pom.xml index 4291d3b95e7d2..2ed1b111872ed 100644 --- a/components/camel-krati/pom.xml +++ b/components/camel-krati/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-krati bundle Camel :: Krati diff --git a/components/camel-mongodb/pom.xml b/components/camel-mongodb/pom.xml index 89b29f7d040bb..61195f8853402 100644 --- a/components/camel-mongodb/pom.xml +++ b/components/camel-mongodb/pom.xml @@ -26,7 +26,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-mongodb bundle Camel :: MongoDB diff --git a/components/camel-protobuf/pom.xml b/components/camel-protobuf/pom.xml index 8f106503180c3..47d1940efa5ae 100755 --- a/components/camel-protobuf/pom.xml +++ b/components/camel-protobuf/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-protobuf bundle Camel :: Protobuf diff --git a/components/camel-rabbitmq/pom.xml b/components/camel-rabbitmq/pom.xml index 74eacc8ac2585..613025e9e547e 100644 --- a/components/camel-rabbitmq/pom.xml +++ b/components/camel-rabbitmq/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-rabbitmq bundle Camel :: RabbitMQ diff --git a/components/camel-restlet/pom.xml b/components/camel-restlet/pom.xml index 1ccde83483706..70582a6667628 100644 --- a/components/camel-restlet/pom.xml +++ b/components/camel-restlet/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-restlet bundle Camel :: Restlet diff --git a/components/camel-rss/pom.xml b/components/camel-rss/pom.xml index 637d6dac462e1..870b33605044e 100644 --- a/components/camel-rss/pom.xml +++ b/components/camel-rss/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-rss bundle Camel :: RSS diff --git a/components/camel-sap-netweaver/pom.xml b/components/camel-sap-netweaver/pom.xml index ae3dbf1fa4ae9..52350020568cd 100644 --- a/components/camel-sap-netweaver/pom.xml +++ b/components/camel-sap-netweaver/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-sap-netweaver bundle Camel :: SAP NetWeaver diff --git a/components/camel-servlet/pom.xml b/components/camel-servlet/pom.xml index 8fa58a7476eba..80f2d7b809f3a 100644 --- a/components/camel-servlet/pom.xml +++ b/components/camel-servlet/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-servlet bundle Camel :: Servlet diff --git a/components/camel-servletlistener/pom.xml b/components/camel-servletlistener/pom.xml index 858b0af6b392a..cc994ef5a65b8 100644 --- a/components/camel-servletlistener/pom.xml +++ b/components/camel-servletlistener/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-servletlistener bundle Camel :: Servlet Listener diff --git a/components/camel-solr/pom.xml b/components/camel-solr/pom.xml index 02aa439f7d0da..a9e1cf3118d05 100644 --- a/components/camel-solr/pom.xml +++ b/components/camel-solr/pom.xml @@ -23,7 +23,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-solr bundle Camel :: Solr diff --git a/components/camel-spring-integration/pom.xml b/components/camel-spring-integration/pom.xml index 344db40bec1a9..488cd9104f28a 100644 --- a/components/camel-spring-integration/pom.xml +++ b/components/camel-spring-integration/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-spring-integration Camel :: Spring Integration Camel Spring Integration support diff --git a/components/camel-spring-javaconfig/pom.xml b/components/camel-spring-javaconfig/pom.xml index 650f5c9cbba1c..2c3246faa33f8 100644 --- a/components/camel-spring-javaconfig/pom.xml +++ b/components/camel-spring-javaconfig/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-spring-javaconfig Camel :: Spring JavaConfig Camel Spring JavaConfig support diff --git a/components/camel-spring-redis/pom.xml b/components/camel-spring-redis/pom.xml index a9bacbfc611e9..7129da72b9101 100755 --- a/components/camel-spring-redis/pom.xml +++ b/components/camel-spring-redis/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-spring-redis bundle Camel :: Redis diff --git a/components/camel-stream/pom.xml b/components/camel-stream/pom.xml index d8cce4ba2d16c..476a700f9d804 100644 --- a/components/camel-stream/pom.xml +++ b/components/camel-stream/pom.xml @@ -25,7 +25,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-stream bundle Camel :: Stream diff --git a/components/camel-weather/pom.xml b/components/camel-weather/pom.xml index ce8ad19a3120d..ba8addf76a152 100644 --- a/components/camel-weather/pom.xml +++ b/components/camel-weather/pom.xml @@ -25,7 +25,6 @@ 2.14-SNAPSHOT - org.apache.camel camel-weather bundle Camel :: Weather diff --git a/parent/pom.xml b/parent/pom.xml index f308ea76744ee..1f633806e9cb4 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -24,7 +24,6 @@ 4.0.0 - org.apache.camel camel-parent pom Camel :: Parent diff --git a/platforms/karaf/pom.xml b/platforms/karaf/pom.xml index fe2438725a2d3..c6fcbb053afd3 100644 --- a/platforms/karaf/pom.xml +++ b/platforms/karaf/pom.xml @@ -24,7 +24,6 @@ 2.14-SNAPSHOT - org.apache.camel karaf pom Camel :: Platforms :: Apache Karaf From bdf3021d0b9c19110088c93f9ad7b41d273930c0 Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Sun, 6 Apr 2014 18:39:51 -0400 Subject: [PATCH 34/53] CAMEL-7346: Remove redundant version specification Remove redundant version specification from dependencies that are already defined with the exact same version by their parents. Signed-off-by: Gregor Zurowski --- camel-core/pom.xml | 1 - components/camel-cxf-transport/pom.xml | 2 -- components/camel-cxf/pom.xml | 1 - components/camel-disruptor/pom.xml | 1 - components/camel-dozer/pom.xml | 2 -- components/camel-hbase/pom.xml | 2 -- components/camel-hdfs/pom.xml | 1 - components/camel-http/pom.xml | 1 - components/camel-ical/pom.xml | 2 -- components/camel-jetty/pom.xml | 3 --- components/camel-jpa/pom.xml | 1 - components/camel-netty/pom.xml | 1 - .../camel-salesforce/camel-salesforce-maven-plugin/pom.xml | 4 ---- components/camel-servlet/pom.xml | 1 - components/camel-solr/pom.xml | 3 --- components/camel-spring-javaconfig/pom.xml | 1 - components/camel-spring-ws/pom.xml | 1 - components/camel-stomp/pom.xml | 4 ---- components/camel-test-blueprint/pom.xml | 2 -- components/camel-web/pom.xml | 2 -- components/camel-websocket/pom.xml | 3 --- examples/camel-example-activemq-tomcat/pom.xml | 1 - examples/camel-example-cxf-tomcat/pom.xml | 1 - examples/camel-example-cxf/pom.xml | 1 - examples/camel-example-gauth/pom.xml | 1 - examples/camel-example-osgi/pom.xml | 1 - examples/camel-example-ssh-security/pom.xml | 3 --- examples/camel-example-ssh/pom.xml | 3 --- platforms/karaf/features/pom.xml | 3 --- tests/camel-itest-karaf/pom.xml | 1 - tests/camel-itest-osgi/pom.xml | 2 -- tooling/apt/pom.xml | 2 -- 32 files changed, 58 deletions(-) diff --git a/camel-core/pom.xml b/camel-core/pom.xml index 45968a298795a..f968cc6b68fb9 100644 --- a/camel-core/pom.xml +++ b/camel-core/pom.xml @@ -137,7 +137,6 @@ junit junit - ${junit-version} test diff --git a/components/camel-cxf-transport/pom.xml b/components/camel-cxf-transport/pom.xml index ad613430e99b5..8885d957fe912 100644 --- a/components/camel-cxf-transport/pom.xml +++ b/components/camel-cxf-transport/pom.xml @@ -94,7 +94,6 @@ org.apache.aries.blueprint org.apache.aries.blueprint.core - ${aries-blueprint-core-version} provided @@ -172,7 +171,6 @@ org.apache.cxf cxf-codegen-plugin - ${cxf-version} generate-test-sources diff --git a/components/camel-cxf/pom.xml b/components/camel-cxf/pom.xml index f5e6871facee1..5f73f95f4a099 100644 --- a/components/camel-cxf/pom.xml +++ b/components/camel-cxf/pom.xml @@ -145,7 +145,6 @@ org.apache.aries.blueprint org.apache.aries.blueprint - ${aries-blueprint-core-version} provided diff --git a/components/camel-disruptor/pom.xml b/components/camel-disruptor/pom.xml index f5346d9a45225..870bdb67744a5 100644 --- a/components/camel-disruptor/pom.xml +++ b/components/camel-disruptor/pom.xml @@ -46,7 +46,6 @@ com.lmax disruptor - ${disruptor-version} diff --git a/components/camel-dozer/pom.xml b/components/camel-dozer/pom.xml index 588e1f2e4233f..ddb1d1e0760c5 100644 --- a/components/camel-dozer/pom.xml +++ b/components/camel-dozer/pom.xml @@ -41,9 +41,7 @@ net.sf.dozer dozer - ${dozer-version} - org.apache.camel camel-test-spring diff --git a/components/camel-hbase/pom.xml b/components/camel-hbase/pom.xml index 68c59cef527b2..c23dec5ac830f 100644 --- a/components/camel-hbase/pom.xml +++ b/components/camel-hbase/pom.xml @@ -94,7 +94,6 @@ commons-collections commons-collections - ${commons-collections-version} commons-configuration @@ -146,7 +145,6 @@ org.apache.zookeeper zookeeper - ${zookeeper-version} test diff --git a/components/camel-hdfs/pom.xml b/components/camel-hdfs/pom.xml index c33b772413185..2894784a7d1d5 100644 --- a/components/camel-hdfs/pom.xml +++ b/components/camel-hdfs/pom.xml @@ -86,7 +86,6 @@ commons-collections commons-collections - ${commons-collections-version} commons-configuration diff --git a/components/camel-http/pom.xml b/components/camel-http/pom.xml index e94b041fb2983..13aaf16d97337 100644 --- a/components/camel-http/pom.xml +++ b/components/camel-http/pom.xml @@ -54,7 +54,6 @@ commons-httpclient commons-httpclient - ${httpclient-version} commons-codec diff --git a/components/camel-ical/pom.xml b/components/camel-ical/pom.xml index 454ab08026878..3d451df4585dc 100644 --- a/components/camel-ical/pom.xml +++ b/components/camel-ical/pom.xml @@ -46,7 +46,6 @@ org.apache.camel camel-core - ${project.version} org.mnode.ical4j @@ -56,7 +55,6 @@ org.apache.camel camel-test - ${project.version} test diff --git a/components/camel-jetty/pom.xml b/components/camel-jetty/pom.xml index d8ab3364e27ec..be65b6754aab9 100644 --- a/components/camel-jetty/pom.xml +++ b/components/camel-jetty/pom.xml @@ -68,7 +68,6 @@ org.eclipse.jetty jetty-servlet - ${jetty-version} org.eclipse.jetty @@ -77,7 +76,6 @@ org.eclipse.jetty jetty-servlets - ${jetty-version} org.eclipse.jetty @@ -86,7 +84,6 @@ org.eclipse.jetty jetty-jmx - ${jetty-version} diff --git a/components/camel-jpa/pom.xml b/components/camel-jpa/pom.xml index c578cc2a97da2..0ae0a008b80c2 100644 --- a/components/camel-jpa/pom.xml +++ b/components/camel-jpa/pom.xml @@ -105,7 +105,6 @@ org.hibernate hibernate-entitymanager - ${hibernate-version} test diff --git a/components/camel-netty/pom.xml b/components/camel-netty/pom.xml index 7b2830b1a8ad1..914bd0e54b715 100644 --- a/components/camel-netty/pom.xml +++ b/components/camel-netty/pom.xml @@ -48,7 +48,6 @@ commons-pool commons-pool - ${commons-pool-version} diff --git a/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml b/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml index 05819847b0a02..320ebf19ff00b 100644 --- a/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml +++ b/components/camel-salesforce/camel-salesforce-maven-plugin/pom.xml @@ -51,7 +51,6 @@ org.apache.camel camel-salesforce - ${project.version} org.apache.velocity @@ -63,12 +62,10 @@ org.slf4j slf4j-api - ${slf4j-api-version} log4j log4j - ${log4j-version} @@ -87,7 +84,6 @@ junit junit - ${junit-version} test diff --git a/components/camel-servlet/pom.xml b/components/camel-servlet/pom.xml index 80f2d7b809f3a..4cb0a956e3493 100644 --- a/components/camel-servlet/pom.xml +++ b/components/camel-servlet/pom.xml @@ -56,7 +56,6 @@ org.osgi org.osgi.compendium - ${osgi-version} provided diff --git a/components/camel-solr/pom.xml b/components/camel-solr/pom.xml index a9e1cf3118d05..fe4b4c4ef6f26 100644 --- a/components/camel-solr/pom.xml +++ b/components/camel-solr/pom.xml @@ -89,19 +89,16 @@ org.eclipse.jetty jetty-server - ${jetty-version} test org.eclipse.jetty jetty-servlet - ${jetty-version} test xml-apis xml-apis - ${xml-apis-version} test diff --git a/components/camel-spring-javaconfig/pom.xml b/components/camel-spring-javaconfig/pom.xml index 2c3246faa33f8..038d97f5ad93b 100644 --- a/components/camel-spring-javaconfig/pom.xml +++ b/components/camel-spring-javaconfig/pom.xml @@ -51,7 +51,6 @@ org.springframework spring-test - ${spring-version} org.apache.servicemix.bundles diff --git a/components/camel-spring-ws/pom.xml b/components/camel-spring-ws/pom.xml index ab306de5089a2..9e01e79112d33 100644 --- a/components/camel-spring-ws/pom.xml +++ b/components/camel-spring-ws/pom.xml @@ -72,7 +72,6 @@ org.apache.geronimo.specs geronimo-servlet_3.0_spec - ${geronimo-servlet-spec-version} provided diff --git a/components/camel-stomp/pom.xml b/components/camel-stomp/pom.xml index dc506e224f117..6637a0c9a6989 100644 --- a/components/camel-stomp/pom.xml +++ b/components/camel-stomp/pom.xml @@ -55,7 +55,6 @@ org.apache.activemq activemq-broker - ${activemq-version} test @@ -67,19 +66,16 @@ junit junit - ${junit-version} test org.slf4j slf4j-log4j12 - ${slf4j-version} test com.thoughtworks.xstream xstream - ${xstream-version} test diff --git a/components/camel-test-blueprint/pom.xml b/components/camel-test-blueprint/pom.xml index 3045bbda8b8c8..363db78dbb53e 100644 --- a/components/camel-test-blueprint/pom.xml +++ b/components/camel-test-blueprint/pom.xml @@ -62,7 +62,6 @@ org.apache.aries.blueprint org.apache.aries.blueprint - ${aries-blueprint-core-version} org.apache.aries @@ -97,7 +96,6 @@ org.ops4j.pax.swissbox pax-swissbox-tinybundles - ${pax-tiny-bundle-version} commons-logging diff --git a/components/camel-web/pom.xml b/components/camel-web/pom.xml index 01d311c9313cc..d41e41fea2c76 100644 --- a/components/camel-web/pom.xml +++ b/components/camel-web/pom.xml @@ -179,7 +179,6 @@ org.eclipse.jetty jetty-webapp - ${jetty-version} test @@ -255,7 +254,6 @@ org.mortbay.jetty jetty-maven-plugin - ${jetty-version} diff --git a/examples/camel-example-activemq-tomcat/pom.xml b/examples/camel-example-activemq-tomcat/pom.xml index 8edc81f3cd1d3..177ef1c29fba5 100755 --- a/examples/camel-example-activemq-tomcat/pom.xml +++ b/examples/camel-example-activemq-tomcat/pom.xml @@ -35,7 +35,6 @@ org.codehaus.mojo tomcat-maven-plugin - 1.1 myTomcat ${tomcat.url} diff --git a/examples/camel-example-cxf-tomcat/pom.xml b/examples/camel-example-cxf-tomcat/pom.xml index 38c75e6e834f9..eaa48d49a2779 100755 --- a/examples/camel-example-cxf-tomcat/pom.xml +++ b/examples/camel-example-cxf-tomcat/pom.xml @@ -35,7 +35,6 @@ org.codehaus.mojo tomcat-maven-plugin - 1.1 myTomcat ${tomcat.url} diff --git a/examples/camel-example-cxf/pom.xml b/examples/camel-example-cxf/pom.xml index 7ac8b12b602c8..e761867fc938c 100644 --- a/examples/camel-example-cxf/pom.xml +++ b/examples/camel-example-cxf/pom.xml @@ -109,7 +109,6 @@ org.apache.geronimo.specs geronimo-j2ee-connector_1.5_spec - ${geronimo-j2ee-connector-spec-version} provided diff --git a/examples/camel-example-gauth/pom.xml b/examples/camel-example-gauth/pom.xml index 664976e7e3437..69b65dd840220 100644 --- a/examples/camel-example-gauth/pom.xml +++ b/examples/camel-example-gauth/pom.xml @@ -57,7 +57,6 @@ org.springframework spring-web - ${spring-version} org.springframework diff --git a/examples/camel-example-osgi/pom.xml b/examples/camel-example-osgi/pom.xml index 82f10033d2c22..c90a1955bfe39 100644 --- a/examples/camel-example-osgi/pom.xml +++ b/examples/camel-example-osgi/pom.xml @@ -48,7 +48,6 @@ org.slf4j slf4j-simple - ${slf4j-version} diff --git a/examples/camel-example-ssh-security/pom.xml b/examples/camel-example-ssh-security/pom.xml index 2add587e0cd44..2450d593b81e4 100644 --- a/examples/camel-example-ssh-security/pom.xml +++ b/examples/camel-example-ssh-security/pom.xml @@ -80,13 +80,11 @@ org.slf4j slf4j-log4j12 - ${slf4j-version} runtime log4j log4j - ${log4j-version} runtime @@ -104,7 +102,6 @@ junit junit - ${junit-version} test diff --git a/examples/camel-example-ssh/pom.xml b/examples/camel-example-ssh/pom.xml index 55918441bdd64..67ae0ff9bc4a0 100644 --- a/examples/camel-example-ssh/pom.xml +++ b/examples/camel-example-ssh/pom.xml @@ -62,13 +62,11 @@ org.slf4j slf4j-log4j12 - ${slf4j-version} runtime log4j log4j - ${log4j-version} runtime @@ -86,7 +84,6 @@ junit junit - ${junit-version} test diff --git a/platforms/karaf/features/pom.xml b/platforms/karaf/features/pom.xml index 46c26a4ecc03d..6b55778229ff1 100644 --- a/platforms/karaf/features/pom.xml +++ b/platforms/karaf/features/pom.xml @@ -39,7 +39,6 @@ org.osgi org.osgi.core - ${osgi-version} provided @@ -63,13 +62,11 @@ org.apache.felix org.apache.felix.configadmin - ${felix-configadmin-version} provided org.apache.aries.blueprint org.apache.aries.blueprint - ${aries-blueprint-core-version} provided diff --git a/tests/camel-itest-karaf/pom.xml b/tests/camel-itest-karaf/pom.xml index ed480cd4f5fe3..50f9264f124c1 100644 --- a/tests/camel-itest-karaf/pom.xml +++ b/tests/camel-itest-karaf/pom.xml @@ -76,7 +76,6 @@ org.osgi org.osgi.core - ${osgi-version} test diff --git a/tests/camel-itest-osgi/pom.xml b/tests/camel-itest-osgi/pom.xml index e152c3bbd5799..971c115f5b365 100644 --- a/tests/camel-itest-osgi/pom.xml +++ b/tests/camel-itest-osgi/pom.xml @@ -78,14 +78,12 @@ org.osgi org.osgi.compendium - ${osgi-version} compile true org.osgi org.osgi.core - ${osgi-version} test diff --git a/tooling/apt/pom.xml b/tooling/apt/pom.xml index c0ebb8a1c3448..30aef0b41162a 100644 --- a/tooling/apt/pom.xml +++ b/tooling/apt/pom.xml @@ -48,7 +48,6 @@ junit junit - ${junit-version} test @@ -65,7 +64,6 @@ maven-compiler-plugin - ${maven-compiler-plugin-version} 1.6 1.6 From a59414d3751a6ba0946dcc60193c6d32db92a115 Mon Sep 17 00:00:00 2001 From: Claus Ibsen Date: Mon, 7 Apr 2014 11:18:06 +0200 Subject: [PATCH 35/53] CAMEL-7347: camel-netty-http should return 404 instead 503 if resource not found. --- .../handlers/HttpServerMultiplexChannelHandler.java | 6 +++--- .../http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java | 11 ++++++++++- .../http/NettyHttpTwoRoutesStopOneRouteTest.java | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java index 3351fd896f900..e00abd4a4764a 100644 --- a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java +++ b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerMultiplexChannelHandler.java @@ -38,7 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.jboss.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; +import static org.jboss.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; /** @@ -102,8 +102,8 @@ public void messageReceived(ChannelHandlerContext ctx, MessageEvent messageEvent ctx.setAttachment(handler); handler.messageReceived(ctx, messageEvent); } else { - // this service is not available, so send empty response back - HttpResponse response = new DefaultHttpResponse(HTTP_1_1, SERVICE_UNAVAILABLE); + // this resource is not found, so send empty response back + HttpResponse response = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND); response.setHeader(Exchange.CONTENT_TYPE, "text/plain"); response.setHeader(Exchange.CONTENT_LENGTH, 0); response.setContent(ChannelBuffers.copiedBuffer(new byte[]{})); diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java index 382e2c95cad91..370753f0b3b28 100644 --- a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesMatchOnUriPrefixTest.java @@ -36,7 +36,16 @@ public void testTwoRoutesMatchOnUriPrefix() throws Exception { fail("Should have thrown exception"); } catch (CamelExecutionException e) { NettyHttpOperationFailedException cause = assertIsInstanceOf(NettyHttpOperationFailedException.class, e.getCause()); - assertEquals(503, cause.getStatusCode()); + assertEquals(404, cause.getStatusCode()); + } + + // .. and likewise baz is not a context-path we have mapped as input + try { + template.requestBody("netty-http:http://localhost:{{port}}/baz", "Hello World", String.class); + fail("Should have thrown exception"); + } catch (CamelExecutionException e) { + NettyHttpOperationFailedException cause = assertIsInstanceOf(NettyHttpOperationFailedException.class, e.getCause()); + assertEquals(404, cause.getStatusCode()); } out = template.requestBody("netty-http:http://localhost:{{port}}/bar", "Hello Camel", String.class); diff --git a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesStopOneRouteTest.java b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesStopOneRouteTest.java index 75635111b6fbf..9e9dc53652645 100644 --- a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesStopOneRouteTest.java +++ b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/NettyHttpTwoRoutesStopOneRouteTest.java @@ -43,13 +43,13 @@ public void testTwoRoutes() throws Exception { getMockEndpoint("mock:foo").expectedMessageCount(0); getMockEndpoint("mock:bar").expectedBodiesReceived("Hello Camel"); - // the foo route is stopped so this service is not available + // the foo route is stopped so this service is no longer there try { template.requestBody("netty-http:http://localhost:{{port}}/foo", "Hello World", String.class); fail("Should have thrown exception"); } catch (CamelExecutionException e) { NettyHttpOperationFailedException cause = assertIsInstanceOf(NettyHttpOperationFailedException.class, e.getCause()); - assertEquals(503, cause.getStatusCode()); + assertEquals(404, cause.getStatusCode()); } out = template.requestBody("netty-http:http://localhost:{{port}}/bar", "Hello Camel", String.class); From 9122881581c14f2e4c3fe32ae6660d35bcccf34a Mon Sep 17 00:00:00 2001 From: Akitoshi Yoshida Date: Mon, 7 Apr 2014 14:42:35 +0200 Subject: [PATCH 36/53] CAMEL-7344: Some endpoints configured using beans may result in NPE under DEBUG mode --- .../apache/camel/impl/DefaultEndpoint.java | 8 +++++- .../camel/impl/DefaultEndpointTest.java | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultEndpoint.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultEndpoint.java index 3dae2895bf2d5..badd48ecb63d4 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultEndpoint.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultEndpoint.java @@ -136,7 +136,13 @@ public boolean equals(Object object) { @Override public String toString() { - return String.format("Endpoint[%s]", URISupport.sanitizeUri(getEndpointUri())); + String value = null; + try { + value = getEndpointUri(); + } catch (RuntimeException e) { + // ignore any exception and use null for building the string value + } + return String.format("Endpoint[%s]", URISupport.sanitizeUri(value)); } /** diff --git a/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointTest.java b/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointTest.java index 81040955e0295..0b61ef770d53e 100644 --- a/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointTest.java +++ b/camel-core/src/test/java/org/apache/camel/impl/DefaultEndpointTest.java @@ -16,7 +16,10 @@ */ package org.apache.camel.impl; +import org.apache.camel.Consumer; import org.apache.camel.ContextTestSupport; +import org.apache.camel.Processor; +import org.apache.camel.Producer; import org.apache.camel.util.URISupport; /** @@ -39,6 +42,14 @@ public void testSanitizeUri() { URISupport.sanitizeUri("aws-sqs://MyQueue?accessKey=1672t4rflhnhli3&secretKey=qi472qfberu33dqjncq")); } + public void testToString() { + final String epstr = "myep:///test"; + MyEndpoint ep = new MyEndpoint(); + assertNotNull(ep.toString()); + ep.setEndpointUri(epstr); + assertTrue(ep.toString().indexOf(epstr) > 0); + } + /** * Ensures that the Uri was not changed because no password was found. * @@ -48,4 +59,20 @@ private void assertSanitizedUriUnchanged(String uri) { assertEquals(uri, URISupport.sanitizeUri(uri)); } + private static class MyEndpoint extends DefaultEndpoint { + @Override + public Producer createProducer() throws Exception { + return null; + } + + @Override + public Consumer createConsumer(Processor processor) throws Exception { + return null; + } + + @Override + public boolean isSingleton() { + return false; + } + } } From 1949235f1ae94bbd3fc8e7d7f7b6bb1e854ecae1 Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Mon, 7 Apr 2014 17:37:28 -0400 Subject: [PATCH 37/53] CAMEL-5539: Fix compilation problems Fix typo as requested by Bilgin Ibryam . Signed-off-by: Gregor Zurowski --- .../org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala b/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala index c2b31c39ad5b1..5fdb2ab4c5638 100644 --- a/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala +++ b/components/camel-scala/src/main/scala/org/apache/camel/scala/dsl/SLoadBalanceDefinition.scala @@ -26,7 +26,7 @@ import org.apache.camel.Exchange case class SLoadBalanceDefinition(override val target: LoadBalanceDefinition)(implicit val builder: RouteBuilder) extends SAbstractDefinition[LoadBalanceDefinition] { def circuitbreaker(threshold: Int, halfOpenAfter: Long, classes: Class[_]*) - = wrap(target.circuitBreaker(threshold, halfOpenAfter, classesb: _*)) + = wrap(target.circuitBreaker(threshold, halfOpenAfter, classes: _*)) def failover(classes: Class[_]*) = wrap(target.failover(classes: _*)) From cd8da7d36b008a6f11e5c00715c1906b1a02a87d Mon Sep 17 00:00:00 2001 From: Grzegorz Grzybek Date: Tue, 8 Apr 2014 08:56:32 +0200 Subject: [PATCH 38/53] [CAMEL-6694] Adding INFO log about Loggers discovered in Registry --- .../main/java/org/apache/camel/component/log/LogComponent.java | 1 + .../src/main/java/org/apache/camel/model/LogDefinition.java | 1 + 2 files changed, 2 insertions(+) diff --git a/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java b/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java index c24189c9f4e6d..1e9e18b56a6ff 100644 --- a/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java +++ b/camel-core/src/main/java/org/apache/camel/component/log/LogComponent.java @@ -55,6 +55,7 @@ protected Endpoint createEndpoint(String uri, String remaining, Map availableLoggers = getCamelContext().getRegistry().findByTypeWithName(Logger.class); if (availableLoggers.size() == 1) { providedLogger = availableLoggers.values().iterator().next(); + LOG.info("Using custom Logger: {}", providedLogger); } else if (availableLoggers.size() > 1) { LOG.info("More than one {} instance found in the registry. Falling back to creating logger from URI {}.", Logger.class.getName(), uri); } diff --git a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java index 883c7a3370bec..7aa000ea3aa8d 100644 --- a/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/LogDefinition.java @@ -100,6 +100,7 @@ public Processor createProcessor(RouteContext routeContext) throws Exception { Map availableLoggers = routeContext.lookupByType(Logger.class); if (availableLoggers.size() == 1) { logger = availableLoggers.values().iterator().next(); + LOG.info("Using custom Logger: {}", logger); } else if (availableLoggers.size() > 1) { // we should log about this somewhere... LOG.info("More than one {} instance found in the registry. Falling back to create logger by name.", Logger.class.getName()); From faeeb925f63f087a6cd423d31be5a6e30cdf6395 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Wed, 16 Oct 2013 01:04:42 +0400 Subject: [PATCH 39/53] Created. --- .../HazelcastAggregationRepository.java | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java new file mode 100644 index 0000000000000..fe72d04a1595f --- /dev/null +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -0,0 +1,237 @@ +package org.apache.camel.processor.aggregate.hazelcast; + + +import com.hazelcast.config.Config; +import com.hazelcast.config.XmlConfigBuilder; +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.core.IMap; +import com.hazelcast.core.TransactionalMap; +import com.hazelcast.transaction.TransactionContext; +import com.hazelcast.transaction.TransactionOptions; +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.spi.OptimisticLockingAggregationRepository; +import org.apache.camel.spi.RecoverableAggregationRepository; +import org.apache.camel.support.ServiceSupport; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class HazelcastAggregationRepository extends ServiceSupport + implements RecoverableAggregationRepository, + OptimisticLockingAggregationRepository { + private boolean optimistic; + private boolean localHzInstance; + private boolean useRecovery; + private IMap cache; + private IMap persistedCache; + private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; + private HazelcastInstance hzInstance; + private String mapName; + private String completedSuffix = "-completed"; + private String deadLetterChannel; + private long recoveryInterval; + private int maximumRedeliveries; + + public HazelcastAggregationRepository(final String repositoryName) { + mapName = repositoryName; + optimistic = false; + localHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, boolean optimistic) { + this(repositoryName); + this.optimistic = optimistic; + localHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, HazelcastInstance hzInstanse) { + this (repositoryName, false); + this.hzInstance = hzInstanse; + localHzInstance = false; + } + + public HazelcastAggregationRepository(final String repositoryName, boolean optimistic, HazelcastInstance hzInstance) { + this(repositoryName, optimistic); + this.hzInstance = hzInstance; + } + + @Override + public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, Exchange newExchange) throws OptimisticLockingException { + if (!optimistic) { + throw new UnsupportedOperationException(); + } + if (oldExchange == null) { + if (cache.putIfAbsent(key, newExchange) != null) { + throw new OptimisticLockingException(); + } + } else { + if (!cache.replace(key, oldExchange, newExchange)) { + throw new OptimisticLockingException(); + } + } + return oldExchange; + } + + @Override + public Exchange add(CamelContext camelContext, String key, Exchange exchange) { + if (optimistic){ + throw new UnsupportedOperationException(); + } + Lock l = hzInstance.getLock(mapName); + try { + l.lock(); + return cache.put(key, exchange); + } finally { + l.unlock(); + } + } + + @Override + public Set scan(CamelContext camelContext) { + if (useRecovery) + return Collections.unmodifiableSet(persistedCache.keySet()); + else + return Collections.emptySet(); + } + + @Override + public Exchange recover(CamelContext camelContext, String exchangeId) { + return useRecovery ? persistedCache.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 + public Exchange get(CamelContext camelContext, String key) { + return cache.get(key); + } + + @Override + public void remove(CamelContext camelContext, String key, Exchange exchange) { + if (optimistic) { + if (!cache.remove(key, exchange)) { + throw new OptimisticLockingException(); + } + if (useRecovery) { + persistedCache.put(key, exchange); + } + } else { + if (useRecovery) { + // The only considerable case for transaction usage is fault tolerance: + // the transaction will be rolled back automatically (default timeout is 2 minutes) + // if no commit occurs during the timeout. So we are still consistent whether local node crashes. + + TransactionOptions tOpts = new TransactionOptions(); + + tOpts.setTransactionType(TransactionOptions.TransactionType.LOCAL); + TransactionContext tCtx = hzInstance.newTransactionContext(tOpts); + tCtx.beginTransaction(); + + TransactionalMap tCache = tCtx.getMap(cache.getName()); + TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); + + tCache.remove(key); + tPersistentCache.put(key, exchange); + + tCtx.commitTransaction(); + } else { + cache.remove(key); + } + } + } + + @Override + public void confirm(CamelContext camelContext, String exchangeId) { + persistedCache.remove(exchangeId); + } + + @Override + public Set getKeys() { + return Collections.unmodifiableSet(cache.keySet()); + } + + public void setCompletedSuffix(final String completedSuffix) { + this.completedSuffix = completedSuffix; + } + + public String getCompletedSuffix() { + return completedSuffix; + } + + @Override + protected void doStart() throws Exception { + if (localHzInstance) { + Config cfg = new XmlConfigBuilder().build(); + cfg.setProperty("hazelcast.version.check.enabled", "false"); + hzInstance = Hazelcast.newHazelcastInstance(cfg); + } + cache = hzInstance.getMap(mapName); + if (useRecovery) { + persistedCache = hzInstance.getMap(String.format("%s%s", mapName, completedSuffix)); + } + } + + @Override + protected void doStop() throws Exception { + if (useRecovery) { + persistedCache.clear(); + } + + cache.clear(); + + if (localHzInstance) { + hzInstance.getLifecycleService().shutdown(); + } + } +} From e8c3b7c78ee2c34662414c1d13f069f417361350 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Wed, 16 Oct 2013 14:29:05 +0400 Subject: [PATCH 40/53] Persistent repository name handling updated to achieve robustness and consistency. --- .../HazelcastAggregationRepository.java | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index fe72d04a1595f..b1d498d29e751 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -20,6 +20,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; +import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,34 +28,55 @@ public final class HazelcastAggregationRepository extends ServiceSupport implements RecoverableAggregationRepository, OptimisticLockingAggregationRepository { private boolean optimistic; - private boolean localHzInstance; + private boolean useLocalHzInstance; private boolean useRecovery; private IMap cache; private IMap persistedCache; private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; private HazelcastInstance hzInstance; private String mapName; - private String completedSuffix = "-completed"; + private String persistenceMapName; + private static final String COMPLETED_SUFFIX = "-completed"; private String deadLetterChannel; private long recoveryInterval; private int maximumRedeliveries; public HazelcastAggregationRepository(final String repositoryName) { mapName = repositoryName; + persistenceMapName = String.format("%s%s", mapName, COMPLETED_SUFFIX); optimistic = false; - localHzInstance = true; + useLocalHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName) { + mapName = repositoryName; + persistenceMapName = persistentRepositoryName; + optimistic = false; + useLocalHzInstance = true; } public HazelcastAggregationRepository(final String repositoryName, boolean optimistic) { this(repositoryName); this.optimistic = optimistic; - localHzInstance = true; + useLocalHzInstance = true; + } + + public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic) { + this(repositoryName, persistenRepositoryName); + this.optimistic = optimistic; + useLocalHzInstance = true; } public HazelcastAggregationRepository(final String repositoryName, HazelcastInstance hzInstanse) { this (repositoryName, false); this.hzInstance = hzInstanse; - localHzInstance = false; + useLocalHzInstance = false; + } + + public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, HazelcastInstance hzInstanse) { + this (repositoryName, persistenRepositoryName, false); + this.hzInstance = hzInstanse; + useLocalHzInstance = false; } public HazelcastAggregationRepository(final String repositoryName, boolean optimistic, HazelcastInstance hzInstance) { @@ -62,6 +84,11 @@ public HazelcastAggregationRepository(final String repositoryName, boolean optim this.hzInstance = hzInstance; } + public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic, HazelcastInstance hzInstance) { + this(repositoryName, persistenRepositoryName, optimistic); + this.hzInstance = hzInstance; + } + @Override public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, Exchange newExchange) throws OptimisticLockingException { if (!optimistic) { @@ -171,7 +198,6 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { // The only considerable case for transaction usage is fault tolerance: // the transaction will be rolled back automatically (default timeout is 2 minutes) // if no commit occurs during the timeout. So we are still consistent whether local node crashes. - TransactionOptions tOpts = new TransactionOptions(); tOpts.setTransactionType(TransactionOptions.TransactionType.LOCAL); @@ -201,27 +227,30 @@ public Set getKeys() { return Collections.unmodifiableSet(cache.keySet()); } - public void setCompletedSuffix(final String completedSuffix) { - this.completedSuffix = completedSuffix; - } - - public String getCompletedSuffix() { - return completedSuffix; + public String getPersistentRepositoryName() { + return getPersistentMapName(); } @Override protected void doStart() throws Exception { - if (localHzInstance) { + ObjectHelper.notEmpty(mapName, "repositoryName"); + if (useLocalHzInstance) { Config cfg = new XmlConfigBuilder().build(); cfg.setProperty("hazelcast.version.check.enabled", "false"); hzInstance = Hazelcast.newHazelcastInstance(cfg); + } else { + ObjectHelper.notNull(hzInstance, "hzInstanse"); } cache = hzInstance.getMap(mapName); if (useRecovery) { - persistedCache = hzInstance.getMap(String.format("%s%s", mapName, completedSuffix)); + persistedCache = hzInstance.getMap(persistenceMapName); } } + private String getPersistentMapName() { + return persistenceMapName; + } + @Override protected void doStop() throws Exception { if (useRecovery) { @@ -230,7 +259,7 @@ protected void doStop() throws Exception { cache.clear(); - if (localHzInstance) { + if (useLocalHzInstance) { hzInstance.getLifecycleService().shutdown(); } } From a48f3fc1a0dca490774d4fa51d2d42fcce3c103b Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Mon, 21 Oct 2013 01:07:24 +0400 Subject: [PATCH 41/53] Lots of trace logging. --- .../HazelcastAggregationRepository.java | 71 ++++++++++++++----- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index b1d498d29e751..e53ea40f8a6a0 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -38,7 +38,7 @@ public final class HazelcastAggregationRepository extends ServiceSupport private String persistenceMapName; private static final String COMPLETED_SUFFIX = "-completed"; private String deadLetterChannel; - private long recoveryInterval; + private long recoveryInterval = 5000; private int maximumRedeliveries; public HazelcastAggregationRepository(final String repositoryName) { @@ -94,15 +94,22 @@ public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, if (!optimistic) { throw new UnsupportedOperationException(); } + LOG.trace("Adding an Exchange with ID {} for key {} in an optimistic manner.", newExchange.getExchangeId(), key); if (oldExchange == null) { - if (cache.putIfAbsent(key, newExchange) != null) { + final Exchange misbehaviorEx = cache.putIfAbsent(key, newExchange); + if (misbehaviorEx != null) { + 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, misbehaviorEx.getExchangeId()); throw new OptimisticLockingException(); } } else { if (!cache.replace(key, oldExchange, newExchange)) { + 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; } @@ -111,25 +118,35 @@ 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); Lock l = hzInstance.getLock(mapName); try { l.lock(); return cache.put(key, exchange); } finally { + LOG.trace("Adding an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); l.unlock(); } } @Override public Set scan(CamelContext camelContext) { - if (useRecovery) - return Collections.unmodifiableSet(persistedCache.keySet()); - else + if (useRecovery) { + LOG.trace("Scanning for exchanges to recover in {} context", camelContext.getName()); + Set scanned = Collections.unmodifiableSet(persistedCache.keySet()); + LOG.trace("Found {} keys for exchanges to recover in {} context", scanned.size(),camelContext.getName()); + return scanned; + } + else { + LOG.warn("What for to run recovery scans in {} context while repository {} is running in non-recoverable aggregation repository mode?!", + camelContext.getName(), mapName); return Collections.emptySet(); + } } @Override public Exchange recover(CamelContext camelContext, String exchangeId) { + LOG.trace("Recovering an Exchange with ID {}.", exchangeId); return useRecovery ? persistedCache.get(exchangeId) : null; } @@ -187,14 +204,23 @@ public Exchange get(CamelContext camelContext, String key) { @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { if (optimistic) { + LOG.trace("Removing an exchange with ID {} for key {} in an optimistic manner.", exchange.getExchangeId(), key); if (!cache.remove(key, exchange)) { + 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); if (useRecovery) { + LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", + exchange.getExchangeId(), key); persistedCache.put(key, exchange); + LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", + exchange.getExchangeId(), key); } } else { if (useRecovery) { + LOG.trace("Removing an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); // The only considerable case for transaction usage is fault tolerance: // the transaction will be rolled back automatically (default timeout is 2 minutes) // if no commit occurs during the timeout. So we are still consistent whether local node crashes. @@ -202,15 +228,31 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { tOpts.setTransactionType(TransactionOptions.TransactionType.LOCAL); TransactionContext tCtx = hzInstance.newTransactionContext(tOpts); - tCtx.beginTransaction(); - TransactionalMap tCache = tCtx.getMap(cache.getName()); - TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); + try { + + tCtx.beginTransaction(); + + TransactionalMap tCache = tCtx.getMap(cache.getName()); + TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); - tCache.remove(key); - tPersistentCache.put(key, exchange); + tCache.remove(key); + LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", + exchange.getExchangeId(), key); + tPersistentCache.put(key, exchange); - tCtx.commitTransaction(); + tCtx.commitTransaction(); + LOG.trace("Removed an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); + LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", + exchange.getExchangeId(), key); + } catch (Throwable throwable) { + tCtx.rollbackTransaction(); + + final String msg = String.format("Transaction with ID %s was rolled back for remove operation with a key %s and an Exchange ID %s.", + tCtx.getTxnId(), key, exchange.getExchangeId()); + LOG.warn(msg, throwable); + throw new RuntimeException(msg, throwable); + } } else { cache.remove(key); } @@ -219,6 +261,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { @Override public void confirm(CamelContext camelContext, String exchangeId) { + LOG.trace("Confirming an exchange with ID {}.", exchangeId); persistedCache.remove(exchangeId); } @@ -228,7 +271,7 @@ public Set getKeys() { } public String getPersistentRepositoryName() { - return getPersistentMapName(); + return persistenceMapName; } @Override @@ -247,10 +290,6 @@ protected void doStart() throws Exception { } } - private String getPersistentMapName() { - return persistenceMapName; - } - @Override protected void doStop() throws Exception { if (useRecovery) { From f254e062c4569b392098450d122da39993f21129 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Mon, 21 Oct 2013 23:19:12 +0400 Subject: [PATCH 42/53] Tons of javadoc. --- .../HazelcastAggregationRepository.java | 100 ++++++++++++++++-- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index e53ea40f8a6a0..8bb0e5503fb2d 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -24,6 +24,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +/** + * A Hazelcast-based AggregationRepository implementing + * {@link RecoverableAggregationRepository} and {@link OptimisticLockingAggregationRepository}. + * Defaults to thread-safe (non-optimistic) locking and recoverable strategy. + * Hazelcast settings are given to an end-user and can be controlled with repositoryName and persistentRespositoryName, + * both are {@link com.hazelcast.core.IMap} <String, Exchange>. However HazelcastAggregationRepository + * can run it's own Hazelcast instance, but obviously no benefits of Hazelcast clustering are gained this way. + * If the {@link HazelcastAggregationRepository} uses it's own local {@link HazelcastInstance} it will destroy this + * instance on {@link #doStop()}. You should control {@link HazelcastInstance} lifecycle yourself whenever you instantiate + * {@link HazelcastAggregationRepository} passing a reference to the instance. + * + */ public final class HazelcastAggregationRepository extends ServiceSupport implements RecoverableAggregationRepository, OptimisticLockingAggregationRepository { @@ -39,15 +51,28 @@ public final class HazelcastAggregationRepository extends ServiceSupport private static final String COMPLETED_SUFFIX = "-completed"; private String deadLetterChannel; private long recoveryInterval = 5000; - private int maximumRedeliveries; - + private int maximumRedeliveries = 3; + + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior and a local Hazelcast instance. Recoverable repository name defaults to + * {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + */ public HazelcastAggregationRepository(final String repositoryName) { mapName = repositoryName; persistenceMapName = String.format("%s%s", mapName, COMPLETED_SUFFIX); optimistic = false; useLocalHzInstance = true; + useRecovery = true; } + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior and a local Hazelcast instance. + * @param repositoryName {@link IMap} repository name; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + */ public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName) { mapName = repositoryName; persistenceMapName = persistentRepositoryName; @@ -55,38 +80,80 @@ public HazelcastAggregationRepository(final String repositoryName, final String useLocalHzInstance = true; } + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior and a local Hazelcast instance. + * Recoverable repository name defaults to {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + * @param optimistic whether to use optimistic locking manner. + */ public HazelcastAggregationRepository(final String repositoryName, boolean optimistic) { this(repositoryName); this.optimistic = optimistic; useLocalHzInstance = true; } - public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic) { - this(repositoryName, persistenRepositoryName); + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior and a local Hazelcast instance. + * @param repositoryName {@link IMap} repository name; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + * @param optimistic whether to use optimistic locking manner. + */ + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName, boolean optimistic) { + this(repositoryName, persistentRepositoryName); this.optimistic = optimistic; useLocalHzInstance = true; } + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior. Recoverable repository name defaults to + * {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + * @param hzInstanse externally configured {@link HazelcastInstance}. + */ public HazelcastAggregationRepository(final String repositoryName, HazelcastInstance hzInstanse) { this (repositoryName, false); this.hzInstance = hzInstanse; useLocalHzInstance = false; } - public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, HazelcastInstance hzInstanse) { - this (repositoryName, persistenRepositoryName, false); + /** + * Creates new {@link HazelcastAggregationRepository} that defaults to non-optimistic locking + * with recoverable behavior. + * @param repositoryName {@link IMap} repository name; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + * @param hzInstanse externally configured {@link HazelcastInstance}. + */ + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName, HazelcastInstance hzInstanse) { + this (repositoryName, persistentRepositoryName, false); this.hzInstance = hzInstanse; useLocalHzInstance = false; } + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior. + * Recoverable repository name defaults to {@code repositoryName} + "-compeleted". + * @param repositoryName {@link IMap} repository name; + * @param optimistic whether to use optimistic locking manner; + * @param hzInstance externally configured {@link HazelcastInstance}. + */ public HazelcastAggregationRepository(final String repositoryName, boolean optimistic, HazelcastInstance hzInstance) { this(repositoryName, optimistic); this.hzInstance = hzInstance; + useLocalHzInstance = false; } - public HazelcastAggregationRepository(final String repositoryName, final String persistenRepositoryName, boolean optimistic, HazelcastInstance hzInstance) { - this(repositoryName, persistenRepositoryName, optimistic); + /** + * Creates new {@link HazelcastAggregationRepository} with recoverable behavior. + * @param repositoryName {@link IMap} repository name; + * @param optimistic whether to use optimistic locking manner; + * @param persistentRepositoryName {@link IMap} recoverable repository name; + * @param hzInstance externally configured {@link HazelcastInstance}. + */ + public HazelcastAggregationRepository(final String repositoryName, final String persistentRepositoryName, boolean optimistic, HazelcastInstance hzInstance) { + this(repositoryName, persistentRepositoryName, optimistic); this.hzInstance = hzInstance; + useLocalHzInstance = false; } @Override @@ -201,6 +268,14 @@ public Exchange get(CamelContext camelContext, String key) { return cache.get(key); } + /** + * This method performs transactional operation on removing the {@code exchange} + * from the operational storage and moving it into the persistent one if the {@link HazelcastAggregationRepository} + * runs in recoverable mode and {@code optimistic} is false. It will act at your own risk otherwise. + * @param camelContext the current CamelContext + * @param key the correlation key + * @param exchange the exchange to remove + */ @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { if (optimistic) { @@ -270,12 +345,21 @@ public Set getKeys() { return Collections.unmodifiableSet(cache.keySet()); } + /** + * @return Persistent repository {@link IMap} name; + */ public String getPersistentRepositoryName() { return persistenceMapName; } @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."); + } ObjectHelper.notEmpty(mapName, "repositoryName"); if (useLocalHzInstance) { Config cfg = new XmlConfigBuilder().build(); From 6b81f29e0acc6c35b50fa3884cef911946e80252 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 01:09:06 +0400 Subject: [PATCH 43/53] DefaultExchageHolders introduced. --- .../HazelcastAggregationRepository.java | 54 ++++++++++++------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index 8bb0e5503fb2d..54dbb94953495 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -11,6 +11,9 @@ import com.hazelcast.transaction.TransactionOptions; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; +import org.apache.camel.builder.xml.DomResultHandlerFactory; +import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.impl.DefaultExchangeHolder; import org.apache.camel.spi.OptimisticLockingAggregationRepository; import org.apache.camel.spi.RecoverableAggregationRepository; import org.apache.camel.support.ServiceSupport; @@ -42,8 +45,8 @@ public final class HazelcastAggregationRepository extends ServiceSupport private boolean optimistic; private boolean useLocalHzInstance; private boolean useRecovery; - private IMap cache; - private IMap persistedCache; + private IMap cache; + private IMap persistedCache; private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; private HazelcastInstance hzInstance; private String mapName; @@ -163,14 +166,18 @@ public Exchange add(CamelContext camelContext, String key, Exchange oldExchange, } LOG.trace("Adding an Exchange with ID {} for key {} in an optimistic manner.", newExchange.getExchangeId(), key); if (oldExchange == null) { - final Exchange misbehaviorEx = cache.putIfAbsent(key, newExchange); - if (misbehaviorEx != null) { + DefaultExchangeHolder holder = DefaultExchangeHolder.marshal(newExchange); + final DefaultExchangeHolder misbehaviorHolder = cache.putIfAbsent(key, holder); + if (misbehaviorHolder != null) { + Exchange misbehaviorEx = unmarshallExchange(camelContext, misbehaviorHolder); 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, misbehaviorEx.getExchangeId()); + key, misbehaviorEx != null ? misbehaviorEx.getExchangeId() : ""); throw new OptimisticLockingException(); } } else { - if (!cache.replace(key, oldExchange, newExchange)) { + DefaultExchangeHolder oldHolder = DefaultExchangeHolder.marshal(oldExchange); + DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(newExchange); + 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(); @@ -189,9 +196,11 @@ public Exchange add(CamelContext camelContext, String key, Exchange exchange) { Lock l = hzInstance.getLock(mapName); try { l.lock(); - return cache.put(key, exchange); + DefaultExchangeHolder newHolder = DefaultExchangeHolder.marshal(exchange); + DefaultExchangeHolder oldHolder = cache.put(key, newHolder); + return unmarshallExchange(camelContext, oldHolder); } finally { - LOG.trace("Adding an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); + LOG.trace("Added an Exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); l.unlock(); } } @@ -214,8 +223,7 @@ public Set scan(CamelContext camelContext) { @Override public Exchange recover(CamelContext camelContext, String exchangeId) { LOG.trace("Recovering an Exchange with ID {}.", exchangeId); - return useRecovery ? persistedCache.get(exchangeId) : null; - + return useRecovery ? unmarshallExchange(camelContext, persistedCache.get(exchangeId)) : null; } @Override @@ -265,7 +273,7 @@ public int getMaximumRedeliveries() { @Override public Exchange get(CamelContext camelContext, String key) { - return cache.get(key); + return unmarshallExchange(camelContext, cache.get(key)); } /** @@ -278,9 +286,10 @@ public Exchange get(CamelContext camelContext, String key) { */ @Override public void remove(CamelContext camelContext, String key, Exchange exchange) { + DefaultExchangeHolder holder = DefaultExchangeHolder.marshal(exchange); if (optimistic) { LOG.trace("Removing an exchange with ID {} for key {} in an optimistic manner.", exchange.getExchangeId(), key); - if (!cache.remove(key, exchange)) { + 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(); @@ -289,7 +298,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { if (useRecovery) { LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); - persistedCache.put(key, exchange); + persistedCache.put(key, holder); LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); } @@ -308,13 +317,13 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { tCtx.beginTransaction(); - TransactionalMap tCache = tCtx.getMap(cache.getName()); - TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); + TransactionalMap tCache = tCtx.getMap(cache.getName()); + TransactionalMap tPersistentCache = tCtx.getMap(persistedCache.getName()); - tCache.remove(key); + DefaultExchangeHolder removedHolder = tCache.remove(key); LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", exchange.getExchangeId(), key); - tPersistentCache.put(key, exchange); + tPersistentCache.put(key, removedHolder); tCtx.commitTransaction(); LOG.trace("Removed an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); @@ -379,11 +388,18 @@ protected void doStop() throws Exception { if (useRecovery) { persistedCache.clear(); } - cache.clear(); - if (useLocalHzInstance) { hzInstance.getLifecycleService().shutdown(); } } + + private Exchange unmarshallExchange(CamelContext camelContext, DefaultExchangeHolder holder) { + Exchange exchange = null; + if (holder != null) { + exchange = new DefaultExchange(camelContext); + DefaultExchangeHolder.unmarshal(exchange, holder); + } + return exchange; + } } From e22a96dc9a4447a796cc059d15b1132955d56fac Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 01:09:57 +0400 Subject: [PATCH 44/53] HazelcastAggregationRepository Constructor Tests were implemented. --- ...AggregationRepositoryConstructorsTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java new file mode 100644 index 0000000000000..393828f6a9efb --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java @@ -0,0 +1,75 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.Exchange; +import org.apache.camel.impl.DefaultExchange; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Before; +import org.junit.Test; + + +public class HazelcastAggregationRepositoryConstructorsTest extends CamelTestSupport { + private static final String HAZELCAST_INSTANCE_NAME = "HazelcastAggregationRepositoryTestInstance"; + private HazelcastInstance hzInstance; + + @Before + public void start() { + /*Config cfg = new XmlConfigBuilder().build(); + cfg.setProperty("hazelcast.version.check.enabled", "false"); + hzInstance = Hazelcast.newHazelcastInstance(cfg);*/ + } + + @Test(expected = UnsupportedOperationException.class) + public void nonOptimisticRepoFailsOnOptimisticAdd() throws Exception { + final String repoName = "hzRepoMap"; + HazelcastAggregationRepository repo = new HazelcastAggregationRepository(repoName); + repo.doStart(); + + try { + Exchange oldOne = new DefaultExchange(context()); + Exchange newOne = new DefaultExchange(context()); + final String key = "abrakadabra"; + repo.add(context(), key, oldOne, newOne); + fail("OptimisticLockingException should have been thrown"); + } finally { + repo.doStop(); + } + } + + @Test(expected = UnsupportedOperationException.class) + public void optimisticRepoFailsForNonOptimisticAdd() throws Exception { + final String repoName = "hzRepoMap"; + HazelcastAggregationRepository repo = new HazelcastAggregationRepository(repoName, true); + repo.doStart(); + + try { + Exchange ex = new DefaultExchange(context()); + final String key = "abrakadabra"; + repo.add(context(), key, ex); + } finally { + repo.doStop(); + } + } + + @Test(expected = IllegalArgumentException.class) + public void uninitializedHazelcastInstanceThrows() throws Exception { + final String repoName = "hzRepoMap"; + HazelcastAggregationRepository repo = new HazelcastAggregationRepository(repoName, (HazelcastInstance) null); + repo.doStart(); + } + + @Test + public void locallyInitializedHazelcastInstanceAdd() throws Exception { + HazelcastAggregationRepository repo = new HazelcastAggregationRepository("hzRepoMap"); + try { + repo.doStart(); + Exchange ex = new DefaultExchange(context()); + repo.add(context(), "somedefaultkey", ex); + //} catch (Throwable e) { + //fail(e.getMessage()); + } + finally { + repo.doStop(); + } + } +} From a729ce49efd16dc090be472c9f117184c128ca2e Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 01:10:57 +0400 Subject: [PATCH 45/53] Removed unused imports and variables. --- ...HazelcastAggregationRepositoryConstructorsTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java index 393828f6a9efb..99eb0cd6ec766 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java @@ -4,20 +4,10 @@ import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.Before; import org.junit.Test; public class HazelcastAggregationRepositoryConstructorsTest extends CamelTestSupport { - private static final String HAZELCAST_INSTANCE_NAME = "HazelcastAggregationRepositoryTestInstance"; - private HazelcastInstance hzInstance; - - @Before - public void start() { - /*Config cfg = new XmlConfigBuilder().build(); - cfg.setProperty("hazelcast.version.check.enabled", "false"); - hzInstance = Hazelcast.newHazelcastInstance(cfg);*/ - } @Test(expected = UnsupportedOperationException.class) public void nonOptimisticRepoFailsOnOptimisticAdd() throws Exception { From 8612ad3e18662c1ee8913051a233f5b88a860309 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 02:11:05 +0400 Subject: [PATCH 46/53] useRecovery always default to true. --- .../hazelcast/HazelcastAggregationRepository.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index 54dbb94953495..4c5909e256c7a 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -44,7 +44,7 @@ public final class HazelcastAggregationRepository extends ServiceSupport OptimisticLockingAggregationRepository { private boolean optimistic; private boolean useLocalHzInstance; - private boolean useRecovery; + private boolean useRecovery = true; private IMap cache; private IMap persistedCache; private static final Logger LOG = LoggerFactory.getLogger(HazelcastAggregationRepository.class.getName()) ; @@ -67,7 +67,6 @@ public HazelcastAggregationRepository(final String repositoryName) { persistenceMapName = String.format("%s%s", mapName, COMPLETED_SUFFIX); optimistic = false; useLocalHzInstance = true; - useRecovery = true; } /** @@ -298,7 +297,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { if (useRecovery) { LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); - persistedCache.put(key, holder); + persistedCache.put(exchange.getExchangeId(), holder); LOG.trace("Put an exchange with ID {} for key {} into a recoverable storage in an optimistic manner.", exchange.getExchangeId(), key); } @@ -323,7 +322,7 @@ public void remove(CamelContext camelContext, String key, Exchange exchange) { DefaultExchangeHolder removedHolder = tCache.remove(key); LOG.trace("Putting an exchange with ID {} for key {} into a recoverable storage in a thread-safe manner.", exchange.getExchangeId(), key); - tPersistentCache.put(key, removedHolder); + tPersistentCache.put(exchange.getExchangeId(), removedHolder); tCtx.commitTransaction(); LOG.trace("Removed an exchange with ID {} for key {} in a thread-safe manner.", exchange.getExchangeId(), key); From 0f34fb97fe1da62d6160ebd9682e4c084a626eb1 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Tue, 29 Oct 2013 02:18:02 +0400 Subject: [PATCH 47/53] Initial commit (test created). --- ...stAggregationRepositoryOperationsTest.java | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java new file mode 100644 index 0000000000000..cbc2d6c2d5f9d --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java @@ -0,0 +1,171 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.Exchange; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import java.util.Set; + + +public class HazelcastAggregationRepositoryOperationsTest extends CamelTestSupport { + + private static HazelcastInstance hzOne = null; + private static HazelcastInstance hzTwo = null; + private static final String THREAD_SAFE_REPO = "threadSafeRepo"; + private static final String OPTIMISTIC_REPO = "optimisticRepo"; + + @BeforeClass + public static void setUpHazelcastCluster() { + hzOne = Hazelcast.newHazelcastInstance(); + hzTwo = Hazelcast.newHazelcastInstance(); + } + + @AfterClass + public static void shutDownHazelcastCluster() { + hzOne.getLifecycleService().shutdown(); + hzTwo.getLifecycleService().shutdown(); + } + + @Test + public void checkOptimisticAddOfNewExchange() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is an optimistic test body. Sincerely yours, Captain Obvious."; + final String key = "optimisticKey"; + Exchange newEx = createExchangeWithBody(testBody); + Exchange oldEx = repoOne.add(context(), key, null, newEx); + + assertNull("Old exchange should be null.", oldEx); + + final String theNewestBody = "This is the newest test body."; + Exchange theNewestEx = createExchangeWithBody(theNewestBody); + + oldEx = repoTwo.add(context(), key, newEx, theNewestEx); + assertEquals(newEx.getIn().getBody(), oldEx.getIn().getBody()); + + } finally { + repoOne.stop(); + repoTwo.stop(); + } + } + + @Test + public void checkThreadSafeAddOfNewExchange() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is a thread-safe test body. Sincerely yours, Captain Obvious."; + final String key = "threadSafeKey"; + Exchange newEx = createExchangeWithBody(testBody); + Exchange oldEx = repoOne.add(context(), key, newEx); + + assertNull("Old exchange should be null.", oldEx); + + final String theNewestBody = "This is the newest test body."; + Exchange theNewestEx = createExchangeWithBody(theNewestBody); + + oldEx = repoTwo.add(context(), key, theNewestEx); + assertEquals(newEx.getIn().getBody(), oldEx.getIn().getBody()); + + } finally { + repoOne.stop(); + repoTwo.stop(); + } + } + + @Test + public void checkOptimisticGet() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzTwo); + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is an optimistic test body. Sincerely yours, Captain Obvious."; + final String key = "optimisticKey"; + + Exchange ex = createExchangeWithBody(testBody); + repoOne.add(context(), key, null, ex); + + Exchange gotEx = repoTwo.get(context(), key); + assertEquals("ex and gotEx should be equal", gotEx.getIn().getBody(), ex.getIn().getBody()); + } finally { + repoOne.doStop(); + repoTwo.doStop(); + } + } + + @Test + public void checkThreadSafeGet() throws Exception { + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + + final String testBody = "This is a thread-safe test body. Sincerely yours, Captain Obvious."; + final String key = "threadSafeKey"; + + Exchange ex = createExchangeWithBody(testBody); + repoOne.add(context(), key, ex); + + Exchange gotEx = repoTwo.get(context(), key); + assertEquals("ex and gotEx should be equal", gotEx.getIn().getBody(), ex.getIn().getBody()); + } finally { + repoOne.doStop(); + repoTwo.doStop(); + } + } + + @Test + public void checkOptimisticPersistentRemove() throws Exception { + final String persistentRepoName = String.format("%s-completed", OPTIMISTIC_REPO); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzOne); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzTwo); + + try { + repoOne.doStart(); + repoTwo.doStart(); + + final String testBody = "This is an optimistic test body. Sincerely yours, Captain Obvious."; + final String key = "optimisticKey"; + + Exchange ex = createExchangeWithBody(testBody); + + repoOne.add(context(), key, null, ex); + + Exchange getBackEx = repoTwo.get(context(), key); + assertNotNull("getBackEx should not be null.", getBackEx); + + repoTwo.remove(context(), key, ex); + + getBackEx = repoOne.get(context(), key); + assertNull("getBackEx should be null here.", getBackEx); + + Set keys = repoTwo.scan(context()); + assertCollectionSize(keys, 1); + + getBackEx = repoOne.recover(context(), keys.iterator().next()); + assertNotNull("getBackEx got from persistent repo should not be null.", getBackEx); + + + } finally { + repoOne.doStop(); + repoTwo.doStop(); + } + } +} From 629d60e31fd6009b9132e15b4d03d21b50c0fd57 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Fri, 13 Dec 2013 18:20:15 +0400 Subject: [PATCH 48/53] useRecovery always default to true. --- .../aggregate/hazelcast/HazelcastAggregationRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java index 4c5909e256c7a..3b493cac24735 100644 --- a/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java +++ b/components/camel-hazelcast/src/main/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepository.java @@ -11,7 +11,6 @@ import com.hazelcast.transaction.TransactionOptions; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; -import org.apache.camel.builder.xml.DomResultHandlerFactory; import org.apache.camel.impl.DefaultExchange; import org.apache.camel.impl.DefaultExchangeHolder; import org.apache.camel.spi.OptimisticLockingAggregationRepository; From bcdcb6485a487a5a944f21874f6098d3e34d8263 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 02:30:56 +0400 Subject: [PATCH 49/53] Typo fix. --- .../HazelcastAggregationRepositoryConstructorsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java index 99eb0cd6ec766..52581291f7b6b 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryConstructorsTest.java @@ -20,7 +20,7 @@ public void nonOptimisticRepoFailsOnOptimisticAdd() throws Exception { Exchange newOne = new DefaultExchange(context()); final String key = "abrakadabra"; repo.add(context(), key, oldOne, newOne); - fail("OptimisticLockingException should have been thrown"); + fail("OptimisticLockingException should has been thrown"); } finally { repo.doStop(); } From 77180f5316d8caf4e1272121a9777fef24676bbf Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 03:03:56 +0400 Subject: [PATCH 50/53] Superclass was extracted --- ...AggregationRepositoryCamelTestSupport.java | 45 +++++++++++++++++++ ...stAggregationRepositoryOperationsTest.java | 42 ++++++----------- 2 files changed, 58 insertions(+), 29 deletions(-) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java new file mode 100644 index 0000000000000..166c8a0b09f02 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryCamelTestSupport.java @@ -0,0 +1,45 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import com.hazelcast.core.Hazelcast; +import com.hazelcast.core.HazelcastInstance; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** + * @author Alexander Lomov + * Date: 04.01.14 + * Time: 3:00 + */ +public class HazelcastAggregationRepositoryCamelTestSupport extends CamelTestSupport { + private static HazelcastInstance hzOne = null; + private static HazelcastInstance hzTwo = null; + + protected static void doInitializeHazelcastInstances() { + hzOne = Hazelcast.newHazelcastInstance(); + hzTwo = Hazelcast.newHazelcastInstance(); + } + + protected static void doDestroyHazelcastInstances() { + hzOne.getLifecycleService().shutdown(); + hzTwo.getLifecycleService().shutdown(); + } + + protected static HazelcastInstance getFirstInstance() { + return hzOne; + } + + protected static HazelcastInstance getSecondInstance() { + return hzTwo; + } + + @BeforeClass + public static void setUpHazelcastCluster() { + doInitializeHazelcastInstances(); + } + + @AfterClass + public static void shutDownHazelcastCluster() { + doDestroyHazelcastInstances(); + } +} diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java index cbc2d6c2d5f9d..fea8176a2d6e9 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryOperationsTest.java @@ -1,38 +1,22 @@ package org.apache.camel.processor.aggregate.hazelcast; -import com.hazelcast.core.Hazelcast; -import com.hazelcast.core.HazelcastInstance; import org.apache.camel.Exchange; -import org.apache.camel.test.junit4.CamelTestSupport; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; + import java.util.Set; -public class HazelcastAggregationRepositoryOperationsTest extends CamelTestSupport { +public class HazelcastAggregationRepositoryOperationsTest extends HazelcastAggregationRepositoryCamelTestSupport { + - private static HazelcastInstance hzOne = null; - private static HazelcastInstance hzTwo = null; private static final String THREAD_SAFE_REPO = "threadSafeRepo"; private static final String OPTIMISTIC_REPO = "optimisticRepo"; - @BeforeClass - public static void setUpHazelcastCluster() { - hzOne = Hazelcast.newHazelcastInstance(); - hzTwo = Hazelcast.newHazelcastInstance(); - } - - @AfterClass - public static void shutDownHazelcastCluster() { - hzOne.getLifecycleService().shutdown(); - hzTwo.getLifecycleService().shutdown(); - } @Test public void checkOptimisticAddOfNewExchange() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, true, getSecondInstance()); try { repoOne.doStart(); @@ -59,8 +43,8 @@ public void checkOptimisticAddOfNewExchange() throws Exception { @Test public void checkThreadSafeAddOfNewExchange() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, false, getSecondInstance()); try { repoOne.doStart(); @@ -87,8 +71,8 @@ public void checkThreadSafeAddOfNewExchange() throws Exception { @Test public void checkOptimisticGet() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(THREAD_SAFE_REPO, true, getSecondInstance()); try { repoOne.doStart(); repoTwo.doStart(); @@ -109,8 +93,8 @@ public void checkOptimisticGet() throws Exception { @Test public void checkThreadSafeGet() throws Exception { - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, false, getSecondInstance()); try { repoOne.doStart(); @@ -134,8 +118,8 @@ public void checkThreadSafeGet() throws Exception { @Test public void checkOptimisticPersistentRemove() throws Exception { final String persistentRepoName = String.format("%s-completed", OPTIMISTIC_REPO); - HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzOne); - HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, hzTwo); + HazelcastAggregationRepository repoOne = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, getFirstInstance()); + HazelcastAggregationRepository repoTwo = new HazelcastAggregationRepository(OPTIMISTIC_REPO, persistentRepoName, true, getSecondInstance()); try { repoOne.doStart(); From 1649c23ff859b4c78a476d92657291287f25de4d Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 04:24:36 +0400 Subject: [PATCH 51/53] More working tests. --- ...elcastAggregationRepositoryRoutesTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java new file mode 100644 index 0000000000000..e22abe58faa82 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java @@ -0,0 +1,100 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Exchange; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.processor.aggregate.AggregationStrategy; +import org.junit.Test; + +/** + * @author Alexander Lomov + * Date: 04.01.14 + * Time: 2:40 + */ + +public class HazelcastAggregationRepositoryRoutesTest extends HazelcastAggregationRepositoryCamelTestSupport { + + private static final String REPO_NAME = "routeTestRepo"; + private static final String MOCK_GOTCHA = "mock:gotcha"; + private static final String DIRECT_ONE = "direct:one"; + private static final String DIRECT_TWO = "direct:two"; + + @EndpointInject(uri = MOCK_GOTCHA) + private MockEndpoint mock; + + @Produce(uri = DIRECT_ONE) + private ProducerTemplate produceOne; + + @Produce(uri = DIRECT_TWO) + private ProducerTemplate produceTwo; + + + @Test + public void checkAggregationFromTwoRoutes() throws Exception { + final HazelcastAggregationRepository repoOne = + new HazelcastAggregationRepository(REPO_NAME, false, getFirstInstance()); + + final HazelcastAggregationRepository repoTwo = + new HazelcastAggregationRepository(REPO_NAME, false, getSecondInstance()); + + final int completionSize = 4; + final String correlator = "CORRELATOR"; + RouteBuilder rbOne = new RouteBuilder() { + @Override + public void configure() throws Exception { + + from(DIRECT_ONE).routeId("AggregatingRouteOne") + .aggregate(header(correlator)) + .aggregationRepository(repoOne) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_GOTCHA); + } + }; + + RouteBuilder rbTwo = new RouteBuilder() { + @Override + public void configure() throws Exception { + + from(DIRECT_TWO).routeId("AggregatingRouteTwo") + .aggregate(header(correlator)) + .aggregationRepository(repoTwo) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_GOTCHA); + } + }; + + context().addRoutes(rbOne); + context().addRoutes(rbTwo); + context().start(); + + mock.expectedMessageCount(1); + mock.expectedBodiesReceived(1 + 2 + 3 + 4); + + produceOne.sendBodyAndHeader(1, correlator, correlator); + produceTwo.sendBodyAndHeader(2, correlator, correlator); + produceOne.sendBodyAndHeader(3, correlator, correlator); + produceOne.sendBodyAndHeader(4, correlator, correlator); + + mock.assertIsSatisfied(); + } + + private static class SumOfIntsAggregationStrategy implements AggregationStrategy { + @Override + public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { + if (oldExchange == null) { + return newExchange; + } else { + Integer n = newExchange.getIn().getBody(Integer.class); + Integer o = oldExchange.getIn().getBody(Integer.class); + Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); + oldExchange.getIn().setBody(v, Integer.class); + return oldExchange; + } + } + } +} From a9b897c6b27af8ae1a0287b18a10e8f228d2d03e Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 13:28:34 +0400 Subject: [PATCH 52/53] Moved SumOfIntsAggregationStrategy class from inner level. --- ...elcastAggregationRepositoryRoutesTest.java | 16 ------------- .../SumOfIntsAggregationStrategy.java | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java index e22abe58faa82..2b16a5d5c5e6d 100644 --- a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRoutesTest.java @@ -1,12 +1,10 @@ package org.apache.camel.processor.aggregate.hazelcast; import org.apache.camel.EndpointInject; -import org.apache.camel.Exchange; import org.apache.camel.Produce; import org.apache.camel.ProducerTemplate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; -import org.apache.camel.processor.aggregate.AggregationStrategy; import org.junit.Test; /** @@ -83,18 +81,4 @@ public void configure() throws Exception { mock.assertIsSatisfied(); } - private static class SumOfIntsAggregationStrategy implements AggregationStrategy { - @Override - public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { - if (oldExchange == null) { - return newExchange; - } else { - Integer n = newExchange.getIn().getBody(Integer.class); - Integer o = oldExchange.getIn().getBody(Integer.class); - Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); - oldExchange.getIn().setBody(v, Integer.class); - return oldExchange; - } - } - } } diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java new file mode 100644 index 0000000000000..bf88b6322abe2 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/SumOfIntsAggregationStrategy.java @@ -0,0 +1,24 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import org.apache.camel.Exchange; +import org.apache.camel.processor.aggregate.AggregationStrategy; + +/** +* @author Alexander Lomov +* Date: 04.01.14 +* Time: 13:25 +*/ +class SumOfIntsAggregationStrategy implements AggregationStrategy { + @Override + public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { + if (oldExchange == null) { + return newExchange; + } else { + Integer n = newExchange.getIn().getBody(Integer.class); + Integer o = oldExchange.getIn().getBody(Integer.class); + Integer v = (o == null ? 0 : o) + (n == null ? 0 : n); + oldExchange.getIn().setBody(v, Integer.class); + return oldExchange; + } + } +} From 0fbe03e5cd209932f9faaaf31317077a566b2848 Mon Sep 17 00:00:00 2001 From: Alexander Lomov Date: Sat, 4 Jan 2014 14:03:25 +0400 Subject: [PATCH 53/53] Created a test case for recoverable HazelcastAggregationRepository. --- ...gationRepositoryRecoverableRoutesTest.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java diff --git a/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java new file mode 100644 index 0000000000000..cea590394ac40 --- /dev/null +++ b/components/camel-hazelcast/src/test/java/org/apache/camel/processor/aggregate/hazelcast/HazelcastAggregationRepositoryRecoverableRoutesTest.java @@ -0,0 +1,133 @@ +package org.apache.camel.processor.aggregate.hazelcast; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.Test; + +/** + * @author Alexander Lomov + * Date: 04.01.14 + * Time: 4:37 + */ + +public class HazelcastAggregationRepositoryRecoverableRoutesTest extends HazelcastAggregationRepositoryCamelTestSupport { + + private static final String REPO_NAME = "routeTestRepo"; + private static final String MOCK_GOTCHA = "mock:gotcha"; + private static final String MOCK_FAILURE = "mock:failure"; + private static final String DIRECT_ONE = "direct:one"; + private static final String DIRECT_TWO = "direct:two"; + + @EndpointInject(uri = MOCK_GOTCHA) + private MockEndpoint mockGotcha; + + @EndpointInject(uri = MOCK_FAILURE) + private MockEndpoint mockFailure; + + @Produce(uri = DIRECT_ONE) + private ProducerTemplate produceOne; + + @Produce(uri = DIRECT_TWO) + private ProducerTemplate produceTwo; + + @Test + public void checkAggregationFromTwoRoutesWithRecovery() throws Exception { + final HazelcastAggregationRepository repoOne = + new HazelcastAggregationRepository(REPO_NAME, false, getFirstInstance()); + + final HazelcastAggregationRepository repoTwo = + new HazelcastAggregationRepository(REPO_NAME, false, getSecondInstance()); + + final int completionSize = 4; + final String correlator = "CORRELATOR"; + + RouteBuilder rbOne = new RouteBuilder() { + @Override + public void configure() throws Exception { + + onException(EverythingIsLostException.class) + .handled(true) + .useOriginalMessage() + .to(MOCK_GOTCHA) + .end(); + + interceptSendToEndpoint(MOCK_FAILURE) + .throwException(new EverythingIsLostException("The field is lost... everything is lost")) + .end(); + + from(DIRECT_ONE) + .aggregate(header(correlator)) + .aggregationRepository(repoOne) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_FAILURE); + + } + }; + + + RouteBuilder rbTwo = new RouteBuilder() { + @Override + public void configure() throws Exception { + + onException(EverythingIsLostException.class) + .handled(true) + .useOriginalMessage() + .to(MOCK_GOTCHA) + .end(); + + interceptSendToEndpoint(MOCK_FAILURE) + .throwException(new EverythingIsLostException("The field is lost... everything is lost")) + .end(); + + from(DIRECT_TWO) + .aggregate(header(correlator)) + .aggregationRepository(repoTwo) + .aggregationStrategy(new SumOfIntsAggregationStrategy()) + .completionSize(completionSize) + .to(MOCK_FAILURE); + } + }; + + context().addRoutes(rbOne); + context().addRoutes(rbTwo); + context().start(); + + mockFailure.expectedMessageCount(0); + mockGotcha.expectedMessageCount(1); + mockGotcha.expectedBodiesReceived(1 + 2 + 3 + 4); + + produceOne.sendBodyAndHeader(4, correlator, correlator); + produceTwo.sendBodyAndHeader(2, correlator, correlator); + produceOne.sendBodyAndHeader(3, correlator, correlator); + produceTwo.sendBodyAndHeader(1, correlator, correlator); + + mockFailure.assertIsSatisfied(); + mockFailure.assertIsSatisfied(); + } + + @SuppressWarnings("unused") + private static class EverythingIsLostException extends Exception { + private EverythingIsLostException() { + } + + private EverythingIsLostException(String message) { + super(message); + } + + private EverythingIsLostException(String message, Throwable cause) { + super(message, cause); + } + + private EverythingIsLostException(Throwable cause) { + super(cause); + } + + private EverythingIsLostException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + } +}