Camel is removing content of file after move it - spring-boot

I have a spring boot application with camel and I try to move a file from a server using FTP to my local machine, my problem is that the file is empty after move it,
This is my class that extends the RouteBuilder class:
public class Controlador extends RouteBuilder{
#Autowired
private ArchivoBS archivoBS;
#Override
public void configure() throws Exception {
from("ftp://user#host:21?password=pass&passiveMode=true&delete=true").convertBodyTo(InputStream.class).process(archivoBS).to("file:C:\\Users\\juan.gaytan\\Desktop\\prueba2");
}
}
My class ArchivoBS implements the Processor interface:
#Service
public class ArchivoBS implements Processor{
#Override
public void process(Exchange exchange) {
InputStream fis = exchange.getIn().getBody(InputStream.class);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String strLinea;
while ((strLinea = br.readLine()) != null) {
}
}
}
Can someone help me to solve my issue?,
Thanks in advance.

Thanks to #ClausIbsen that provided this link http://camel.apache.org/why-is-my-message-body-empty.html, I changed configure method and now looks like this:
#Override
public void configure() throws Exception {
from("ftp://user#host:21?password=pass&passiveMode=true&delete=true").streamCaching().convertBodyTo(InputStream.class).process(archivoBS).to("file:C:\\Users\\juan.gaytan\\Desktop\\prueba2");
}
I only needed to add the .streamCaching() method and it worked perfectly, hope this help someone else.

Related

How to access properties in BeanFactoryPostProcessor with spring boot 1.5.x

I am trying to get properties from application.yml in BeanFactoryPostProcessor with spring boot 1.5.x:
The application.yml:
prong:
nfcloan:
jackson:
json-sub-types-package:
- com.shuweicloud.starter.acc.dto.request
#ConfigurationProperties(prefix = "prong.nfcloan.jackson")
public class JacksonProperties {
private List<String> jsonSubTypesPackage;
public List<String> getJsonSubTypesPackage() {
return jsonSubTypesPackage;
}
public void setJsonSubTypesPackage(List<String> jsonSubTypesPackage) {
this.jsonSubTypesPackage = jsonSubTypesPackage;
}
}
#Component
public class AccBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
#Autowired
private JacksonProperties jacksonProperties;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
List<String> packages = jacksonProperties.getJsonSubTypesPackage();
// do something
}
}
The main class:
#SpringBootApplication
#EnableConfigurationProperties({JacksonProperties.class})
public class AccountingApplication {
public static void main(String[] args) {
SpringApplication.run(AccountingApplication.class, args);
}
}
But the packages variable is null. How to solve it?
I found a solution:
#Component
public class AccBeanFactoryPostProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
#SuppressWarnings("unchecked")
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
List<String> packages = environment.getProperty("prong.nfcloan.jackson.json-sub-types-package", List.class);
// do something
}
}
Spring boot internally uses Binder APIs to "map" the resolved properties into the #ConfigurationProperties beans.
Indeed, this resolution happens during the spring boot startup process after the BeanFactoryPostProcessors get created.
Now your solution will clearly work, because you kind of "bypass" this resolution.
However if you want to still have the Configuration as an instance of JacksonProperties (might be relevant if you have a lot of properties to resolve, or in general prefer to work more in a more spring-ish manner), you can use this binder API:
// inside the "postProcessBeanFactory" method, using the injected environment
BindResult<ExampleProperties> bindResult = Binder.get(environment)
.bind("prong.nfcloan.jackson", JacksonProperties.class);
JacksonProperties properties = bindResult.get();

Apache Camel - SedaEndpoint

