How to change the operation name of a span with Apache Camel OpenTracing component? - rabbitmq

I successfully added Apache Camel's OpenTracing component to my application. I can see traces in Jaeger UI. But the traces for the RabbitMQ component show only the exchange name without the routing key as operation name. Because of my application uses only one exchange with different routing keys, I need to see the routing key as operation name in my traces.
Research
With OpenTracing Spring RabbitMQ I could expose another customized RabbitMqSpanDecorator, see Span decorator:
Note: you can customize your spans by declaring an overridden RabbitMqSpanDecorator bean.
(However, I coulnd't change the operation name with the RabbitMqSpanDecorator at all, because the operation name is hard coded to producer or consumer.)
Unfortunately Apache Camel uses its own different implementation of a RabbitmqSpanDecorator to decorate spans. I wrote a custom class by overiding Apache Camel's RabbitmqSpanDecorator, but my custom class wasn't used.
Question
How can I change the operation name of a span with Apache Camel OpenTracing component for Apache Camel RabbitMQ component?

It is possible to change the Tracer implementation with ServiceLoader, see OpenTracing:
EXPLICIT
Include the camel-opentracing component in your POM, along with any specific dependencies associated with the chosen OpenTracing compliant Tracer.
To explicitly configure OpenTracing support, instantiate the OpenTracingTracer and initialize the camel context. You can optionally specify a Tracer, or alternatively it can be implicitly discovered using the Registry or ServiceLoader.
With the DefaultTracer it is also possible to change the RabbitmqSpanDecorator with ServiceLoader, see Tracer.java:
static {
ServiceLoader.load(SpanDecorator.class).forEach(d -> {
SpanDecorator existing = DECORATORS.get(d.getComponent());
// Add span decorator if no existing decorator for the component,
// or if derived from the existing decorator's class, allowing
// custom decorators to be added if they extend the standard
// decorators
if (existing == null || existing.getClass().isInstance(d)) {
DECORATORS.put(d.getComponent(), d);
}
});
}
Therefore, I had to add a file org.apache.camel.tracing.SpanDecorator containing the name of my custom RabbitmqSpanDecorator, see ServiceLoader:
Deploying service providers on the class path
A service provider that is packaged as a JAR file for the class path is identified by placing a provider-configuration file in the resource directory META-INF/services. The name of the provider-configuration file is the fully qualified binary name of the service. The provider-configuration file contains a list of fully qualified binary names of service providers, one per line.
My custom RabbitmqSpanDecorator:
public class CustomRabbitmqSpanDecorator extends RabbitmqSpanDecorator {
#Override
public String getOperationName(Exchange exchange, Endpoint endpoint) {
return ((RabbitMQEndpoint) endpoint).getRoutingKey();
}
}

Related

Is there a symbolic item to reference spring-cloud config server?

A project I'm working on is utilizing Spring Cloud Config server to handle property update/refresh.
One question that has repeatedly come up is how to reference/serve plain text from the config server.
I know that the server supports serving plain-text. What I'm trying to figure out is that if I have a reference /foo/default/master/logj42.xml.
How would I reference this in an "agnostic" way such that if I were to put:
{configserver}/foo/default/master/log4j2.xml in the config file
The reference {configserver} would be expanded.
Additionally, when using "discovery", if I inject the reference to the "resource" as above, the default mechanism will attempt to use java.net.URLConnection to load the content. I do not think it will resolve the 'discovery' host.
Thanks in advance.
It also can be resolved using Customizing Bootstrap Configuration without aspects by creating custom property source and set configserver uri after locating from discovery.
I had similar issue, more details in this stackoverflow post
I found a way to do this that is minimally invasive but "pierces the veil" of where the config server actually resides.
On the primary application class, the annotation #EnableDiscoveryClient needs to be added.
I created an aspect to add a property source with a key that indicates the actual URI of the server handling the request:
#Component
#Aspect
public class ResolverAspect {
#Autowired
private DiscoveryClient discoveryClient;
#Pointcut("execution(org.springframework.cloud.config.environment.Environment org.springframework.cloud.config.server.environment.EnvironmentController.*(..))
private void environmentControllerResolve();
#Around("environmentControllerResolve()")
public Object environmentControllerResolveServer(final ProceedingJoinPoint pjp) throws Throwable {
final Environment pjpReturn = (Environment)pjp.proceed();
final ServiceInstance localSErviceInstance = discoveryClient.getLocalServiceInstance();
final PropertySource instancePropertySource =
new PropertySource("cloud-instance", Collections.singletonMap("configserver.instance.uri", localServiceInstance.getUri().toString()));
pjpReturn.addFirst(instancePropertySource);
return pjpReturn;
}
}
By doing this, I expose a key configserver.instance.uri which can then be referenced from within a property value and interpolated/resolved on the client.
This has some ramifications with regard to exposing the actual configuration server, but for resolving resources that do not necessarily utilize the discovery client this can be utilized.

How to use a dynamic URI in From()

As mentioned in Apache Camel, it allows to write dynamic URI in To(), does it allows to write dynamic URI in From().
Cause I need to call the multiple FTP locations to download the files on the basis of configuration which I am going to store it in database.
(FTPHost, FTPUser, FTPPassword, FTPSourceDir, FTPDestDir)
I will read these configuration from the DB and will pass it to the Camel route dynamically at runtime.
Example:
This is the camel route example that I have to write dynamically
<Route>
<from uri="ftp://${ftpUser}#${ftpHost}:${ftpPort}/${FTPSourceDir}?password=${ftpPassword}&delete=true"/>
<to uri="${ftpDestinationDir}"/>
</Route>
As you see in example, I need to pass these mentioned parameters dynamically.
So how to use dynamic uri in From()
You can read it from property file as follows,
<bean id="bridgePropertyPlaceholder" class="org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer">
<property name="location" value="classpath:/config/Test.properties"/>
</bean>
<Route>
<from uri="ftp://{{ftpUser})#${{ftpHost}}:{{ftpPort}}/${{FTPSourceDir}}?password={{ftpPassword}}&delete=true"/>
<to uri="{{ftpDestinationDir}}"/>
</Route>
ftpUser, ftpHost.... - all are keys declared in Test.properties
If you want to get those variables from your exchange dynamically, you cannot do it in regular way as you mentioned in your example. You have to use consumer template as follows,
Exchange exchange = consumerTemplate.receive("ftp:"+url);
producerTemplate.send("direct:uploadFileFTP",exchange );
You have to do that from a spring bean or camel producer. Consumer template will consume from given component, and that producer template will invoke direct component declared in your camel-context.xml
Note: Consumer and Producer templates are bit costly. you can inject both in spring container and let the spring handle the life cycle.
From camel 2.16 on-wards, we can use pollenrich component to define polling consumer like file, ftp..etc with dynamic url/parameter value like below
<route>
<from uri="direct:start"/>
<pollEnrich>
<simple>file:inbox?fileName=${body.fileName}</simple>
</pollEnrich>
<to uri="direct:result"/>
</route>
Its awesomeeee!!!
Refer: http://camel.apache.org/content-enricher.html
I help a team who operates a message broker switching about a million message per day. There are over 50 destinations from which we have to poll files over all file sharing brands (FTP, SFTP, NFS/file: ...). Maintaining up to 50 deployments that each listen to a different local/remote directory is indeed an overhead compared with a single FILE connector capable of polling files at the 50 places according to the specific schedule and security settings of each... Same story for getting e-mail from pop3 and IMAP mailboxes.
In Camel, the outline of a solution is as follows:
you have no choice but use the java DSL to configure at least the from() part of your routes with an URI that you can indeed read/build from a database or get from an admin request to initiate a new route. The XML DSL only allows injecting properties that are resolved once when the Camel context is built and never again afterwards.
the basic idea is to start routes, let them run (listen or poll a precise resource), and then shutdown & rebuild them on demand using the Camel context APIs to manage the state of RouteDefinitions, Routes, and possibly Endpoints
personally, I like to implement such dynamic from() instantiation on minimalist routes with just the 'from' part of the route, i.e. from(uri).to("direct:inboundQueue").routeId("myRoute"), and then define - in java or XML - a common route chunk that handles the rest of the process: from("direct:inboundQueue").process(..).etc... .to(outUri)
I'll advise strongly to combine Camel with the Spring framework, and in particular Spring MVC (or Spring Integration HttpGateway) so that you will enjoy the ability to quickly build REST, SOAP, HTTP/JSP, or JMX bean interfaces to administer route creation, destruction, and updates within a Spring + Camel container, both nicely integrated.
You can then declare in the Spring application context a bean that extends SpringRouteBuilder, as usual when building Camel routes with the java DSL in Spring; in the compulsory #Override configure() method implementation, you shall save your routeDefinition object built by the from(uri) method, and assign it a known String route-id with the .routeId(route-id) method; you may for instance use the route-id as a key in a Map of your route definition objects already created and started, as well as a key in your DB of URI's.
then you extend the SpringRouteBuilder bean you have declared with new methods createRoute(route-id), updateRoute(route-id), and removeRoute(route-id); The associated route-id parameters needed for create or update will be fetched from the database or another registry, and the relevant method, running within the RouteBuilder bean, will take advantage from the getContext() facility to retrieve the current ModelCamelContext, which in turn is used to stopRoute(route-id), removeRoute(route-id), and then addRouteDefinition(here is where you need the routeDefinition object), and finally startRoute(route-id) (Note: beware of possible ghost Endpoints that would not be removed, as explained in the removeRoute() javadoc)
your administrative interface (which typically takes the form of a Spring #Controller component/bean that handles the HTTP/REST/SOAP traffic) will indeed have an easy job to get the previously created SpringRouteBuilder extension Bean injected by Spring in the controller bean, and thus access all the necessary createRoute(route-id), updateRoute(route-id), and removeRoute(route-id) methods that you have added to the SpringRouteBuilder extension Bean.
And that works nicely. The exact implementation with all the error handling and validation code that applies is a bit too much code to be posted here, but you have all the links to relevant "how to's" in the above.
I think you can implement your requirement within a Camel route.
Because you want to poll multiple FTP sites you'll have to somehow trigger this process. Maybe you could do this based on a Quartz2 timer. Once triggered you could read the configured FTP sites from your database.
In order to poll the given FTP sites you can use the Content Enricher pattern to poll (see: pollEnrich) a dynamically evaluated URI.
Your final basic route may look something like this (pseudocode):
from("quarz...")
to("sql...")
pollEnrich("ftp...")
...
Use Camel endpoint with spring spel expression.
Set up a Camel endpoint in the context so it can be accessed from any bean:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<endpoint id="inventoryQueue" uri="#{config.jms.inventoryQueueFromUri}"/>
</camelContext>
Now you can reference the inventoryQueue endpoint within the `#Consume` annotation as follows:
#org.apache.camel.Consume(ref = "inventoryQueue")
public void updateInventory(Inventory inventory) {
// update
}
Or:
<route>
<from ref="inventoryQueue"/>
<to uri="jms:incomingOrders"/>
</route>

Add another UrlHandlerMapping to Spring data rest

I'm having a normal spring-mvc project and I'm also building a rest module as a separate jar file. The goal is when I have the rest jar in my classpath to have the normal website mapped to / and the spring-data-rest repositories mapped to /rest. For the rest module I have defined RepositoryRestMvcConfiguration as well as a WebApplicationInitializer and it all works fine.
So now I want to add some more URLs to the rest module (like /synchronize, and /authenticate, etc.) but as soon as I add controllers in the rest module, they are picked up by the parent application context (the one for the website /). I tried specifying them as bean in the RepositoryRestMvcConfiguration but still they are picked up by the other parent context and the filters of the parent context are fired. And when I access the spring-data-rest through /rest no filters are triggered.
So I was wondering: is there a method I could override in the RepositoryRestMvcConfiguration so that I can add extra url handler mappings?
I assume you mean that you want to have another controller advertised as part of Spring Data REST's root hypermedia.
To do so, you need to create another class in your app like this:
#Component
class DogifierResourceProcessor implements ResourceProcessor<RepositoryLinksResource> {
#Override
public RepositoryLinksResource process(RepositoryLinksResource objects) {
objects.add(new Link(ServletUriComponentsBuilder.fromCurrentRequest()
.build()
.toUriString()
.concat("dogifier/{id}"), "dogifier"));
return objects;
}
}
This will create a hypermedia entry with rel="dogifier" that lists /dogifier/{id} as the URI. It will also prefix it with the proper URN, etc.
Of course, you can use Spring HATEOAS to link to a controller method without having to specify the actual path by hand. That would reduce maintenance and encourage better hypermedia controls.
You need to exclude those controllers from the classpath scanning of the parent context. Just follow the instructions in the Spring documentation.

Trying to set an OnRelease event on components registered through a configuration file

I'm registering components that require special handling on release (namely, a WCF clients) via a config file.
<autofac>
<components>
<component
type="SomeType"
service="ISomeType"/>
</components>
</autofac>
However, via the configuration file, I don't see how I can specify an event.
I've looked at implementing the OnRelease event via a module, but I seem to lack access to some of the innards required to get it working. Mainly, in:
registration.Activating += (s, e) =>
{
var ra = new ReleaseAction(() => ReleaseWcfClient(e.Instance));
e.Context.Resolve<ILifetimeScope>().Disposer.AddInstanceForDisposal(ra);
};
The ReleaseAction class is not available outside Autofac.
Autofac does not support setting up events via XML configuration. You must do that in code.
However, one way you can do this in a more cross-cutting fashion is to create an Autofac module and override the AttachToComponentRegistration method. In there, you can test each registration for something (like whether it's a registration for a particular interface) and attach your Activating event handler there.
There is an example of using AttachToComponentRegistration on the Autofac wiki showing how you can use this event to wire up log4net.
Two other notes:
You might be interested in using the UseWcfSafeRelease() registration extension for WCF client proxies. I don't know what your ReleaseWcfClient() method is doing internally, but if the point of it is to handle the possible exceptions from WCF, Autofac has that in UseWcfSafeRelease(). You can read about that on the wiki.
If a component implements IDisposable (the way WCF clients do) then Autofac will automatically track it and handle disposal. If you have something specific you want to run when Autofac executes disposal, you'd want to use OnRelease for the registration. If it implements IDisposable and you want to set up your own disposal solution, you need to register the component using ExternallyOwned() so your component isn't double-disposed.
(I don't know if either of those latter points apply here, but thought I'd raise them since we were in the territory.)

Naming conventions for a project containg only WCF ServiceReferences?

Let's say we have a back-end that needs to talk to N external systems using some kind of Web Services.
What I do is: Create a separate project and generate there the proxy classes (using the service's WSDL in the WCF Service Reference dialog).
About the project name suffix:
I firstly though XxAdapter. But then, I started creating classes with additional logic like CircuitBreakers so I ended up with XxAgent (from ServiceAgent).
What should be the "correct" suffix for the name of such projects.
The most appropriate suffix is "Proxies" because of several reasons:
Your component contains all the web service proxy classes.
In case that you want to make calls to several service proxies transparent, you can create a new class named MyLocalProxy, and perform the action
public class MyServiceProxy
{
public void DoSomething()
{
var serviceProxy1 = new ServiceProxy1();
serviceProxy1.DoOneThing();
var serviceProxy2 = new ServiceProxy2();
serviceProxy2.DoAnotherThing();
}
}
The additional class helps you to not depend on concrete service proxies, so you can interchange them as you wish.
Cheers.