Inject a Map of Interface Implementations Keyed by Enum Values - java

I am using Java 8 with a Play framework. My goal is to inject a map whose keys are enum values and values are implementations of a specific interface.
Here is my enum:
public enum Service {
HTML("html"), TEXT("txt");
private String serviceId;
Service(String serviceId) { this.serviceId = serviceId; }
}
I have Executable interface
public interface Executable { void execute(); }
and two classes that implement it:
public class HtmlWorker implements Executable { ... }
public class TextWorker implements Executable { ... }
I would like to be able to inject Map<Service, Executable> serviceMap so I can have access to a specific implementation using a Service key:
public class Processor {
#Inject
Map<Service, Executable> serviceMap;
public void doStuff() {
Executable htmlService = this.serviceMap.get(Service.HTML);
Executable textService = this.serviceMap.get(Service.TEXT);
// do more stuff
}
}
I added bindings to the module class:
public class AppModule extends AbstractModule {
#Override
protected void configure() {
MapBinder<Service, Executable> serviceBinder = MapBinder
.newMapBinder(binder(), Service.class, Executable.class);
serviceBinder.addBinding(Service.HtmlService).to(HtmlWorker.class);
serviceBinder.addBinding(Service.TextService).to(TextWorker.class);
}
The problem is that serviceMap is never injected and it is always null inside Processor. What am I missing?

According to the official MapBinder documentation the MapBinder.addBinding should take the map's key.
As far as concerning your provided example what about changing AbstractModule's code from:
serviceBinder.addBinding(Service.HtmlService).to(HtmlWorker.class);
serviceBinder.addBinding(Service.TextService).to(TextWorker.class);
to
serviceBinder.addBinding(Service.HTML).to(HtmlWorker.class); // <-- see the enum constant here?
serviceBinder.addBinding(Service.TEXT).to(TextWorker.class);
Anyway I don't know where the class Service.HtmlService in your example comes from since you didn't state it anywhere.

Related

Inject custom service to spring boot test

I have an interface I and 2+ of its implementations/services (ex. I1, I2)
public interface I {
void handle();
Network getNetwork();
}
and I have another one service (ex. IHolder) that is injecting the list of all services that implementing I and is putting it to its inner private map field where key is some unique Enum (interface has public method that returns one) and value the implementation itself.
public class IHolder {
private final Map<Network, I> iByNetwork = new HashMap<>();
public IHolder(List<I> is) {
for (I i : is) {
register(i.getNetwork(), i);
}
}
private void register(Network network, I i) {
this.iByNetwork.put(network, i);
}
public I getI(Network network) {
return iByNetwork.get(network);
}
}
Now I want to inject IHolder to my test class to use his map filled with those services
So basically annotate the IHolder class with #Service, but you won't be able to use it's private field (map), at least not in a simple way...
edit
Try to add these annotations to your test classes:
#RunWith(SpringRunner.class)
#SpringBootTest

Is it possible to make a JUnit5 Extension implement an interface that is fulfilled by the extended class?

I would like to write a JUnit5 Extension that extends my test class,
#ExtendWith(MyExtension.class)
public class MyTestClass {
#Test myTest1() {}
#Test myTest2() {}
// ...
}
However, my test class also implements a certain interface, so it looks more like this:
public interface SomeInterface {
SomeClient getSomeClient();
SomeClient getSomeClientAsAdministrator();
}
#ExtendWith(MyExtension.class)
public class MyTestClass implements SomeInterface {
#Test myTest1() {}
#Test myTest2() {}
// ...
SomeClient getSomeClient() {
// ...
}
SomeClient getSomeClientAsAdministrator() {
// ...
}
}
No mysteries so far.
But now, I want those interface implementations to be available to the extension as well, e.g.
public class MyExtension implements BeforeEachCallback, SomeInterface
{
#Override
public void beforeAll(ExtensionContext extensionContext) {
// be able to use getSomeClient();
}
}
How can I set up my classes to achieve this? (Or, what is the inherent flaw or code smell against doing this?)
You need to use the #RegisterExtension annotation which allows you to construct your extension instance manually.
When an extension is registered declaratively via #ExtendWith, it can
typically only be configured via annotations. In contrast, when an
extension is registered via #RegisterExtension, it can be configured
programmatically — for example, in order to pass arguments to the
extension’s constructor, a static factory method, or a builder API.
It sounds like SomeClient is provided from elsewhere (a DI like Spring perhaps) but you need it in MyExtension. Assuming this scenario, you can start with something like:
#ExtendWith(SpringExtension.class)
public class MyTestClass {
#Autowired SomeClient someClient;
#RegisterExtension
MyExtension myExtension = new MyExtension(someClient);
}
One way to achieve that is to use getTestInstance() on the context object:
public class MyExtension implements BeforeEachCallback {
#Override
public void beforeEach(ExtensionContext context) throws Exception {
context.getTestInstance().ifPresent(instance -> {
if (instance instanceof SomeInterface) {
SomeInterface some = (SomeInterface) instance;
System.out.println(some.getSomeClient());
}
});
}
}
What you can see here is two things:
There might not be a test instance object, e.g. in a BeforeAllCallback because test instances are usually created per test.
A cast is required. That means you should check if your test instance really does implement SomeInterface
Having said that, I'm not really sure why you'd want to go down that rather complicated route. What's MyExtension supposed to abstract from?

How to inject an interface implementation based on annotations at runtime using Google Guice

I have the following scenario:
public interface ServiceClientAdapter {
SomeData getSomeData()
}
#LegacyServiceClientAdapter
public class MyLegacyServiceClientAdapterImpl implements ServiceClientAdapter {
public SomeData getSomeData() {
// implementation
}
}
#NewServiceClientAdapter
public class MyNewServiceClientAdapterImpl implements ServiceClientAdapter {
public SomeData getSomeData() {
// implementation
}
}
public class BusinessLogic {
#Inject
private ServiceClientAdapter serviceClientAdapter;
}
LegacyServiceClientAdapter and NewServiceClientAdapter are custom annotations.
The implementation for the serviceClientAdapter field will be determined at runtime by whether the user has been migrated from the legacy to the new service or not.
What is the best way to accomplish this dependency injection using Google Guice?
Take into account that different BusinessLogic classes will exist, each with their own (different) ServiceClientAdapter-like interface and corresponding legacy and new implementation classes.
Ideally this should be done with a piece of framework code that can be used across all use cases.
I'm going to assume that the result of your LDAP call can be represented as a string, let's say "legacy" or "new". If not, hopefully you should still be able to adapt this example.
In your module, use a MapBinder:
public class BusinessLogicModule {
#Override
protected void configure() {
// create empty map binder
MapBinder<String, ServiceClientAdapter> mapBinder =
MapBinder.newMapBinder(
binder(), String.class, ServiceClientAdapter.class);
// bind different impls, keyed by descriptive strings
mapBinder.addBinding("legacy")
.to(MyLegacyServiceClientAdapterImpl.class);
mapBinder.addBinding("new")
.to(MyNewServiceClientAdapterImpl.class);
}
}
Now you can inject a map of instances (or a map of providers of instances if you need to keep creating new instances) into your main class and use the string discovered at runtime to control which kind of instance you get.
public class BusinessLogic {
#Inject
private ServiceClientAdapter serviceClientAdapter;
#Inject
private Map<String, ServiceClientAdapter> mapBinder;
public void setupAndUseClientAdapter() {
String userType = getUserTypeFromLdapServer();
serviceClientAdapter = mapBinder.get(userType);
if (serviceClientAdapter == null) {
throw new IllegalArgumentException(
"No service client adapter available for " +
userType + " user type.";
}
doStuffWithServiceClientAdapter();
}
}

How to properly access application state from JAX-RS

#Path("/test")
public class MyClass {
#GET
public Response response() {
// Generating some expensive object here.
}
Right now I load the data into arrays etc inside the "response" function, but I want to do it before the query is even made. This way, I want to avoid reloading the data every time a a query is made. How do I achieve this?
This depends on your framework. Are you using the reference implementation Jersey? Jersey comes bundled with HK2 automatically. Then you could add
#Path("/test")
public class MyClass {
#Inject
private MyState stateful;
// here comes your code...
}
to your resource. Of course, you would still need to configure what to inject into this field. With HK2, you use an AbstractBinder for this purpose:
class MyBinder extends AbstractBinder {
private final MyState stateful;
public MyBinder (MyState stateful) {
this.stateful = stateful;
}
#Override
protected void configure() {
bind(stateful).to(MyState.class);
}
}
Finally, you need to add this binder on the application's setup. For this purpose, JAX-RS Application object can be queried for singletons. Simply add the required instance to the application such that it is returned by Application#getSingletons as here:
class MyJaxRSApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
return Collections.singletonSet(MyClass.class);
}
#Override
public Set<Object> getSingletons() {
return Collections.singletonSet(new MyBinder(new MyStateImpl()));
}
}
You can now run your application where MyStateImpl is always injected into MyClass.

Binding A HashMap Instance In Guice

I have a method that returns a Map():
private Map<String, Catalog> readCatalogFromXml(String xmlFile) {
// blah blah read XML via DOM
}
However, I need to bind the results of that call to a single instance to be injected into multiple classes. I've been looking over the docs for MapBinder but am confused. I'm probably missing something simple, can anyone assist?
Jason
Just implement a provider in your Module class:
public class ModuleImpl extends AbstractModule {
#Override
protected void configure() {
// bind whatever needed
}
#Singleton
#Provides
Map<String, Catalog> provideMap(#XMLFile String xmlFile) {
// create an instance of your class
return myClass.readCatalogFromXml(xmlFile);
}
}
If you just need the returned Map injected, I would ...
define a subclass "#Singleton public class MyInjectedMap extends HashMap { }"
bind a Provider < MyInjectedMap > to produce instances

Categories

Resources