Issue with Camel custom #Converter using Spring DSL - apache-camel

I Just want to learn custom converter and ran into issue. Any help is much appreciated. Camel Version 2.17 and JBoss Fuse 6.3
#Converter
public class MyConvertor{
public MyConvertor(){}
#Converter
public static String convertTo(Exchange exchange) {}
}
In My Spring DSL
<convertBodyTo charset="UTF-8" id="_convertBodyTo1" type="com.x.convertor.MyConvertor"/>
In META-INF/services/org/apache/camel/TypeConverter
com.x.convertor.MyConvertor
Error message :
org.apache.camel.InvalidPayloadException: No body available of type: com.x.convertor.MyConvertor but has value: GenericFile[output.txt] of type: org.apache.camel.component.file.GenericFile on: output.txt. Caused by: No type converter available to convert from type:
at org.apache.camel.processor.Pipeline.process(Pipeline.java:121)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:83)
Caused by: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: org.apache.camel.component.file.GenericFile to the required type: com.x.convertor.MyConvertor

There are a couple of errors. The type attribute should be your target type (the type you want after conversion).
<convertBodyTo charset="UTF-8" id="_convertBodyTo1" type="java.lang.String"/>
Camel can do this conversion automatically. If you want to write a converter on your own, mind that converter method should have the expected input type as parameter, and not the Exchange (which can be an optional second parameter from Camel 2.16 onwards).
Class should be something like:
#Converter
public class MyConvertor{
public MyConvertor(){}
#Converter
public static String convertTo(GenericFile body, Exchange exchange) {
// The exchange parameter is optional
}
}
See https://camel.apache.org/type-converter.html
If you want to read the content of a CSV file to transform it into a POJO, use Bindy component.

Related

Setting a custom processStrategy in the Spring Camel file component throws Could not find a suitable setter for property: processStrategy

