I'm trying with no success to integrate Opentelemetry with Zuul using Spring Boot. What I'm trying to do is to pass Opentelemetry context from Zuul to other microservices in the chain and when the response came into Zuul close the trace
(Create trace) Zuul --call--> (new Span) Microservice A --call--> (new Span) Microservice B
(Close trace ) Zuul <--response-- Microservice A <-response-- Microservice B
Anyone has made something similar to this?
I solved this way:
#Component
public class HeaderRequestFilter extends ZuulFilter {
#Autowired
private Tracer tracer;
#Override
public int filterOrder() {
// run before PreDecoration
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
#Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
Span span = tracer.spanBuilder(ctx.getRequest().getRequestURI()).startSpan();
span.setAttribute("eurekaInstanceId", eurekaInstanceId);
tracer.withSpan(span);
OpenTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), ctx, new Setter<RequestContext>() {
#Override
public void set(RequestContext carrier, String key, String value) {
carrier.addZuulRequestHeader(key, value);
}
});
return null;
}
}
and the response, where the span is closed
#Component
public class HeaderResponseFilter extends ZuulFilter {
#Autowired
private Tracer tracer;
#Override
public int filterOrder() {
// Run before PreDecoration
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
#Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
// Close span
tracer.getCurrentSpan().end();
return null;
}
}
Related
Hi i am trying to study named injection in Dagger2
Here are my Java classes but none of them seems to be working.
What i want is that based on #Named annotation i wish to get different objects.
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
AudioServer provideAudioServer();
VideoServer provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
public TestInject() {
// ServerComponent component = DaggerServerComponent.builder()
// .build();
// component.inject(this);
}
public void test() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.test();
}
}
EDITED Please see answer in TestInject.java and ServerComponent.java
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
#Named("audio")
Server provideAudioServer();
#Named("video")
Server provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
#Inject
#Named("video")
Server videoServer;
public TestInject() {
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
public void testAudioServer() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "audio server Failure";
logger.info(serverResponse);
}
}
public void testVideoServer() {
String serverResponse = null;
if (videoServer != null) {
serverResponse = videoServer.request("movie.mp4");
logger.warning(serverResponse);
} else {
serverResponse = "Video server Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.testAudioServer();
inject.testVideoServer();
}
}
Your main problem seems to be related to the fact that you expect in the class TestInject a Server named audio while your provider returns AudioServer so dagger cannot satisfy your dependency.
Indeed don't forget that the annotation #Named is used to distinguish 2 objects of the same type, in other words you can annotate with #Named("audio") different providers as long as they don't return the same type. The object produced will then be identified by its type and its name.
So for example here is one way to fix you problem:
The class TestInject:
public class TestInject {
...
public TestInject() {
// Needed to inject your dependencies
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
...
}
The class ServerComponent
#Singleton
#Component(modules = ServerModule.class)
public interface ServerComponent {
void inject(TestInject inject);
}
The class ServerModule
#Module
public class ServerModule {
#Provides
#Named("audio")
#Singleton
public Server provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
public Server provideVideoServer() {
return new VideoServer();
}
}
Even with your question update, your module should be what I propose abose otherwise it won't compile for the same reason described previously.
Thymeleaf has a number of useful utilities like #strings.capitalize(...) or #lists.isEmpty(...). I'm trying to add a custom one but have no idea how to register this.
Have made a custom Util class:
public class LabelUtil {
public String[] splitDoubleWord(String str) {
return str.split("[A-Z]", 1);
}
}
Now I'm going to use it like this:
<span th:each="item : ${#labels.splitDoubleWord(name)}" th:text="${item}"></span>
Of course, it won't work because I haven't registered the Util and defined #labels var.
So, the question is how and where to do it?
This answer is for thymeleaf 2.x.
If you use thymeleaf 3.x or later, please see other answers.
public class MyDialect extends AbstractDialect implements IExpressionEnhancingDialect {
public MyDialect() {
super();
}
#Override
public String getPrefix() {
// #see org.thymeleaf.dialect.IDialect#getPrefix
return "xxx";
}
#Override
public boolean isLenient() {
return false;
}
#Override
public Map<String, Object> getAdditionalExpressionObjects(IProcessingContext ctx) {
Map<String, Object> expressions = new HashMap<>();
expressions.put("labels", new LabelUtil());
return expressions;
}
}
and register your dialect.
#Configuration
public class ThymeleafConfig {
#Bean
public MyDialect myDialect() {
return new MyDialect();
}
}
thymeleaf-extras-java8time source code is good reference for creating custom thymeleaf expressions.
The API for registering a custom expression object has changed in Thymeleaf 3, for example:
public class MyDialect extends AbstractDialect implements IExpressionObjectDialect {
MyDialect() {
super("My Dialect");
}
#Override
public IExpressionObjectFactory getExpressionObjectFactory() {
return new IExpressionObjectFactory() {
#Override
public Set<String> getAllExpressionObjectNames() {
return Collections.singleton("myutil");
}
#Override
public Object buildObject(IExpressionContext context,
String expressionObjectName) {
return new MyUtil();
}
#Override
public boolean isCacheable(String expressionObjectName) {
return true;
}
};
}
}
I use GWTP and restyGWT. I would like to use placeManager in restyGWT DispatcherCallback, when my rest server will answer with 401 unauthorised I would like to redirect application to login page, that User could apply credentials and retried his request.
To do this I have to somehow get instance of PlaceManager (from gwtp framework). I cannot use #Inject annotation, cause I have manuall call to constructor as follow:
public class ForbiddenDispatcherFilter implements DispatcherFilter {
#Override
public boolean filter(Method method, RequestBuilder builder) {
builder.setCallback(new ForbiddenDispatcherCallback(method));
return true;
}
}
public class ForbiddenDispatcherCallback implements RequestCallback {
protected RequestCallback requestCallback;
public ForbiddenDispatcherCallback(Method method) {
this.requestCallback = method.builder.getCallback();
}
#Override
public void onResponseReceived(Request request, Response response) {
if (response.getStatusCode() == Response.SC_FORBIDDEN || response.getStatusCode() == Response.SC_UNAUTHORIZED) {
// make a hard redirect to login page
// TODO change redirect to GWTP native
Window.Location.assign("#login");
// PlaceRequest placeRequest = new
// PlaceRequest.Builder(placeManager.getCurrentPlaceRequest()).nameToken(Routing.Url.login).build();
// placeManager.revealPlace(placeRequest);
} else {
requestCallback.onResponseReceived(request, response);
}
}
public class RestyDispatcher extends DefaultFilterawareDispatcher {
public RestyDispatcher() {
addFilter(new ForbiddenDispatcherFilter());
addFilter(new BasicAuthHeaderDispatcherFilter());
}
#Override
public Request send(Method method, RequestBuilder builder) throws RequestException {
return super.send(method, builder);
}
}
Please help.
Edit
public class ClientModule extends AbstractPresenterModule {
#Override
protected void configure() {
bind(RestyGwtConfig.class).asEagerSingleton();
install(new DefaultModule.Builder()//
.defaultPlace(Routing.HOME.url)//
.errorPlace(Routing.ERROR.url)//
.unauthorizedPlace(Routing.LOGIN.url)//
.tokenFormatter(RouteTokenFormatter.class).build());
install(new AppModule());
// install(new
// GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
bind(CurrentUser.class).in(Singleton.class);
bind(IsAdminGatekeeper.class).in(Singleton.class);
bind(UserLoginGatekeeper.class).in(Singleton.class);
// Google Analytics
// bindConstant().annotatedWith(GaAccount.class).to("UA-8319339-6");
// Load and inject CSS resources
bind(ResourceLoader.class).asEagerSingleton();
}
}
and:
public class RestyGwtConfig {
static {
// GWT.log("--> RestyGwtConfig -> setDispatcher");
Defaults.setDispatcher(new RestyDispatcher());
// GWT.log("--> RestyGwtConfig -> setServiceRoot");
Defaults.setServiceRoot(new Resource(GWT.getModuleBaseURL()).resolve(ServiceRouting.SERVICE_ROOT).getUri());
UserCredentials.INSTANCE.setUserName("ronan");
UserCredentials.INSTANCE.setPassword("password");
}
}
How and where do you create your ForbiddenDispatcherFilter ?
You could use guice's AssistedInjection to inject the PlaceManager into your ForbiddenDispatcherCallback.
public class ForbiddenDispatcherCallback implements RequestCallback {
protected RequestCallback requestCallback;
protected PlaceManager placeManager;
#Inject
public ForbiddenDispatcherCallback(PlaceManager placeManager, #Assisted Method method) {
this.placeManager = placeManager;
this.requestCallback = method.builder.getCallback();
}
}
You need to define an factory interface:
public interface AssistedInjectionFactory {
ForbiddenDispatcherCallback createForbiddenCallback(Method method);
}
In the configure method of your ClientModule you need to call:
install(new GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
Then you can instantiate your class this way:
public class ForbiddenDispatcherFilter implements DispatcherFilter {
AssistedInjectionFactory factory;
#Inject
public ForbiddenDispatcherFilter(AssistedInjectionFactory factory)
{
this.factory = factory;
}
#Override
public boolean filter(Method method, RequestBuilder builder) {
builder.setCallback(factory.AssistedInjectionFactory(method))
return true;
}
}
Of course this requires that you also inject the ForbiddenDispatcherFilter.
Edit:
You could try to pass the RestyDispatcher to the constructor of your RestyGWTConfig:
public class RestyGwtConfig {
#Inject
public RestyGwtConfig(RestyDispatcher dispatcher) {
Defaults.setDispatcher(dispatcher);
}
static {
// GWT.log("--> RestyGwtConfig -> setServiceRoot");
Defaults.setServiceRoot(new Resource(GWT.getModuleBaseURL()).resolve(ServiceRouting.SERVICE_ROOT).getUri());
UserCredentials.INSTANCE.setUserName("ronan");
UserCredentials.INSTANCE.setPassword("password");
}
}
The RestyDispatcher looks like this:
public class RestyDispatcher extends DefaultFilterawareDispatcher {
#Inject
public RestyDispatcher(ForbiddenDispatcherFilter filter) {
addFilter(filter);
addFilter(new BasicAuthHeaderDispatcherFilter());
}
#Override
public Request send(Method method, RequestBuilder builder) throws RequestException {
return super.send(method, builder);
}
}
I am using Dropwizard and wrote a Security Provider that should check the session. If a user is available, it should return it. Otherwise it should throw an exception. How can I get the Session from an HttpRequestContext?
public class SecurityProvider<T> implements InjectableProvider<Auth, Parameter> {
private static class Injectable<T> extends AbstractHttpContextInjectable<T> {
#Override
public T getValue(HttpContext c) {
//Here, get somehow the session,
//then check if user is in session,
//if so, proceed
return null;
}
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public com.sun.jersey.spi.inject.Injectable getInjectable(ComponentContext ic, Auth auth, Parameter parameter) {
return new Injectable<T>();
}
}
I have managed to successfully implement a custom injection annotation with target PARAMETER. I do not understand how I make my the annotation support target METHOD as well though?
Here is my sample code:
Hello annotation:
#Retention(RUNTIME)
#Target({METHOD, PARAMETER})
public #interface Hello {
}
Hello annotation resolver:
#Singleton
public class HelloResolver {
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
}
#Singleton
public static class HelloValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public HelloValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
if (!String.class.equals(parameter.getRawType())) return null;
if (parameter.getAnnotation(Hello.class) == null) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
final DateTime now = DateTime.now();
if (22 < now.getHourOfDay() || now.getHourOfDay() < 6) {
throw new WebApplicationException(FORBIDDEN);
} else {
return format("Hello, it is %s o'clock so I am awake! :)", forPattern("HH:mm").print(now));
}
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(HelloValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(HelloInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Hello>>() {
}
).in(Singleton.class);
}
}
}
Hello resources:
#Path("hello")
public class HelloResource {
#GET
#Path("method")
#Produces(APPLICATION_JSON)
#Hello
public String method() {
return "Hello!";
}
#GET
#Path("param")
#Produces(APPLICATION_JSON)
public String param(#Hello final String hello) {
return hello;
}
}
When I hit
http://localhost:8080/hello/method
I get a Hello! back no matter if the hour is within the forbidden interval.
I am not sure this will work, but you could try this:
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
public boolean isMethodParameterIndicator() {
return true;
}
}
Warning: I have not tried this myself but in theory that should allow your resolver to work as a parameter in the method.