dropwizard version migration and DI - dropwizard

I've run to some issues trying to migrate dropwizard from 1.2 to 2.0.24 (and 2.0.25) related to DI, wondering if someone has had same issues or any ideas.
We have one application,
public class Account extends Application<AccountConfiguration> {
public static void main(String[] args) throws Exception {
new Account().run(args);
}
#Override
public void initialize(Bootstrap<Configuration> bootstrap) {
bootstrap.addBundle(new DropwizardBundle<>());
}
#Override
public void run(AccountConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().register(new SessionResource(authenticationService));
}
}
The DropWizardBundle class binds an instance to a class:
public void run(AccountConfiguration configuration, Environment environment) {
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
bind(configuration.getResponder()).to(Responder.class);
}
});
And the SessionResource looks like
#Path("/sessions")
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON, "application/v1+json"})
#RequiredArgsConstructor
#Timed
public class SessionResource {
#Inject
private Responder responder;
private final AuthenticationService authenticationService;
#POST / #GET methods
}
The current code, in Dropwizard 1.2 is running and Responder is injected. Switching to 2.0.24/25 Responder is null. I am wondering if I missed something in the migration..

I'm not sure if this is working the way you intended (or the way you are think it is) in version1.X for you. I do not know what configuration.getResponder() is returning so I can't really test your case. I found for resource classes using DI, a lot of problems I see people face can be solved by registering the class and allow the H2K instantiate the resources for you.
environment.jersey().register(SessionResource.class);
You will have to update how AuthenticationService is passed into the resource by registering it to so it can also be injected. The below is an example of registering it as a AuthenticationService as a singleton.
public void run(AccountConfiguration configuration, Environment environment) {
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
//Not sure how AuthenticationService is instantiate but this is an example
bind(new AuthenticationService()).to(AuthenticationServic.class).in(Singleton.class);
}
});

Related

ServiceLocator with Autofac in asp.net core 3.1

I'm developing an asp.net core 3.1 webapi application and i'm using Autofac as DI container.
For one particular case i cannot use ConstructorInjection nor propertyinjection nor methodinjection. My only way is to implement a sort of ServiceLocator pattern with the support of Autofac.
*I known that the service locator is an antipattern, but i will use that only if it will be the only chance *
Said that, I create a little static class :
public static class ServiceLocator
{
private static XXXX Resolver;
public static T Resolve<T>()
{
return Resolver.Resolve<T>();
}
public static void SetCurrentResolver(XXXX resolver)
{
Resolver = resolver;
}
}
I write XXXX on the type of Resolver property because i don't know which is the Autofac class to use. The method SetCurrentResolver will be called in the Configure method of Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILifetimeScope serviceProvider)
{
//OTHER STUFF NOT RELATED HERE
ServiceLocator.SetCurrentResolver(serviceProvider);
}
I tried to pass the instance of ILifetimeScope but when i use it later in the service locator it will be Disposed and then not work. I thinked to pass an IContainer object but i'm not able to retrieve an instance in Startup.cs (neither in the Configure method nor in the ConfigureContainer)
I Report the ConfigureContainer method for completion
public void ConfigureContainer(ContainerBuilder builder)
{
//first register the dependency for WebApi Project
builder.RegisterType<HttpContextUserService>().As<IUserService>().SingleInstance();
//and then register the dependency for all other project
var appConfiguration = new AppConfiguration();
Configuration.GetSection("Application").Bind(appConfiguration);
builder.RegisterInstance(appConfiguration).SingleInstance();
builder.RegisterModule(new DependencyInjectionBootstrapper(appConfiguration));
}
Anyone can help me with this problem?
Thanks
This is actually answered in the Autofac docs if you look at the example.
Here are the relevant bits.
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Body omitted for brevity.
}
public ILifetimeScope AutofacContainer { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
// Body omitted for brevity.
}
public void ConfigureContainer(ContainerBuilder builder)
{
// Body omitted for brevity.
}
public void Configure(IApplicationBuilder app)
{
// If, for some reason, you need a reference to the built container, you
// can use the convenience extension method GetAutofacRoot.
// THIS IS WHERE YOU'D SET YOUR SERVICE LOCATOR.
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
}
}

Registering Dropwizard configuration with Jersey 2 (HK2) DI

In my Dropwizard (1.2.4) application I'm having trouble injecting my Dropwizard configuration into classes that are instantiated by HK2. What's the best way to achieve this?
Just bind the configuration instance.
#Override
public void run(final DummyConfiguration conf, Environment env) {
env.jersey().register(new AbstractBinder() {
#Override
public void configure() {
bind(conf).to(DummyConfiguration.class);
}
})
}
Now you can #Inject the DummyConfiguration anywhere you need it.
For dropwizard version 2.0.1:
#Override
public void run(final YourConfiguration conf, Environment env) throws Exception {
env.jersey().getResourceConfig().register(new AbstractBinder() {
#Override
public void configure() {
bind(conf).to(YourConfiguration.class);
// bind other classes
}
});
// register resources
}