I'm trying to send a message to an async route but it's not working.
I have just created a projeto on github to simulate the problem
#SpringBootApplication
public class SedaQueueApplication implements CommandLineRunner {
#Autowired
#EndpointInject(uri = "direct://direct-queue")
ProducerTemplate producerTemplate;
public static void main(String[] args) {
SpringApplication.run(SedaQueueApplication.class, args);
}
#Override
public void run(String... strings) throws Exception {
producerTemplate.sendBody("Teste Direct - Async");
}
#Component
class Router extends RouteBuilder {
#Override
public void configure() throws Exception {
from("direct://direct-queue").routeId("toAsync").to("seda://async-queue?size=100");
from("seda://async-queue").routeId("toLog").log("${body}");
}
}
you have two routes. In one of the route you have specified seda://async-queue and in other seda://async-queue?size=100 make this consistent i.e. add size attribute to first route or remove from second. It will work like a peach.
The reason for this is (Not sure if it is a bug in camel code), In SedaComponent::getOrCreateQueue they are comparing for size attribute also. Hence you get an exception if the size attribute if present and doeśn't match.
Hope that helps.

Using test application.properties file with CamelSpringTestSupport in Spring Boot

Prerequisites
Apache Tomcat 7
Spring 4.1.5.RELEASE
Spring Boot 1.2.2.RELEASE
Apache Camel 2.15.1
Problem
I am Using Spring Boot with a configuration class which is also used by EndpointSetup.
#SpringBootApplication
#Import({MyConfiguration.class, EndpointSetup.class})
public class MyFatJarRouter extends FatJarRouter { ... }
#Configuration
#ConfigurationProperties(prefix = "camel.route", ignoreUnknownFields = false)
public class MyConfiguration {
private List<String> brokerUrl = new ArrayList<>();
public List<String> getBrokerUrl() {return brokerUrl;}
public void setBrokerUrl(List<String> brokerUrl) {this.brokerUrl = brokerUrl;}
}
In production properties will be read from conf/application.properties by default.
I want to test my routes via CamelSpringTestSupport
So I have tried following:
I have placed a application.properties under test/resources/config/application.properties (--> in classpath of test)
then wrote following:
public class MyJmsTest extends CamelSpringTestSupport {
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(MyFatJarRouter.class);
}
#Test
public void myTest() throws Exception {
...
}
}
In the example above the configuration is not read from the application.properties placed in test folder.
How can I read a test specific config file in my CamelSpringTestSupport Unit-Test?
I may be little late in answering, but there is a better way than hacking endpoints. The following solution uses toD introduced in Camel 2.16. I wrote a custom component "github" (there's an official one as well), and the following is how I test it. Note that I'm not using a single Camel proprietary annotation. To inject properties, I can either use the properties attribute in #SpringBootTest, or any of the other standard techniques available in Spring Boot.
Note that I'm using $simple{...} to avoid clash with Spring property resolution.
<rant>
And yes, Camel documentation sucks! They write it like release notes, with a section dedicated to each release, and don't seem to update the doc to keep up with the latest versions (the following technique is not documented). Imagine going to a restaurant and asking for the special, only to be told by the server about the special for the day before, and the week before, and so on. How about versioning the doc instead?
</rant>
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = AFTER_EACH_TEST_METHOD)
public class GitHubRouteTest {
#Autowired
private CamelContext camelContext;
#Autowired
private ProducerTemplate template;
#Autowired
private GitHubClient gitHubClient;
#Test
public void testGitHubClientInvoked() throws InterruptedException {
template.sendBodyAndHeader("direct:start", "whatever",
"endpoint", "commits/test/test?username=test&password=test");
verify(gitHubClient).getCommitsForARepo(eq("test"), eq("master"), eq("test"), eq(20));
}
#SpringBootApplication
public static class TestApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(TestApplication.class)
.web(false)
.run(args);
}
#Bean
public RouteBuilder testRoute() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.toD("github:$simple{in.header.endpoint}");
}
};
}
#Bean
public GitHubClient mockGitHubClient() {
GitHubClient mock = Mockito.mock(GitHubClient.class);
return mock;
}
}
}
I solved it by using standard spring unit-tests like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#ActiveProfiles("test") // Load applicaton-test.properties in test/resources/config/application-test.properties
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) // cleanup spring context because jms broker does not exit properly
public class MyJmsTest {
private static final String MOCK_MY_ENDPOINT = "mock:myEndpoint";
#Autowired
CamelContext context;
#Autowired
ApplicationContext applicationContext;
#Autowired
ProducerTemplate producerTemplate;
#Before
public void configureMocks() throws Exception {
context.getRouteDefinition("MyRoute")
.adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString(".*myEndPointId.*")
.replace()
.to(MOCK_MY_ENDPOINT);
}
});
final MockEndpoint endpoint = context.getEndpoint(MOCK_MY_ENDPOINT, MockEndpoint.class);
endpoint.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
InputStream inStream = getClass().getClassLoader().getResourceAsStream("xml/my.xml");
String in = context.getTypeConverter().convertTo(String.class, inStream);
exchange.getIn().setBody(in);
}
});
}
#Test
public void synchronousCallBasic_1() throws Exception {
final MyConfiguration MyConfiguration = applicationContext.getBean(MyConfiguration.class);
final String myMessageBody =
context.getTypeConverter().convertTo(String.class, getClass().getClassLoader()
.getResourceAsStream("xml/0010_example.xml"));
final Object myResult = producerTemplate.requestBody(MyConfiguration.getActiveMqSynchronousEndpointUri(), myMessageBody);
assertThat(myResult, notNullValue());
assertThat((String)myResult, is("<example>1</example>"));
}
}
I solved this issue, with a lot of annotation which I found here, and now the test properties are correctly injected:
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#ActiveProfiles("test")
#EnableAutoConfiguration
#ComponentScan
#ContextConfiguration()
public class MessageDeliveryTest{
}
Also, the test properties file needs to be named application-{env}.properties, where "env" is the profile used here. For eg. for test the properties file should be application-test.properties

