I'm currently setting up tests with CamelBlueprintTestSupport.
In my blueprint.xml, I tried the following implementation:
<reference id="foo" interface="com.myexample.FooInterface" />
Mocking FooInterface in my test class with
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
FooInterface foo = Mockito.mock(FooImpl.class);
services.put(FooInterface.class.getCanonicalName(), asService(foo, null));
super.addServicesOnStartup(services);
}
works fine (where FooImpl implements FooInterface, of course), e.g. executing the test (the test simply contains assert(true) cause I'd only like to check the test configuration)
ends up positive.
But in my real implementation, I do not have an interface as service. Instead, it is a class referenced this way:
<bean class=com.myexample.FooBar" id="foobar">
<property name="foo" ref="foo" />
</bean>
<bean class="com.myexample.FooImpl" id="foo"/>
<reference id="fooBarReference"
component-name="foobar"
interface="com.myexample.FooImpl" ext:proxy-method="classes" />
My mock configuration in the test is this one:
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
FooImpl foo = Mockito.mock(FooImpl.class);
services.put(FooImpl.class.getCanonicalName(), asService(foo, null));
super.addServicesOnStartup(services);
}
Executing the test now fails with the following exception:
java.lang.RuntimeException: Gave up waiting for BlueprintContainer
from bundle "FooTest"
at com.myexample.test.FooTest.setUp(FooTest.java:49)
I cannot see what's actually wrong. By the way, the implementation of the camel route on Karaf runs without any problems. I don't know if my test set up is wrong or if it's a bug
in the CamelBlueprintTestSupport.
Finally, there is a solution:
Properties dictionary = new Properties();
dictionary.setProperty("osgi.service.blueprint.compname","foobar");
services.put(FooImpl.class.getName(), asService(new FooImpl(), dictionary));
CU, iE
Related
I have the following scenario:
I have an OSGI bundle that has a service reference defined in the blueprint XML that references an interface in a remote bundle, and a bean that uses one of the impl's methods to populate a Properties object.
Relevant snippet from Bundle #1's XML (the consumer):
...
<!-- other bean definitions, namespace stuff, etc -->
<!-- reference to the fetching service -->
<reference id="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- bean to hold the actual Properties object: the getConfigProperties method is one of the overridden interface methods -->
<bean id="fetchedProperties" class="java.util.Properties" factory-ref="fetchingService" factory-method="getProperties" />
<camelContext id="contextThatNeedsProperties" xmlns="http://camel.apache.org/schema/blueprint">
<propertyPlaceholder id="properties" location="ref:fetchedProperties" />
...
<!-- the rest of the context stuff - routes and so on -->
</camelContext>
Remote Bundle's blueprint.xml:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<cm:property-placeholder id="config-properties" persistent-id="company.path.configfetcher" />
<bean id="fetchingService" class="company.path.to.fetching.bundle.impl.FetchingServiceImpl" scope="singleton" init-method="createLoader" depends-on="config-properties">
<property name="environment" value="${environment}" />
<property name="pathToRestService" value="${restPath}" />
</bean>
<service ref="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- END TESTING -->
</blueprint>
From Impl Class:
public synchronized Properties getProperties() {
if(!IS_RUNNING) {
// timer task that regularly calls the REST api to check for updates
timer.schedule(updateTimerTask, 0, pollInterval);
IS_RUNNING = true;
}
//Map<String, Properties> to return matching object if it's there
if(PROPERTIES_BY_KEY.containsKey(environment)) {
return PROPERTIES_BY_KEY.get(environment);
}
/* if nothing, return an empty Properties object - if this is the case, then whatever bundle is relying on these
* properties is going to fail and we'll see it in the logs
*/
return new Properties();
}
The issue:
I have a test class (extending CamelBlueprintTestSupport) and there are a lot of moving parts such that I can't really change the order of things. Unfortunately, the properties bean method gets called before the CamelContext is started. Not that big a deal because in the test environment there is no config file to read the necessary properties from so the retrieval fails and we get back an empty properties object [note: we're overriding the properties component with fakes since it's not that class being tested], but in a perfect world, I'd like to be able to do two things:
1) replace the service with a new Impl()
2) intercept calls to the getProperties method OR tie the bean to the new service so that the calls return the properties from the fake impl
Thoughts?
Edit #1:
Here's one of the things I'm doing as a workaround right now:
try {
ServiceReference sr = this.getBundleContext().getServiceReference(FetchingService.class);
if(sr != null) {
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setEnvironment(env);
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setPath(path);
}
} catch(Exception e) {
log.error("Error getting Fetching service: {}", e.getMessage());
}
The biggest problem here is that I have to wait until the createCamelContext is called for a BundleContext to exist; therefore, the getProperties call has already happened once. As I said, since in the testing environment no config for the FetchingService class exists to provide the environment and path strings, that first call will fail (resulting in an empty Properties object). The second time around, the code above has set the properties in the impl class and we're off to the races. This is not a question about something that isn't working. Rather, it is about a better, more elegant solution that can be applied in other scenarios.
Oh, and for clarification before anyone asks, the point of this service is so that we don't have to have a .cfg file for every OSGI bundle deployed to our Servicemix instance - this central service will go and fetch the configs that the other bundles need and the only .cfg file that need exist is for the Fetcher.
Other pertinent details:
Camel 2.13.2 - wish it was 2.14 because they've added more property-placeholder tools to that version that would probably make this easier
Servicemix - 5.3.1
Have you tried overriding CamelBlueprintTestSupport's addServicesOnStartup in your test (see "Adding services on startup" http://camel.apache.org/blueprint-testing.html)?
In your case something like:
#Override
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
services.put(FetchingService.class.getName(), asService(new FetchServiceImpl(), null));
}
Of some reason, QUEUE_A does not ALWAYS has 1 exchanges - sometimes it has 0, unless I add a Thread.sleep(100) to the test. I guess whenCompleted/whenDone isn't completely done when it actually says it's done. How can I verify that it is completely done?
multicast().parallelProcessing().to(QUEUE_A, QUEUE_B, QUEUE_C, QUEUE_D)
And testing with:
#Test
public void test() {
NotifyBuilder notify = new NotifyBuilder(context)
.from(QUEUE_INCOMING)
.whenCompleted(1)
.create();
template.sendBody(QUEUE_INCOMING, streamToString(loadResourceAsStream("/data/TestData.xml")));
boolean matches = notify.matches(4, SECONDS);
assertTrue("Notify failed", matches);
Thread.sleep(100); //Without this, it fails
verifyEndpoints(1, context, QUEUE_A, QUEUE_B, QUEUE_C, QUEUE_D);
}
public static void verifyEndpoints(int expectedSize, ModelCamelContext context, String... endpoints) {
for (String endpoint : endpoints) {
BrowsableEndpoint be = context.getEndpoint(endpoint, BrowsableEndpoint.class);
assertThat(String.format("Endpoint exchanges '%s' has wrong size", endpoint), be.getExchanges(), hasSize(expectedSize));
}
}
And the endpoint bean, using ActiveMQ when testing, but is going to use WebSphere MQ in prod:
<bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false" />
</bean>
</property>
</bean>
The problem is that you browse a WMQ asap after sending a message to it, and therefore depending on broker implementation and timing etc, you may not see the very last messages, when using the JMS browsing api.
And hence why it seems to fix when you wait a bit with the sleep.
I have a camel 'seda' route that contains code roughly:
JaxbDataFormat jaxb = new JaxbDataFormat(false);
jaxb.setContextPath("com.example.data.api");
from("seda:validate")
.marshal(jaxb)
.to("spring-ws:" + getDataServiceURL())
.unmarshal(jaxb)
I send an object from com.example.data.api, the JaxbDataFormat formatter sets it up as a SOAP request and passes it along wo spring-ws to actually send to my service. This works like a charm most of the time.
I say "most" because every now and then, spring-ws throws an exception like so:
org.springframework.ws.client.WebServiceTransformerException: Transformation error: Can't transform a Source of type javax.xml.transform.stax.StAXSource; nested exception is javax.xml.transform.TransformerException: Can't transform a Source of type javax.xml.transform.stax.StAXSource
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:608)
at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:537)
at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:492)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceive(WebServiceTemplate.java:479)
at org.springframework.ws.client.core.WebServiceTemplate.sendSourceAndReceive(WebServiceTemplate.java:470)
at org.apache.camel.component.spring.ws.SpringWebserviceProducer.process(SpringWebserviceProducer.java:81)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:73)
The core of that is this error message: "Can't transform a Source of type javax.xml.transform.stax.StAXSource".
None of that makes sense. The Jaxb marshalling will have already made sure that the object in question is an XML string (according to any debug logging I do). Furthermore, this exact code works most of the time and only occasionally will fail. It appears random.
For instance, I ran a test just a few minutes ago where I sent a message into my route and got this error. I then restarted my service and resend the exact same message... and it worked like a charm. Same code; same environment; same test -- two different results.
It's this randomness that makes this so maddening. Any ideas what I should be looking for to making sure this never happens?
The issue is not with Camel but Spring-WS. Modifying the transformerFactoryClass in WS template config would work
<bean id="baseCamelMarshallerWSTemplate" class="org.springframework.ws.client.core.WebServiceTemplate" scope="prototype">
<constructor-arg ref="messageFactory" />
<property name="messageSender">
<ref bean="httpSender"/>
</property>
<property name="checkConnectionForError" value="true"/>
**<property name="transformerFactoryClass" value="com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"/>**
</bean>
If you still face the issue, please share spring WS config and a test case
I have a service that has several methods marked with #Transactional, including methods a, b and c. These 3 methods are nested in the following way a -> b -> c. Here is a code sample:
#Transactional
public void a() {
while(condition) {
try {
b();
} catch(MyException e) {
logger.warn(e.getMessage());
}
}
}
Method b however is annotated like this:
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = {MyException.class})
However, when MyException is thrown from method c, the exception is caught at method a and when that exits, the transaction is comitted and that includes whatever has been done by call to b() that threw the exception and should have been rolled back(?).
I am using sql server 2012 express with spring 3.0.7 and my spring configuration is like this:
<tx:annotation-driven />
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<qualifier value="txm1"/>
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/jpa-persistence.xml"/>
<property name="persistenceUnitName" value="Unit1" />
</bean>
Method b seems to be in the same class as method a. If you're not using AspectJ, the #Transactional-annotations are handled by a JDK dynamic proxy, through which your calls from outside the class travel to method a. The call needs to travel through the proxy for the #Transactional-annotations to have effect, see for example here, under 'Understanding AOP proxies': http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch08s06.html
The key thing to understand here is that the client code inside the
main(..) of the Main class has a reference to the proxy. This means
that method calls on that object reference will be calls on the proxy,
and as such the proxy will be able to delegate to all of the
interceptors (advice) that are relevant to that particular method
call. However, once the call has finally reached the target object,
the SimplePojo reference in this case, any method calls that it may
make on itself, such as this.bar() or this.foo(), are going to be
invoked against the this reference, and not the proxy. This has
important implications. It means that self-invocation is not going to
result in the advice associated with a method invocation getting a
chance to execute.
We are trying with ServiceA calling ServiceB as soon as bundle loads during the SericeMix startup. Service2 having activemq endpoints we need to invoke a method of that particular service. I tried by spring init-method attribute in the bean tag which helps in auto trigger a method in ServiceA in that method I am calling the serviceB’s method. I am getting Exception like No Consumer available for the endpoint. I assume that as soon as the Service1 is up it is not getting the instance of the service2 which needs to get initialized using #Produce annotation activemq endpoint. The same services work fine in the other normal scenarios.
Exception:
Caused by: org.apache.camel.CamelExchangeException: No consumers available on endpoint: Endpoint[direct://ServiceB]. Exchange[Message: BeanInvocation public java.lang.String java.lang.Object.toString() with null]]
at org.apache.camel.component.direct.DirectProducer.process(DirectProducer.java:46)
at org.apache.camel.component.bean.CamelInvocationHandler.invoke(CamelInvocationHandler.java:64)
... 35 more
I am copy pasting the Code Block for your reference.
public class ServiceA{
#Produce(uri = "direct:ServiceB") //Active MQ endpoint
private ServiceB serviceB;
public void start()
{
Object obj = serviceB.getData(); }
. . .
.....
}
}
**bundle-context.xml**
//Changes for method to auto trigger during spring bean load
<bean id="serviceA" class="com.test.serviceA" init-method="start">
</bean>
**bundle-context-camel.xml**
<osgi:camelContext id="ServiceA"
xmlns="http://camel.apache.org/schema/spring">
<template id="producerTemplate" />
<!-- These routes are outbound to other services -->
<route>
<from uri="ServiceB" />
<bean ref="enrichOutboundExchangeRef" />
<to uri="activemq:ServiceB?transferException=true" />
</route>
..............
</osgi:camelContext>
Or is their anyother way if i need to achieve this requirement? where i can load a service(consumes other services) automatically during the servicemix bootup.
You can use seda instead of direct as it's queue based and thus consumers can come and go.
Also try using springs depends-on attribute
<bean id="serviceA" depends-on="myCamel" .../>
<osgi:camelContext id="myCamel" ...>
We tried the above approach but we still we are getting exceptions, we resolved it by adding a listener to the onCamelContextStarted() during th init of the serviceA.
Thanks
Ravi
If you are getting "no consumers available on endpoint", it means that messages are being routed to an endpoint that hasn't been initialized. I recommend decoupling the services using a JMS queue between them. That way serviceA can put messages in the queue (independent of serviceB's availability) and serviceB can then act as a polling consumer against that queue whenever its ready.