apache camel for jira - java

I want to build an Apache camel application to download a Jira
issue report, parse it, and store it into a .csv file.
I'm new at Apache camel, I do believe the jira here should be an endpoint, how to setup this configuration, I want to set is as from:("Jira") to (csv file).
I believe it could be something like this:
public void configure() throws Exception {
from("jira://pullRequestComment?ProjectKey=CAMEL-0000&IssueTypeId=1&IssueSummary=title")
.process(new MyLogProcessor())
.bean(new MyTransformer(),"transformContent")
.to("file:D:/camel/output");
}
I tried the above code, I got an exception for java conversion type.
Exception:

The JIRA component returns Java objects from the JIRA REST API. You need to either:
Support passing in the object type to your processor class as a method argument
Convert the JIRA Java Object to something else, then pass into your processor
BTW- The JIRA component caches "seen" data to know what is "new" to pass into the route. For really busy JIRA servers, this looks and acts like a memory leak so you'll need to be mindful to manage that scenario

The pullRequestComment endpoint is for a producer endpoint (i.e. it can only go in to("jira:pullRequestComment?..."). Since you want to poll for new comments, you should use the newComment endpoint. So your route would look something like:
from("jira:newComment?serverUrl={host}&username={username}password={password}")
.process(new MyLogProcessor())
.bean(new MyTransformer(),"transformContent")
.to("file:D:/camel/output");
Note that this endpoint returns an object of type com.atlassian.jira.rest.client.domain.Comment, so in MyLogProcessor if you do exchange.getIn().getBody(), it will return an object of type Comment (or maybe a List if there are multiple objects, you'll have to test this).
If you want to post a pull request comment, then you can use the pullRequestComment endpoint like the following:
from("direct://some/uri/name")
.header("ProjectKey", "CAMEL-0000")
.header("IssueTypeId", 1L)
.header("IssueSummary", "title")
.to("jira:pullRequestComment?serverUrl={host}&username={username}password={password}")
.... // More processing here
Then if you invoke the route from("direct://some/uri/name"), it should post the comment that's in the exchange body.

Related

Where is the Spring Actuator Controller endpoint and can I call it programmatically with jvm call?

I want to find the actual java class that serves the Spring Actuator endpoint (/actuator).
It's similar to this question in a way, but that person wanted to call it via a network HTTP call. Ideally, I can call it within the JVM to save on the cost of setting up an HTTP connection.
The reason for this is because we have 2 metrics frameworks in our system. We have a legacy metrics framework built on OpenCensus and we migrated to Spring Actuator (Prometheus metrics based on Micrometer). I think the Spring one is better but I didn't realize how much my company built infrastructure around the old one. For example, we leverage internal libraries that use OpenCensus. Infra team is depending on Opencensus-based metrics from our app. So the idea is to try to merge and report both sets of metrics.
I want to create my own metrics endpoint that pulls in data from Opencensus's endpoint and Actuator's endpoint. I could make an HTTP call to each, but I'd rather call them within the JVM to save on resources and reduce latency.
Or perhaps I'm thinking about it wrong. Should I simply be using MeterRegistry.forEachMeter() in my endpoint?
In any case, I thought if I found the Spring Actuator endpoint, I can see an example of how they're doing it and mimic the implementation even if I don't call it directly.
Bonus: I'll need to track down the Opencensus handler that serves its endpoint too and will probably make another post for that, but if you know the answer to that as well, please share!
I figured it out and posting this for anyone else interested.
The key finding: The MeterRegistry that is #Autowired is actually a PrometheusMeterRegistry if you enable the prometheus metrics.
Once you cast it into a PrometheusMeterRegistry, you can call its .scrape() method to return the exact same metrics printout you would when you hit the http endpoint.
I also need to get the same info from OpenCensus and I found a way to do that too.
Here's the snippet of code for getting metrics from both frameworks
Enumeration<MetricFamilySamples> openCensusSamples = CollectorRegistry.defaultRegistry.filteredMetricFamilySamples(ImmutableSet.of());
StringWriter writer = new StringWriter();
TextFormat.write004(writer, openCensusSamples);
String openCensusMetrics = writer.toString();
PrometheusMeterRegistry registry = (PrometheusMeterRegistry) meterRegistry;
String micrometerMetrics = registry.scrape();
return openCensusMetrics.concat(micrometerMetrics);
I found out another interesting way of doing this.
The other answer I gave but it has one issue. It contains duplicate results. When I looked into it, I realized that both OpenCensus and Micrometer were reporting the same result.
Turns out that the PrometheusScrapeEndpoint implementation uses the same CollectorRegistry that OpenCensus does so the both sets of metrics were being added to the same registry.
You just need to make sure to provide these beans
#PostConstruct
public void openCensusStats() {
PrometheusStatsCollector.createAndRegister();
}
#Bean
public CollectorRegistry collectorRegistry() {
return CollectorRegistry.defaultRegistry;
}

Apache camel cannot marshal to JSON from InputStreamCache

In Apache Camel I expose a REST service, take its input to call a SOAP service and then I'd like to marshal the SOAP response to JSON. My RouteBuilder looks roughly like this:
rest("/api")
.get("/client/{id}")
.to("direct:getClient");
from("direct:getClient")
.log(LoggingLevel.INFO, "Getting client with id ${id}")
.process(new GetClientProcessor())
.marshal().jaxb()
.to("spring-ws:http://localhost:9000/searchClient?soapAction=search")
.process(new ClientProcessor())
.marshal().json(JsonLibrary.Jackson);
I get the following error while marshalling the result to JSON:
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.apache.camel.converter.stream.InputStreamCache and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:275)
at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110)
at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1135)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
...
I know why this is happening, as I have turned on stream caching by default. However, I don't know how to fix this without turning off stream caching.
I have searched through the Camel documentation, mailing lists and fora, but I haven't found useful information sofar.
I finally solved it. The problem had nothing to do with described routes, rather the global rest configuration:
RestConfiguration restConfiguration = new RestConfiguration();
restConfiguration.setComponent("servlet");
restConfiguration.setBindingMode(RestConfiguration.RestBindingMode.json);
restConfiguration.setHost("localhost");
restConfiguration.setPort(serverPort);
camelContext.setRestConfiguration(restConfiguration);
The third line, setting the binding mode, is unnecessary as I state explicitly when I'd like to map to JSON and also which framework I use. After I remove this line everything works like a charm.
At this moment I don't exactly know how or why this has solved my problem, but I'm happy it did ;)

