[ 
https://issues.apache.org/jira/browse/CAMEL-16567?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Dietrich Schulten updated CAMEL-16567:
--------------------------------------
    Description: 
It is not possible to mock consul producers, at least I have tried with the 
catalog and agent producers. Test:
{code:java}
public class MockAgentTest extends CamelTestSupport {

    @Test
    public void testMockAgent() throws Exception {
        MockEndpoint mockConsulAgent = getMockEndpoint("mock:consul:agent");

        AdviceWith.adviceWith(context, "servicesRoute", a -> {
            a.mockEndpointsAndSkip("consul:agent*");
        });
        mockConsulAgent.returnReplyBody(constant(ImmutableMap.of("foo-1", 
ImmutableService.builder()
            .id("foo-1")
            .service("foo")
            .address("localhost")
            .port(80)
            .build())));

        @SuppressWarnings("unchecked")
        Map<String, Service> result = 
fluentTemplate.to("direct:start").request(Map.class);
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").to("consul:agent?action=" + 
ConsulAgentActions.SERVICES);
            }
        };
    }
}
{code}
Error:
{code:java}
org.apache.camel.FailedToStartRouteException: Failed to start route 
servicesRoute because of null
 at org.apache.camel.impl.engine.RouteService.warmUp(RouteService.java:123) 
...
Caused by: java.lang.NullPointerException at 
org.apache.camel.support.HeaderSelectorProducer.doBuild(HeaderSelectorProducer.java:150)
 at org.apache.camel.support.service.BaseService.build(BaseService.java:63) at 
org.apache.camel.support.service.ServiceHelper.buildService(ServiceHelper.java:55)
 at 
org.apache.camel.support.service.ServiceHelper.buildService(ServiceHelper.java:72)
 at 
org.apache.camel.processor.InterceptSendToEndpointProcessor.doBuild(InterceptSendToEndpointProcessor.java:151)
 at org.apache.camel.support.service.BaseService.build(BaseService.java:63) at 
org.apache.camel.support.service.BaseService.init(BaseService.java:79) at 
org.apache.camel.support.service.BaseService.start(BaseService.java:111) at 
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:113)
 at 
org.apache.camel.impl.engine.AbstractCamelContext.internalAddService(AbstractCamelContext.java:1465)
 at 
org.apache.camel.impl.engine.AbstractCamelContext.addService(AbstractCamelContext.java:1383)
 at org.apache.camel.processor.SendProcessor.doStart(SendProcessor.java:247) at 
org.apache.camel.support.service.BaseService.start(BaseService.java:119) at 
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:113)
 at 
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:130)
 at 
org.apache.camel.processor.errorhandler.RedeliveryErrorHandler.doStart(RedeliveryErrorHandler.java:1638)
 at 
org.apache.camel.support.ChildServiceSupport.start(ChildServiceSupport.java:60) 
... 92 more{code}
The reason is that {{camelContext}} is {{null}} in {{HeaderSelectorProducer}}, 
but I am still unsure why that is:
{code:java}
@Override
protected void doBuild() throws Exception {
    super.doBuild();

    String key = this.getClass().getName();
    String fqn = RESOURCE_PATH + key;
    // -------- camelContext is null here:
    strategy = 
camelContext.adapt(ExtendedCamelContext.class).getBootstrapFactoryFinder(RESOURCE_PATH)
            .newInstance(key, InvokeOnHeaderStrategy.class)
            .orElseThrow(() -> new IllegalArgumentException("Cannot find " + 
fqn + " in classpath.")); 
    ...{code}
Same with catalog:
{code:java}
 public class MockCatalogTest extends CamelTestSupport {

    @Test
    public void testMockCatalog() throws Exception {
        MockEndpoint mockConsulAgent = getMockEndpoint("mock:consul:catalog");

        AdviceWith.adviceWith(context, "servicesRoute", a -> {
            a.mockEndpointsAndSkip("consul:catalog*");
        });
        
mockConsulAgent.returnReplyBody(constant(singletonList(ImmutableNode.builder().node("node-1").build())));

        @SuppressWarnings("unchecked")
        Map<String, Service> result = 
fluentTemplate.to("direct:start").request(Map.class);
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").to("consul:catalog?action=" + 
ConsulCatalogActions.LIST_NODES);
            }
        };
    }
}
{code}
Why this fails is not clear to me. Two instances of {{ConsulAgentProducer}} are 
being created. At runtime the method {{setCamelContext}} of the failing 
instance never gets called, although it is a {{CamelContextAware}}. I suspect 
that it is the mock or the real object behind the mock which is not a 
{{CamelContextAware}} so that it receives no {{camelContext}}. My assumption is 
that a mock is a kind of wrapper around the real thing, but I wasn't able to 
understand the relationship between the two enough to fix this. The instances 
look very similar, the failing instance appears not to be a Java proxy.

