0

I am using Apache Camel with SpringBoot to handle\process some data.

I was always thinking that exchange's properties can only be changed by fetching data from property via exchange.getProperty(..), processing data and setting new data into property via setProperty(..).

But now I see that my property in exchange is being changed while I am processing my fetched array.

Here is an example: I have an array of strings that I want to process (retain specific words from) in one property ("words" in sources) and another property ("retain" in sources) that holds a word that I want to replace.

I am fetching list of words into ArrayList<String> and run method retainAll to retain word I do not want. My idea is that original property in exchange will not be changed. But when I do run debug in unittest I can see that after retainAll(..) method has finished not only local ArrayList<String> is updated, but also property which holds all the words.

Am I overlooking something (wrongly using retainAll method, maybe?) or is it a bug?

StringsProccessor.class:

package com.mydummytest;

import java.util.ArrayList;
import java.util.stream.Collectors;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@Component
public class StringsProcessor implements Processor {

    @SuppressWarnings("unchecked")
    @Override
    public void process(Exchange exchange) throws Exception {
      ArrayList<String> input = (ArrayList<String>) exchange.getProperty("words");
      String toRetain = (String) exchange.getProperty("retain");

      input.retainAll(input.stream()
      .filter(s -> s.equals(toRetain))
      .collect(Collectors.toList())
      );
    }
}

StringsProcessorTest.class:

package com.mydummytest;

import java.util.ArrayList;
import java.util.Collections;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.impl.DefaultExchange;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class StringsProcessorTest {

  private Exchange exchange;
  private StringsProcessor proc;

  @BeforeEach
  void beforeEach() {
    // camel classes
    CamelContext ctx = new DefaultCamelContext();
    exchange = new DefaultExchange(ctx);
    proc = new StringsProcessor ();

    ArrayList<String> words = new ArrayList<String>();
    Collections.addAll(words, "fox", "racoon", "eagle", "salmon");

    String retain = "salmon";

    exchange.setProperty("words", words);
    exchange.setProperty("retain", retain);
  }

  @Test
  void testProcessing() throws Exception {
    proc.process(exchange);
  }

}
Dmitriy
  • 515
  • 7
  • 14
  • 2
    Then use a new array list – Claus Ibsen Jun 26 '18 at 12:04
  • Yes, it was a quick fix that was implemented and it works fine. Is it a bug in Camel? – Dmitriy Jun 26 '18 at 12:24
  • 2
    Properties are stored in HashMap and `input` stores reference to the same list. This is how Java works. https://stackoverflow.com/questions/934775/changing-value-after-its-placed-in-hashmap-changes-whats-inside-hashmap – Bedla Jun 26 '18 at 18:23
  • @Bedla thanks for explanation. So it is pass-by-reference in this case which confused me. – Dmitriy Jun 27 '18 at 05:22

1 Answers1

0

The answer was to use a new instance of an array list to hold value during processing (what I got from getProperty method was a reference to original property as @ClausIbsen and @Bedla pointed out).

StringProcessor.java class should look like this:

package com.mydummytest;

import java.util.ArrayList;
import java.util.stream.Collectors;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


@Component
public class StringsProcessor implements Processor {

    @SuppressWarnings("unchecked")
    @Override
    public void process(Exchange exchange) throws Exception {
      ArrayList<String> input = (ArrayList<String>) exchange.getProperty("words");
      String toRetain = (String) exchange.getProperty("retain");

      ArrayList<String> processed = input.stream()
      .filter(s -> s.equals(toRetain))
      .collect(Collectors.toList())
      );

      // do something with processed data
      // ...
    }
}
Dmitriy
  • 515
  • 7
  • 14