Spring MVC like processing of AMQP messages

What I want to do is process AMQP messages in a very similar way the Http Requests are processed using spring-webmvc annotations such as #RequestMapping, #RequestParam etc. But, instead of the Http Request my source object will be an AMQP message. The AMQP message request will have two headers, for example -
method="POST"
url="/api/myobjects/{someParam}"
and the payload will contain data in json format.
If you have noticed, this is nothing but HTTP REST api mapped to AMQP message.
I want to be able to write a controller like handler, for example -
#Controller
public class MyObjectHandler {
#RequestMapping(value="/api/myobjects/{someParam}", method="POST")
public MyObject createMyObject(#Payload MyObject myObj, #PathParam String someParam) {
//... some processing
return myObj;
}
// ...more handlers
}
I have looked at spring-amqp/rabbitmq annotations and also spring integration annotations. They are close to what I want, but would not allow routing to handler methods based on header parameters, especially the REST url.
I don't expect that a readymade solution would be available for this. Just want to make sure I choose the best possible option. Some of the options I think are (in order of precedence)
If the spring-webmvc annotation processing mechanism is extensible, just extend it to use AMQP message as source instead of Http Request
Modify the spring-webmvc annotation processing mechanism to take the AMQP message as input instead of Http Request
Write your own solution with custom annotaions and their processors, which I think is a very involving task
Or any other possible approach than above?
Any guidance/direction is appreciated.
I think the starting point is likely AbstractMethodMessageHandler in spring-messaging.
There's currently a SimpAnnotationMethodMessageHandler implementation for websockets which invokes #Controllers.
You could use a #RabbisListener method that has a Message<?> parameter (Spring AMQP will convert the underlying Rabbit message to a spring-messaging message, including the headers). Then, invoke the message handler to route to the appropriate controller method.
If you come up with a robust implementation, please consider contributing it.

Any simple way to test a #RequestBody method?