Anyone who understands the mocking mechanics better than me: what might go 
wrong here? Any hints how it *should* work? Is the mock a proxy or what exactly 
is the relationship between mock and mocked?

*UPDATE*

The failing instance is the delegate {{ConsulAgentProducer}} inside an 
{{InterceptSendToEndpointProcessor}}. The latter is not a 
{{CamelContextAware}}, hence it receives no {{camelContext.}} I see two 
different approaches:
 # The delegate inside the {{InterceptSendToEndpointProcessor}} could be made 
to receive a camelContext, e.g. by making the 
{{InterceptSendToEndpointProcess}} a {{CamelContextAware}} and setting the 
{{camelContext}} on the delegate
 # The method invocation {{HeaderSelectorProducer.doBuild()}} could be skipped 
somehow

Advice would be very much appreciated.

*UPDATE 2*

Turns out, it is not the {{delegate}} (ConsulEndpoint) member nor the 
{{endpoint}} (DefaultInterceptorSendToEndpoint) member inside 
InterceptSendToEndpointProcessor which has no CamelContext, but the 
{{producer}} (ConsulAgentProducer) member, which holds a second instance of the 
producer. Again, two approaches:
 # Let InterceptSendToEndpointProcessor set the camelContext on the producer 
when it receives the camelContext.
 # Find out why the second producer inside InterceptSendToEndpointProcessor 
does not receive a camelContext when it gets constructed

*UPDATE 3*

DefaultInterceptSendToEndpoint is responsible to create the producer in 
createAsyncProducer. One could call camelContext.addService(producer) there, in 
order to initialize the delegate producer like any other component. It does fix 
the problem, but I am unsure if the delegate producer should be added as a 
service or if it is the whole point not to add it.

_DefaultInterceptSendToEndpoint.java_
{code:java}
@Override
public AsyncProducer createAsyncProducer() throws Exception {
    AsyncProducer producer = delegate.createAsyncProducer();
    camelContext.addService(producer); // <-- insert this to initialize the 
producer with camelContext
    return 
camelContext.adapt(ExtendedCamelContext.class).getInternalProcessorFactory()
            .createInterceptSendToEndpointProcessor(this, delegate, producer, 
skip);
} {code}
The alternative would still be to make {{InterceptSendToEndpointProcessor}} a 
{{CamelContextAware}} and if its {{producer}} is a {{CamelContextAware}} in 
turn, set the camelContext on the {{producer}}, hold it in the 
{{InterceptSendToEndpointProcessor}} and get it from there in 
{{getCamelContext}}. Somehow this feels less intrusive than adding the producer 
as a service.

What would you recommend?

 

  was:
It is not possible to mock consul producers, at least I have tried with the 
catalog and agent producers. Test:
{code:java}
public class MockAgentTest extends CamelTestSupport {

    @Test
    public void testMockAgent() throws Exception {
        MockEndpoint mockConsulAgent = getMockEndpoint("mock:consul:agent");

        AdviceWith.adviceWith(context, "servicesRoute", a -> {
            a.mockEndpointsAndSkip("consul:agent*");
        });
        mockConsulAgent.returnReplyBody(constant(ImmutableMap.of("foo-1", 
ImmutableService.builder()
            .id("foo-1")
            .service("foo")
            .address("localhost")
            .port(80)
            .build())));

        @SuppressWarnings("unchecked")
        Map<String, Service> result = 
fluentTemplate.to("direct:start").request(Map.class);
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").to("consul:agent?action=" + 
ConsulAgentActions.SERVICES);
            }
        };
    }
}
{code}
Error:
{code:java}
org.apache.camel.FailedToStartRouteException: Failed to start route 
servicesRoute because of null
 at org.apache.camel.impl.engine.RouteService.warmUp(RouteService.java:123) 
...
Caused by: java.lang.NullPointerException at 
org.apache.camel.support.HeaderSelectorProducer.doBuild(HeaderSelectorProducer.java:150)
 at org.apache.camel.support.service.BaseService.build(BaseService.java:63) at 
org.apache.camel.support.service.ServiceHelper.buildService(ServiceHelper.java:55)
 at 
org.apache.camel.support.service.ServiceHelper.buildService(ServiceHelper.java:72)
 at 
org.apache.camel.processor.InterceptSendToEndpointProcessor.doBuild(InterceptSendToEndpointProcessor.java:151)
 at org.apache.camel.support.service.BaseService.build(BaseService.java:63) at 