hk2: why bind(X.class).to(X.class)

I am learning Java, but found the following piece of code. I am confused. What is bind(X.class).to(X.class); for?
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class ApplicationBinder extends AbstractBinder {
#Override
protected void configure() {
bind(X.class).to(X.class);
}
}
Thanks
You're configuring how you want your services to be discovered in the DI (dependency injection) system. bind(Service).to(Contract) is basically saying that you want to provide the Service as an injectable service, and want to "advertise" it as Contract. By "advertise", I mean what you want to be able to inject it as. For instance Service can be UserRepositoryImpl, while Contract can be UserRepository (interface). With this you would only be able #Inject UserRepository as that's what you advertise. The benefit of this is all the benefits that come with programming to an interface.
Example
interface UserRepository {
List<User> findAll();
}
class UserRepositoryImpl implements UserRepository {
#Override
public List<User> findAll() {
return Arrays.asList(new User("username"));
}
}
#Path("users")
class UserResource {
#Inject
private UserRepository repository;
#GET
public List<User> getUsers() {
return repository.findAll();
}
}
class JerseyApp extends ResourceConfig {
public JerseyApp() {
register(UserResource.class);
register(new AbstractBinder() {
#Override
public void configure() {
bind(UserRepositoryImpl.class)
.to(UserRepository.class);
}
});
}
}
Here the UserRepository is injected into the UserResource. When the DI system injects it, it will actually be the UserRepositoryImpl instance.
By doing that you are actually binding a new contract to a service.
bind(Service.class).to(Contract.class);
OR (binding a new contract to a service in Singleton)
bind(Service.class).to(Contract.class)..in(Singleton.class);

Inject dependencies into ServletContextListener using HK2 in Jersey

In order to initialize the application at startup, I extended ServletContextListener:
#WebListener
public class MyServletContextListener implements javax.servlet.ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
... initialization code here
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
Now I want to extract the initialization logic into a standalone StartupManager class, and delegate to this class from MyServletContextListener:
public class StartupManager {
public void performStartup() {
... initialization code here
}
}
I tried to inject StartupManager into ServletContextListener by simply adding #Inject annotation:
#WebListener
public class MyServletContextListener implements javax.servlet.ServletContextListener {
#Inject StartupManager mStartupManager;
#Override
public void contextInitialized(ServletContextEvent sce) {
mStartupManager.performStartup();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
This did not work - the reference is null when contextInitialized(ServletContextEvent ) is called.
Then I thought that I might have to register a binder:
#ApplicationPath("")
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new DependencyInjectionBinder());
}
}
public class DependencyInjectionBinder extends AbstractBinder {
#Override
protected void configure() {
bind(StartupManager.class).to(StartupManager.class);
}
}
This did not work either.
My question is how can I perform injection of dependencies into ServletContextListener? Preferably constructor injection, but field injection will also be alright.
It's not going to work, as the servlet listener and Jersey are not linked to the same system. As an alternative, you can use Jersey's Event Listeners. You can implement an ApplicationEventListener where you would be able to initialization and clean up in the same way you would in the servlet listener. You would be able to inject your services into Jersey's listener.

How to inject a bean into custom argument resolver?

Hello i use spring boot 1.3.2 version. I have a custom argument resolver which's name is ActiveCustomerArgumentResolver. Everything is great, resolveArgument method works fine but i can't initialize my service component which is of my custom arg. resolver. Is there a problem with lifecycle process? Here is my code:
import org.springframework.beans.factory.annotation.Autowired;
//other import statements
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {
#Autowired
private CustomerService customerService;
#Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(ActiveCustomer.class) && parameter.getParameterType().equals(Customer.class))
return true;
else
return false;
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
Principal userPrincipal = webRequest.getUserPrincipal();
if (userPrincipal != null) {
Long customerId = Long.parseLong(userPrincipal.getName());
return customerService.getCustomerById(customerId).orNull(); //customerService is still NULL here, it keeps me getting NullPointerEx.
} else {
throw new IllegalArgumentException("No user principal is associated with the current request, yet parameter is annotated with #ActiveUser");
}
}
}
Let the Spring create the resolver for you by making it a Component:
#Component
public class ActiveCustomerArgumentResolver implements HandlerMethodArgumentResolver {...}
Then inject the resolver into your WebConfig instead of simply using the new, like following:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired private ActiveCustomerArgumentResolver activeCustomerArgumentResolver;
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver);
}
}
This is how i've solved the problem, not a generic one but helps me a lot:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(Application.class);
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(activeCustomerArgumentResolver());
}
#Bean
public ActiveCustomerArgumentResolver activeCustomerArgumentResolver() {
return new ActiveCustomerArgumentResolver();
}
}

Resources