If I have a #Controller method whose parameter is a #RequestBody param, I usually have to write some jQuery script or something similar to perform an AJAX request with JSON object in order to call that method. If I tried calling that method via a web browser directly, it returns with a Error 415 Unsupported Media Type.
Is there any alternative to just quickly call such method using browser without having to write some jQuery code? Like perhaps a way to write the JSON object in the URL/address bar?
code:
#RequestMapping("testCall")
#ResponseBody
public List<TestObject> getTestCall (#RequestBody TestParams testParams) {
return stuff;
}
public class TestParams {
private Integer testNumber;
//getter/setter for testNumber
}
I thought maybe I could just do:
http://localhost/testCall?testNumber=1
maybe Spring would auto populate a new TestParams instance with that property set to 1 but that didnt work...
maybe I need to do something extra for that?
The whole point of a #RequestBody annotated parameters is for the Spring MVC stack to use the HTTP request body to produce an argument that will be bound to the parameter. As such, you need to provide a request body. Sending a request body is very atypical for a GET request. As such, browsers don't typically support it, at least not when simply entering an address in the address bar and submitting the request.
You'll need to use a different HTTP client, like jQuery. I typically have a small Java project in Eclipse that's setup with an Apache HTTP components client which can send HTTP requests to whatever server. It takes a few seconds/minutes to setup the correct request body and run.
I have spent the last year building a REST API, and by far the best way to exercise that API manually is using the Chrome Extension, Postman. I cannot recommend this tool enough.
https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en
To test your simple example you'll need to invoke a POST (I assume that as you have a request body, but your controller method doesn't define a HTTP Verb) using POSTMAN to your Url (like the following example):
POST /contextRoot/testCall
{
"testNumber": 1
}
If you want to test your API automatically (which I recommend), you can use the excellent Spring Mvc Test project. This allows your to call your API via a rest-like DSL and assert that the response is in the shape you want. More details can be found here:
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework
you can add request params to the getTestCall method:
#RequestParam(value = "testNumber", required = false, defaultValue = "") String testNumber
There is a chrome app called Advanced REST client. You can pass the data in form of json to your controller using this chrome app. For eg. json data is
id:1,
name:"xyz"
whereas the controller can have #RequestBody Person form.
The Person class would be a POJO having id and name as instance variables. The Spring would automatically map the json data to the form.
I think this is the easiest and simplest way of checking your spring controller.
Check the extension Advanced REST client here
From what I know You can send JSON object to the webbrowser and it will be displayed without further need of AJAX.
useful tutorial:
http://www.mkyong.com/spring-mvc/spring-3-mvc-and-json-example/

Apache Camel - http Producer don't use GET-Method with enrich Pattern

I have a RESTful Webservice which I want to use with a content enricher in a Camel-Route.
See this similar Code:
from("direct:in") // Here comes XML
.to("validator:something.xsd") // validate it
.unmarshal("something-jaxb") // put it into a POJO
.setHeader(Exchange.HTTP_URI, simple("http://localhost:12345/restws/${header.foo}")) // Create the dynamic URI with simple
.setHeader(Exchange.HTTP_METHOD, constant("GET")) // set the HTTP-Method to use
.enrich("http://dummy", new MyAggregator()) // fetch some Information from a Restful Webservice
.to("direct:out"); // send the Message to another route
If I run this, I get the following error:
No type converter available to convert from type: de.my.Class to the required type: java.io.InputStream with value de.my.Class#620ee765.
It seems to me, he tries to send the body of the Exchange to the http-Endpoint, although I set the HTTP-Method to GET. I've read the documentation (https://camel.apache.org/http.html) and below Calling using GET or POST it describes that the Algorithm which selects the Method first look at the Header (1. Use method provided in header).
I found some workarounds, which describes how to move the body to a Exchange-property and move it back again after the Webservice-Call, but this can't be it...
EDIT:
Like Claus Ibsen mentioned enrich doesn't support dynamic uris. Fixed this in the example!
Neither enrich nor pollEnrich supports dynamic uris for their endpoints. Instead of using enrich, you can use the recipient list which support dynamic uris, and the aggregation strategy as well.
If it's "full of lists" of resources, sounds like you would want to split the lists and do a get on each resource
What's in the pojo? Your GET parameter should be a resource identifier and query parameters. That requires a custom converter.
http://fusesource.com/docs/esb/4.2/rest/RESTIntro.html
maybe consider using restlet instead... it's easier I think.
http://camel.apache.org/restlet.html

Categories