I'm trying to set a custom processStrategy in Camel 2.23.2. I've tried several ways to reference it from the processStrategy uri parameter, but I always get this exception:
Caused by: java.lang.IllegalArgumentException: Could not find a suitable setter for property: processStrategy as there isn't a setter method with same type: java.lang.String nor type conversion possible: No type converter available to convert from type: java.lang.String to the required type: org.apache.camel.component.file.GenericFileProcessStrategy with value #genericFileNoOpProcessStrategy"
Route definition
<bean id="genericFileNoOpProcessStrategy" class="org.apache.camel.component.file.strategy.GenericFileNoOpProcessStrategy"/>
<routeContext id="routes" xmlns="http://camel.apache.org/schema/spring">
<route id="route">
<from uri="seda:someEvent"/>
<pollEnrich>
<constant>file:/folder?fileName=file.csv&processStrategy=#genericFileNoOpProcessStrategy"</constant>
</pollEnrich>
What's the right way to reference it?
This would've been working if it weren't a user error (notice the quote "):
<constant>file:/folder?fileName=file.csv&processStrategy=#genericFileNoOpProcessStrategy"</constant>
Fixed with:
<constant>file:/folder?fileName=file.csv&processStrategy=#genericFileNoOpProcessStrategy"</constant>

No consumers available on endpoint: Endpoint[direct://LookUpRoute]

I'm new to Apache Camel. I'm trying to send an exchange from a java method to a route but it gives me "Caused by: org.apache.camel.component.direct.DirectConsumerNotAvailableException: No consumers available on endpoint" error. I want to understand what exactly this error is and when do we get this?
#EndpointInject(uri = "direct:reportRoute")
private ProducerTemplate templatereportRoute;
public void saveDataFromExchange(Map<String, Object> DataMap){
List<Map<String, Object>> paramList = new ArrayList<Map<String, Object>>();
List<Map<String, Object>> rows = templatereportRoute.requestBody("direct:reportReport", DataMap, List.class);
<from uri="direct:reportRoute"/>
<log message=" - ${body}" loggingLevel="INFO"/>
<setProperty propertyName="DataMap">
<simple>${body}</simple>
</setProperty>
The error you encounter means that you are sending to a direct endpoint that does not exist in the Camel Context.
Since you posted an XML fragment that defines the route in question there are two possible problems (as already commented by #claus-ibsen):
The XML you posted is not in use. You are starting a Camel Context but it does not use your XML code. Are you using Spring? Then you can define your Camel routes in Spring XML.
Your setup is fine but your Java code sends the message too early, i.e. before the direct endpoint is up and running. You can put this code in a Test class and run it after the Camel context is started and ready.
Try put in public class from routerBuilder implemention the anotation #Component from Spring context
Ex:
#Component //<<<<---- This
public class RouterClass extends RouteBuilder {
#Override
public void configure() throws Exception {
}
}//class closure

Passing parameter to bean's method in camel blueprint

I'm trying to call bean's public method by passing parameter .But i'm unable to run it.Here is my sample blueprint code(I have written from clause but here i'm pasting only necessary code)-
<bean id="ProcessorRef" class="com.healthedge.customer.THC.extractor.ProcessorClass">
<to uri="bean:ProcessorRef" />
Processor class-
public class ProcessorClass{
public String whatAmI(String str) {
return "I am "+str;
}
}
Now in above example how can I invoke whatAmI method with parameter from blueprint?TIA
You can do it by calling directly the method you want
<bean id="ProcessorRef" class="com.healthedge.customer.THC.extractor.ProcessorClass">
<bean ref="ProcessorRef" method="whatAmI('your_parameter_here')" />
If the value you want to pass to whatAmI method is in header, you can do like this.
<to uri="bean:ProcessorRef?method=whatAmI(${header.xyz})" />
If its a constant string, you can put the string directly in place of ${header.xyz}
Another option is to modify your whatAmI method to
public class ProcessorClass{
public String whatAmI(Exchange exchange) {
// exchange has many methods, with which you can access headers and body.
}
}
In such a case you can write the route like this
<to uri="bean:ProcessorRef?method=whatAmI" />
Personally, I would prefer second option, because it gives you access to complete exchange object, which would have all the details.

Camel JAXB marshalling return XML object type?

I am sending a Java object to producer endpoint and waiting for the marshalled XML object. I tried changing it to Node object/ File object but it is giving ClassCastException.
So took the xmlObj in an object class type. What should be the correct class to capture the response?
public class ClientEight {
#Produce(uri = "direct:invoice")
ProducerTemplate template;
public static void main(String args[]) throws InterruptedException {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("resources/camel-configTen.xml");
InvoiceXml invoice = new InvoiceXml("fdf3443", 3454, 435345.44 f, "hfhfddfdg"); //any java object we are passing
ClientEight client = (ClientEight) ctx.getBean("client");
Object xmlObj = client.template.requestBody(invoice);
System.out.println(xmlObj);
}
}
Above is a client code which you are using to send the Java object to a producer endpoint and since you are using template.requestBody, you are getting back the object returned.
<camel:camelContext>
<camel:dataFormats>
<!-- path to jaxb annotated class -->
<camel:jaxb id="invoiceJaxb" contextPath="com.java.bean"
prettyPrint="true" />
</camel:dataFormats>
<camel:route>
<camel:from uri="direct:invoice" />
<camel:marshal ref="invoiceJaxb" />
<camel:log message=" ${body}" />
<camel:to uri="file://src/resources?fileName=One.xml"/>
</camel:route>
</camel:camelContext>
the unmarshall processor return a stream, not a single object. In camel, more generally, if you want a specific type, you shouldn't get directly a body as an object, but use the various methods to convert the body into that type.
Try :
Document document = client.template.requestBody(invoice, org.w3c.dom.Document.class);

Error in custom repository interface

I'm trying to set up my first Java application using Spring Data for MongoDB in a multi-module Maven 3 project. Here are the relevant versions:
Java 7
mongodb-win32-x86_64-2.2.0
Spring Data 1.1.1.RELEASE
Spring 3.2.0.RELEASE
I'm getting the following runtime error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'actorFacade': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private es.mi.casetools.praetor.persistence.springdata.repositories.ActorRepository es.mi.casetools.praetor.facade.impl.DefaultActorFacade.actorRepository; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'actorRepository': FactoryBean threw exception on object creation; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property insert found for type void
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:609)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:106)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:57)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:248)
at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
... 30 more
Searching in google I found people with the same issue and it seems to be related with custom repositories.
Here is the entity I want to store as a mongo document.
public class Actor {
public enum ActorStereotype {
SYSTEM,
PERSON
}
private String id;
private String code; // unique
private ActorStereotype stereotype;
private String title; // Short title for the actor
private String description;
private String projectId; // project this actor belongs to
// getters & setters
The standard repository interface.
public interface ActorRepository extends MongoRepository<Actor, String>, ActorRepositoryCustom {
}
The custom interface (where I think the error lives).
#NoRepositoryBean
public interface ActorRepositoryCustom {
void updateSingleActor(Actor actor);
void insertActor(Actor actor);
}
The custom interface implementation.
public class ActorRepositoryCustomImpl implements ActorRepositoryCustom {
#Autowired
private MongoTemplate mongoTemplate;
#Override
public void updateSingleActor(Actor actor) {
if(actor.getId() != null)
throw new MissingIdException();
// TODO change to Spring Converter
DBObject dbo = (DBObject)mongoTemplate.getConverter().convertToMongoType(actor);
mongoTemplate.updateFirst(query(where("_id").is(actor.getId())),
Update.fromDBObject(dbo, new String[]{}),
Actor.class);
}
#Override
public void insertActor(Actor actor) {
if(actor.getId() != null)
throw new IdProvidedException();
mongoTemplate.save(actor);
}
}
And finally, the application context.
<context:annotation-config/>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:properties/test.properties</value>
</list>
</property>
</bean>
<!-- mongodb configuration -->
<mongo:repositories base-package="es.mi.casetools.praetor.persistence.springdata.repositories"
mongo-template-ref="mongoTemplate" repository-impl-postfix="Impl">
<repository:exclude-filter type="annotation" expression="org.springframework.data.repository.NoRepositoryBean"/>
</mongo:repositories>
<mongo:mongo id="mongotest" host="${mongo.host}" port="${mongo.port}" write-concern="SAFE">
</mongo:mongo>
<mongo:db-factory dbname="${mongo.dbname}" mongo-ref="mongotest"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
</bean>
<bean id="actorFacade" class="es.mi.casetools.praetor.facade.impl.DefaultActorFacade">
</bean>
</beans>
I also have a little Spring test that fails loading the above application context giving the exception I listed near the top.
I tried adding the following but I get the same exception.
<bean id="actorRepositoryCustomImpl" class="es.mi.casetools.praetor.persistence.springdata.repositories.ActorRepositoryCustomImpl"></bean>
Has someone got a clue of what the error may be?
Miguel's comment solved the problem. I had given the wrong name to the implementing class. I've seen similar questions around so I'll try to clarify the solution in the hope that it helps someone else.
I have the following interface definition
public interface ActorRepository extends MongoRepository<Actor, String>, ActorRepositoryCustom
The custom interface definition looks like this:
public interface ActorRepositoryCustom
So my error was naming the implementation of ActorRepositoryCustom with ActorRepositoryCustomImpl, expecting Springdata would pick up the implementation as its postfix is the default one Impl. The thing is, Springdata looks for ActorRepositoryImpl by default, even though what you are implementing is ActorRepositoryCustom. The solution is using the optional attribute repository-impl-postfix and set it to CustomImpl.

Resources