org.apache.camel.support.service.BaseService.init(BaseService.java:79) at 
org.apache.camel.support.service.BaseService.start(BaseService.java:111) at 
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:113)
 at 
org.apache.camel.impl.engine.AbstractCamelContext.internalAddService(AbstractCamelContext.java:1465)
 at 
org.apache.camel.impl.engine.AbstractCamelContext.addService(AbstractCamelContext.java:1383)
 at org.apache.camel.processor.SendProcessor.doStart(SendProcessor.java:247) at 
org.apache.camel.support.service.BaseService.start(BaseService.java:119) at 
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:113)
 at 
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:130)
 at 
org.apache.camel.processor.errorhandler.RedeliveryErrorHandler.doStart(RedeliveryErrorHandler.java:1638)
 at 
org.apache.camel.support.ChildServiceSupport.start(ChildServiceSupport.java:60) 
... 92 more{code}
The reason is that {{camelContext}} is {{null}} in {{HeaderSelectorProducer}}, 
but I am still unsure why that is:
{code:java}
@Override
protected void doBuild() throws Exception {
    super.doBuild();

    String key = this.getClass().getName();
    String fqn = RESOURCE_PATH + key;
    // -------- camelContext is null here:
    strategy = 
camelContext.adapt(ExtendedCamelContext.class).getBootstrapFactoryFinder(RESOURCE_PATH)
            .newInstance(key, InvokeOnHeaderStrategy.class)
            .orElseThrow(() -> new IllegalArgumentException("Cannot find " + 
fqn + " in classpath.")); 
    ...{code}
Same with catalog:
{code:java}
 public class MockCatalogTest extends CamelTestSupport {

    @Test
    public void testMockCatalog() throws Exception {
        MockEndpoint mockConsulAgent = getMockEndpoint("mock:consul:catalog");

        AdviceWith.adviceWith(context, "servicesRoute", a -> {
            a.mockEndpointsAndSkip("consul:catalog*");
        });
        
mockConsulAgent.returnReplyBody(constant(singletonList(ImmutableNode.builder().node("node-1").build())));

        @SuppressWarnings("unchecked")
        Map<String, Service> result = 
fluentTemplate.to("direct:start").request(Map.class);
    }

    @Override
    protected RoutesBuilder createRouteBuilder() throws Exception {
        return new RouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("direct:start").to("consul:catalog?action=" + 
ConsulCatalogActions.LIST_NODES);
            }
        };
    }
}
{code}
Why this fails is not clear to me. Two instances of {{ConsulAgentProducer}} are 
being created. At runtime the method {{setCamelContext}} of the failing 
instance never gets called, although it is a {{CamelContextAware}}. I suspect 
that it is the mock or the real object behind the mock which is not a 
{{CamelContextAware}} so that it receives no {{camelContext}}. My assumption is 
that a mock is a kind of wrapper around the real thing, but I wasn't able to 
understand the relationship between the two enough to fix this. The instances 
look very similar, the failing instance appears not to be a Java proxy.

Anyone who understands the mocking mechanics better than me: what might go 
wrong here? Any hints how it *should* work? Is the mock a proxy or what exactly 
is the relationship between mock and mocked?

*UPDATE*

The failing instance is the delegate {{ConsulAgentProducer}} inside an 
{{InterceptSendToEndpointProcessor}}. The latter is not a 
{{CamelContextAware}}, hence it receives no {{camelContext.}} I see two 
different approaches:
 # The delegate inside the {{InterceptSendToEndpointProcessor}} could be made 
to receive a camelContext, e.g. by making the 
{{InterceptSendToEndpointProcess}} a {{CamelContextAware}} and setting the 
{{camelContext}} on the delegate
 # The method invocation {{HeaderSelectorProducer.doBuild()}} could be skipped 
somehow

Advice would be very much appreciated.

*UPDATE 2*

Turns out, it is not the {{delegate}} (ConsulEndpoint) member nor the 
{{endpoint}} (DefaultInterceptorSendToEndpoint) member inside 
InterceptSendToEndpointProcessor which has no CamelContext, but the 
{{producer}} (ConsulAgentProducer) member, which holds a second instance of the 
producer. Again, two approaches:
 # Let InterceptSendToEndpointProcessor set the camelContext on the producer 
when it receives the camelContext.
 # Find out why the second producer inside InterceptSendToEndpointProcessor 
does not receive a camelContext when it gets constructed

*UPDATE 3*