Rest Custom HTTP Message Converter Spring Boot 1.2.3

I want to create a custom of HttpMessageConverter using Rest, Json, Spring Boot 1.2.3 and Spring 4, However my custom HTTPMessageConverter its' never called.
I have preformed the following steps :
1: Created a class that extends AbstractHttpMessageConverter
#Component
public class ProductConverter extends AbstractHttpMessageConverter<Employee> {
public ProductConverter() {
super(new MediaType("application", "json", Charset.forName("UTF-8")));
System.out.println("Created ");
}
#Override
protected boolean supports(Class<?> clazz) {
return false;
}
#Override
protected Employee readInternal(Class<? extends Employee> clazz,
HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
InputStream inputStream = inputMessage.getBody();
System.out.println("Test******");
return null;
}
#Override
protected void writeInternal(Employee t,
HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
// TODO Auto-generated method stu
}
}
2: I create a configuration class to register HTTPMessageConverters
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
System.out.println("Configure Message Converters");
converters.add(new ProductConverter());
super.configureMessageConverters(converters);
//super.extendMessageConverters(converters);
}
}
3: The rest class method
#RequestMapping(value="/{categoryId}" ,method=RequestMethod.POST, consumes="application/json")
#PreAuthorize("permitAll")
public ResponseEntity<ProductEntity> saveProduct(#RequestBody Employee employee , #PathVariable Long categoryId) {
logger.log(Level.INFO, "Category Id: {0}" , categoryId);
ResponseEntity<ProductEntity> responseEntity =
new ResponseEntity<ProductEntity>(HttpStatus.OK);
return responseEntity;
}
My Custom HTTPMessageCoverter it's created but is never called ? Is there a configuration or step I'm missing ? any input or advice is appreciated.
After overriding the (AbstractHttpMessageConverter) class methods, I found out there's two annotations for achieving polymorphism #JsonTypeInfo and #JsonSubTypes. For anyone who wants achieve polymorphism can use these two annotations.
I believe you want to configure these message converters using the configureMessageConverters method in a configuration class that extends WebMvcConfigurerAdapter. I've done this myself with a converter for CSV content. I've included that code below. This link shows an example as well. This link may also be helpful. It seems like with Spring configuration it is not always clear on the best place to configure things. :) Let me know if this helps.
#Configuration
public class ApplicationWebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(new CsvMessageConverter());
}
}
You will also need top modify your supports() method to return true for classes supported by the converter. See the Spring doc for AbstractHttpMessageConverter supports method.

why invoking a service in my servlet gives me an NPE (spring)

I have defined a service in Spring
#Service("StockageService")
public class StockageServiceImpl implements StockageService{
}
with
public interface StockageService extends Serializable {
}
And I need in a servlet to invoke this service
So I wrote
public class SpringApplicationContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
sce.getServletContext().setAttribute("applicationContext", ac);
}
public void contextDestroyed(ServletContextEvent sce) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
And
public class handler extends HttpServlet {
private String message;
private StockageService stockageService;
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
ApplicationContext ac = (ApplicationContext) config.getServletContext().getAttribute("applicationContext");
this.stockageService = (StockageService)ac.getBean("StockageService");
}
The problem is that I get a NPE at the last mentionned line
(StockageService)ac.getBean("StockageService");
Where could I have made a mistake ?
First, thanks ankur-singhal for having taken the time to answer my question
I understand the reasonnement of your answer but it does not work when I invoke
ApplicationContextUtils. getApplicationContext().getBean("StockageService");
So I used a trick which works but I do not understand very well
I override the init in my servlet as it follows
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
config.getServletContext());
}
and I have to put a
#Autowired
private StockageService stockageService;
in the servlet and it works
It seems that ApplicationContext itself is comming as null.
Look at below code and try make use of this.
ApplicationContextUtils.java
We will create the following utility class, it implements ApplicationContextAware and provides the setApplicationContext which will be invoked by spring container and the applicationContext will be passed by it. We store it in a static variable and expose it through a get method so that it can be accessed all through the application.
You can set the same while creating ApplicationContext
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}

Resources