DefaultInterceptSendToEndpoint is responsible to create the producer in 
createAsyncProducer. One could call camelContext.addService(producer) there, in 
order to initialize the delegate producer like any other component. It does fix 
the problem, but I am unsure if the delegate producer should be added as a 
service or if it is the whole point not to add it.

_DefaultInterceptSendToEndpoint.java_
{code:java}
@Override
public AsyncProducer createAsyncProducer() throws Exception {
    AsyncProducer producer = delegate.createAsyncProducer();
    camelContext.addService(producer); // <-- insert this to initialize the 
producer with camelContext
    return 
camelContext.adapt(ExtendedCamelContext.class).getInternalProcessorFactory()
            .createInterceptSendToEndpointProcessor(this, delegate, producer, 
skip);
} {code}
What would you recommend?

 


> Mocking of consul producers fails
> ---------------------------------
>
>                 Key: CAMEL-16567
>                 URL: https://issues.apache.org/jira/browse/CAMEL-16567
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-consul
>    Affects Versions: 3.9.0
>            Reporter: Dietrich Schulten
>            Priority: Major
>
> It is not possible to mock consul producers, at least I have tried with the 
> catalog and agent producers. Test:
> {code:java}
> public class MockAgentTest extends CamelTestSupport {
>     @Test
>     public void testMockAgent() throws Exception {
>         MockEndpoint mockConsulAgent = getMockEndpoint("mock:consul:agent");
>         AdviceWith.adviceWith(context, "servicesRoute", a -> {
>             a.mockEndpointsAndSkip("consul:agent*");
>         });
>         mockConsulAgent.returnReplyBody(constant(ImmutableMap.of("foo-1", 
> ImmutableService.builder()
>             .id("foo-1")
>             .service("foo")
>             .address("localhost")
>             .port(80)
>             .build())));
>         @SuppressWarnings("unchecked")
>         Map<String, Service> result = 
> fluentTemplate.to("direct:start").request(Map.class);
>     }
>     @Override
>     protected RoutesBuilder createRouteBuilder() throws Exception {
>         return new RouteBuilder() {
>             @Override
>             public void configure() throws Exception {
>                 from("direct:start").to("consul:agent?action=" + 
> ConsulAgentActions.SERVICES);
>             }
>         };
>     }
> }
> {code}
> Error:
> {code:java}
> org.apache.camel.FailedToStartRouteException: Failed to start route 
> servicesRoute because of null
>  at org.apache.camel.impl.engine.RouteService.warmUp(RouteService.java:123) 
> ...
> Caused by: java.lang.NullPointerException at 
> org.apache.camel.support.HeaderSelectorProducer.doBuild(HeaderSelectorProducer.java:150)
>  at org.apache.camel.support.service.BaseService.build(BaseService.java:63) 
> at 
> org.apache.camel.support.service.ServiceHelper.buildService(ServiceHelper.java:55)
>  at 
> org.apache.camel.support.service.ServiceHelper.buildService(ServiceHelper.java:72)
>  at 
> org.apache.camel.processor.InterceptSendToEndpointProcessor.doBuild(InterceptSendToEndpointProcessor.java:151)
>  at org.apache.camel.support.service.BaseService.build(BaseService.java:63) 
> at org.apache.camel.support.service.BaseService.init(BaseService.java:79) at 
> org.apache.camel.support.service.BaseService.start(BaseService.java:111) at 
> org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:113)
>  at 
> org.apache.camel.impl.engine.AbstractCamelContext.internalAddService(AbstractCamelContext.java:1465)
>  at 
> org.apache.camel.impl.engine.AbstractCamelContext.addService(AbstractCamelContext.java:1383)
>  at org.apache.camel.processor.SendProcessor.doStart(SendProcessor.java:247) 
> at org.apache.camel.support.service.BaseService.start(BaseService.java:119) 
> at 
> org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:113)
>  at 
> org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:130)
>  at 
> org.apache.camel.processor.errorhandler.RedeliveryErrorHandler.doStart(RedeliveryErrorHandler.java:1638)
>  at 
> org.apache.camel.support.ChildServiceSupport.start(ChildServiceSupport.java:60)
>  ... 92 more{code}
> The reason is that {{camelContext}} is {{null}} in 
> {{HeaderSelectorProducer}}, but I am still unsure why that is:
> {code:java}
> @Override
> protected void doBuild() throws Exception {
>     super.doBuild();
>     String key = this.getClass().getName();
>     String fqn = RESOURCE_PATH + key;
>     // -------- camelContext is null here:
>     strategy = 
> camelContext.adapt(ExtendedCamelContext.class).getBootstrapFactoryFinder(RESOURCE_PATH)
>             .newInstance(key, InvokeOnHeaderStrategy.class)
>             .orElseThrow(() -> new IllegalArgumentException("Cannot find " + 
> fqn + " in classpath.")); 
>     ...{code}
> Same with catalog:
> {code:java}
>  public class MockCatalogTest extends CamelTestSupport {
>     @Test
>     public void testMockCatalog() throws Exception {
>         MockEndpoint mockConsulAgent = getMockEndpoint("mock:consul:catalog");
>         AdviceWith.adviceWith(context, "servicesRoute", a -> {
>             a.mockEndpointsAndSkip("consul:catalog*");
>         });
>         
> mockConsulAgent.returnReplyBody(constant(singletonList(ImmutableNode.builder().node("node-1").build())));
>         @SuppressWarnings("unchecked")
>         Map<String, Service> result = 
> fluentTemplate.to("direct:start").request(Map.class);
>     }
>     @Override
>     protected RoutesBuilder createRouteBuilder() throws Exception {
>         return new RouteBuilder() {
>             @Override
>             public void configure() throws Exception {
>                 from("direct:start").to("consul:catalog?action=" + 
> ConsulCatalogActions.LIST_NODES);
>             }
>         };
>     }
> }
> {code}
> Why this fails is not clear to me. Two instances of {{ConsulAgentProducer}} 
> are being created. At runtime the method {{setCamelContext}} of the failing 
> instance never gets called, although it is a {{CamelContextAware}}. I suspect 
> that it is the mock or the real object behind the mock which is not a 
> {{CamelContextAware}} so that it receives no {{camelContext}}. My assumption 
> is that a mock is a kind of wrapper around the real thing, but I wasn't able 
> to understand the relationship between the two enough to fix this. The 
> instances look very similar, the failing instance appears not to be a Java 
> proxy.
> Anyone who understands the mocking mechanics better than me: what might go 
> wrong here? Any hints how it *should* work? Is the mock a proxy or what 
> exactly is the relationship between mock and mocked?
> *UPDATE*
> The failing instance is the delegate {{ConsulAgentProducer}} inside an 
> {{InterceptSendToEndpointProcessor}}. The latter is not a 
> {{CamelContextAware}}, hence it receives no {{camelContext.}} I see two 
> different approaches:
>  # The delegate inside the {{InterceptSendToEndpointProcessor}} could be made 
> to receive a camelContext, e.g. by making the 
> {{InterceptSendToEndpointProcess}} a {{CamelContextAware}} and setting the 
> {{camelContext}} on the delegate
>  # The method invocation {{HeaderSelectorProducer.doBuild()}} could be 
> skipped somehow
> Advice would be very much appreciated.
> *UPDATE 2*
> Turns out, it is not the {{delegate}} (ConsulEndpoint) member nor the 
> {{endpoint}} (DefaultInterceptorSendToEndpoint) member inside 
> InterceptSendToEndpointProcessor which has no CamelContext, but the 
> {{producer}} (ConsulAgentProducer) member, which holds a second instance of 
> the producer. Again, two approaches:
>  # Let InterceptSendToEndpointProcessor set the camelContext on the producer 
> when it receives the camelContext.
>  # Find out why the second producer inside InterceptSendToEndpointProcessor 
> does not receive a camelContext when it gets constructed
> *UPDATE 3*
> DefaultInterceptSendToEndpoint is responsible to create the producer in 
> createAsyncProducer. One could call camelContext.addService(producer) there, 
> in order to initialize the delegate producer like any other component. It 
> does fix the problem, but I am unsure if the delegate producer should be 
> added as a service or if it is the whole point not to add it.
> _DefaultInterceptSendToEndpoint.java_
> {code:java}
> @Override
> public AsyncProducer createAsyncProducer() throws Exception {
>     AsyncProducer producer = delegate.createAsyncProducer();
>     camelContext.addService(producer); // <-- insert this to initialize the 
> producer with camelContext
>     return 
> camelContext.adapt(ExtendedCamelContext.class).getInternalProcessorFactory()
>             .createInterceptSendToEndpointProcessor(this, delegate, producer, 
> skip);
> } {code}
> The alternative would still be to make {{InterceptSendToEndpointProcessor}} a 
> {{CamelContextAware}} and if its {{producer}} is a {{CamelContextAware}} in 
> turn, set the camelContext on the {{producer}}, hold it in the 
> {{InterceptSendToEndpointProcessor}} and get it from there in 
> {{getCamelContext}}. Somehow this feels less intrusive than adding the 
> producer as a service.
> What would you recommend?
>  



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to