Pass generics class to an annotation - java

In the testing framework I'm using, there is a Precondition annotation that takes a class and calls call() method in it at runtime.
Core Modules of the Testing Framework:
Precondition Annotation
public #interface Precondition {
Class<? extends Scriptlet<? extends Context>> scriptlet();
String value() default Constants.EMPTY_STRING;
}
Scriptlet
public interface Scriptlet<V> extends Callable<V> {
}
Context
public interface Context {
}
If I pass the following precondition class to Precondition annotation there will be no errors.
CommonContextInit precondition class
class CommonContextInit implements Scriptlet<DataModel>{
CommonContextInit(Object script,String value){
}
#override
public DataModel call() throws Exception {
return null;
}
}
Script
#Precondition(scriptlet=CommonContextInit.class)
But I want to make CommonContextInit a generic where user can pass the type of the scriptlet so I changed the CommonContextInit class as follows
CommonContextInit precondition class
class CommonContextInit<T extends Context> implements Scriptlet<T>{
CommonContextInit(Object script,String value){
}
#override
public T call() throws Exception {
return null;
}
}
But the issue here is I have no idea how to pass the class to Precondition. The following I have tried but looks like it's totally invalid syntax.
// ERROR: The annotation #Precondition is disallowed for this location.
#Precondition(scriptlet=CommonContextInit<DataModel>.class)
How should I pass the Class of CommonContextInit<DataModel> to Precondition?

To extend my comment you shall have something like
class DataModelInstance extends CommonContextInit<DataModel> implements Scriptlet<DataModel>{
DataModelInstance(Object script,String value){
super(script, value);
}
//other overrides
}
and then call #Precondition(scriptlet=DataModelInstance.class)

Related

How to create an interface for any service in Spring Boot

I want all the Service classes in my backend to have CRUD methods.
For that purpose, I thought of creating an interface:
public interface ServiceCRUD {
public Object save(Object object);
...
}
And then on my service, implement it:
#Service
public class SampleService implements ServiceCRUD {
#Autowired
private SampleRepository repository;
#Override
public Sample save(Sample sample) {
return repository.save(sample);
}
...
}
I haven't touched Java in a while, but if I recall correctly, every object extend Object, so why is it that I can't use Object to have the service accept all the entities I might have?
Best regards
You can achieve such scenario using Generics
interface ServiceCRUD {
public < E > void save(E object);
}
class Sample {
private String name = "Joe";
#Override
public String toString() {
return "hello"+name;
}
}
class SampleService implements ServiceCRUD {
#Override
public < Sample > void save(Sample sample) {
System.out.print(sample.toString());
}
}
public class Main {
public static void main(String[] args) {
new SampleService().save(new Sample());
}
}
This is just an example ,you can extend it as per your use case.
See working here
Your interface declares that one is possible to save Object, i. e. any object. But your implementation declares that it cat get only Sample, that's why you get compilation error.
You should go with genetic and let each implementation to declare what kind of object it can deal with. I strongly suggest to have a look at spring data project. It will save you a lot if time

Enum-based State Machine, Dagger2 injection of state handler

I'm trying to create a state machine that uses an enum to specify the states. Since there are a lot of states and the logic implemented by each is pretty complex, I wanted to associate each state with a state handler that will be defined in a different class. Each state handler would implement a common interface (or extend a common abstract class), but each one might have its own set of injected dependencies that the others might not need. So far, something like this....
StateHandler Interface:
public interface StateHandler {
void onActivation();
void onDeactivation();
}
Example StateHandlers:
#Singleton
public class DefaultStateHandler implements StateHandler {
#Inject
public DefaultStateHandler(SomeDependency someDependency) {...}
/** implement onActivation, onDeactivation and state specific logic **/
}
#Singleton
public class OtherStateHandler implements StateHandler {
#Inject
public OtherStateHandler(SomeOtherDependency someOtherDependency) {...}
/** implement onActivation, onDeactivation and state specific logic **/
}
StateManager implementation:
#Singleton
public class StateManager {
private StateType stateType = StateType.DEFAULT;
#Inject
public StateManager() { }
public void changeState(StateType newStateType) {
if (stateType != newStateType) {
stateType.getStateHandler().onDeactivation();
stateType = newStateType;
stateType.getStateHandler().onActivation();
}
}
}
Enum Definition:
public enum StateType {
DEFAULT (/* not sure what to do here */),
OTHER_STATE (...);
private StateHandler stateHandlerInstance;
public getStateHandler { return stateHandlerInstance; }
StateType(/* not sure what to do here */) {
/* assign stateHandlerInstance */
}
}
What I'm trying to figure out is... how do I inject the specific instances of the state handlers when declaring their associated enums? Or if that isn't possible, is there another way of specifying the state handler class for each enum, and then (either in the constructor or by the time its first needed), get the associated state handler instance?
I was originally thinking I needed to inject the state's handler instance into the state enum definition. However, since injection requires public constructors and enums use private constructors, I don't think that approach is feasible.
As mentioned in the comments above, the solution was to use map multibindings.
First the enum StateType is simplified:
public enum StateType {
DEFAULT, OTHER_STATE
}
Now we need a dagger MapKey interface specific to this enum type:
#MapKey
#interface StateTypeKey {
StateType value();
}
Next we need a dagger module which will have provider functions for each StateType / StateHandler combination:
#Module
public StateTypeHandlersModule {
// #Provides #IntoMap // Syntax for dagger >= 2.9
#Provides(type = Provides.Type.MAP) // Syntax for dagger <= 2.8
#StateTypeKey(StateType.DEFAULT)
StateHandler provideDefaultStateHandler(DefaultStateHandler handler) {
return handler;
}
// #Provides #IntoMap // Syntax for dagger >= 2.9
#Provides(type = Provides.Type.MAP) // Syntax for dagger <= 2.8
#StateTypeKey(StateType.OTHER_STATE)
StateHandler provideOtherStateHandler(OtherStateHandler handler) {
return handler;
}
}
Its unfortunately a lot of boilerplate code, which is why I'm using a separate module just for the handlers, and including that in the higher-level state machine module. Note that if you declare two provider functions with the same StateTypeKey, the second handler is ultimately available in the injected map.
Finally, we can inject the Map<StateType, StateHandler> into the StateManager:
#Singleton
public class StateManager {
private Map<StateType, StateHandler> stateHandlerMap;
private StateType stateType = StateType.DEFAULT;
#Inject
public StateManager(Map<StateType, StateHandler> stateHandlerMap) {
this.stateHandlerMap = stateHandlerMap;
}
public void changeState(StateType newStateType) {
if (stateType != newStateType) {
stateHandlerMap.get(stateType).onDeactivation();
stateType = newStateType;
stateHandlerMap.get(stateType).onActivation();
}
}
}

Spring & Java Generics - Getting instance of one service in abstract controller

I have a typed service interface:
public interface BaseArticleService<T extends BaseArticle> {
T getById(Long id);
}
And have two interfaces which extends it:
public interface AccArticleService extends BaseArticleService<AccArticle> {
}
public interface ExpArticleService extends BaseArticleService<ExpArticle> {
Long getCount();
}
Then I have a similar architecture for controllers:
public abstract class BaseArticleController<T extends BaseArticle> {
#Autowired
BaseArticleService<T> baseArticleService;
}
And:
#Controller
#RequestMapping(value = "/exp/articles")
public class ExpArticleController extends BaseArticleController<ExpArticle> {
}
#Controller
#RequestMapping(value = "/acc/articles")
public class AccArticleController extends BaseArticleController<AccArticle> {
}
Now if I want to get my ExpArticleService instance of the BaseArticleService that is injected in my BaseController, how can I achieve this?
If I do this:
public BaseArticleService<ExpArticle> getExpArticleService() {
return super.baseArticleService;
}
then I cannot call my getCount() method like getExpArticleService().getCount()
And cannot do this neither:
public ExpArticleService getExpArticleService() {
return super.baseArticuloService;
}
So what's the solution? Maybe inject another ExpArticleService in my ExpArticleController?
When using DI you are by definition relying on the interface. If you inject BaseArticleService then the only method available to you are the methods defined in this interface. In your case T getById(Long id);
Your way is not a bad way, you have a generic super class which allow you to initialise the interface with different parameters very easily, but you shouldn't expect to explicitely get any of the specific implementation. (maybe you could check and cast but it is not clean there).
As already stated you should extract the specific implementation in another class and directly inject this one in the controller requiring it. Keep your generic though, it is a nice way to to handle all the parametrized methods that will be shared between your controllers

javaslang List.of() on cdi Instance

I have multiple class with a Qualifier that I created:
#ServiceComponent(restPath = "/trucks")
public class TruckService {
}
#ServiceComponent(restPath = "/cars")
public class CarService {
}
here is the Qualifier (not important for the question)
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, FIELD})
public #interface ServiceComponent {
public boolean exposeAsRest() default true;
#Nonbinding public String restPath() default "";
#Nonbinding public String restGetPrefix() default "get,find,all";
#Nonbinding public String restPostPrefix() default "create,new,post";
}
in another class, I inject those instance using javax.enterprise.inject.Instance<>
class SomeConfigurationClasss {
#Inject
#ServiceComponent()
Instance<Object> _restComponents;
#Override
public void iterate() throws Exception {
//iterate
for(Object obj : _restComponents){
somefuncion(obj);
}
//List.of(_restComponents)
//.flatMap(obj -> somefuncion(obj));
}
}
if I execute the "normal" iteration (for...) I get the Object (TruckService or CarService) given as parameter to the somefunction().
but if I use javaslang's List.of(...) I get the Instance itself. Which I think it's the expected behavior
Is there a possibility to use List.of on a Instance that can contain one or multiple bean (depending on the injection binding). (I already try to call iterator(), select() on the Instance)
Instance<T> extends Iterable<T> so you should use List#ofAll(Iterable)

Dagger: class could not be bound with key

I'm trying for first time dagger and, after configuring the module, I get an error I don't understand.
My code is:
public class Parrot {
private Language language;
#Inject
public Parrot(Language language) {
this.language = language;
}
public void sayHello() {
System.out.println(language.getSalute());
}
}
public interface Language {
public String getSalute();
}
public class EnglishLanguage implements Language {
#Override
public String getSalute() {
return "Hello!";
}
}
My module is
#Module(
injects = Language.class
)
public class PetShopModule {
#Provides Parrot provideParrot(Parrot parrot){
return parrot;
}
}
And in the code I use it this way
EnglishLanguage lang=SystemApp.getSystemLanguage();
ObjectGraph objectGraph = ObjectGraph.create(new PetShopModule());
objectGraph.inject(myLanguage);
Parrot parrot = objectGraph.get(Parrot.class);
The compiler complains with:
error: com.example.petshop.Language could not be bound
with key com.example.petshop.Language required by com.example.petshop.PetShopModule
for com.example.petshop.PetShopModule
What do I am doing wrong?
Note: This is only a simplified example, in the real code the EnglishLanguage is a system class, and I can't modify nor create it, just get a reference
Instead of commenting on what you're doing wrong, let's give the correct example, and explain what's happening there.
This snippet is perfect, and stays as it is:
public class Parrot {
private Language language;
#Inject
public Parrot(Language language) {
this.language = language;
}
public void sayHello() {
System.out.println(language.getSalute());
}
}
public interface Language {
public String getSalute();
}
public class EnglishLanguage implements Language {
#Override
public String getSalute() {
return "Hello!";
}
}
With the #Inject annotation on the Parrot constructor, you're telling Dagger, "Whenever I request an instance of Parrot, use this constructor to instantiate it".
Dagger sees that Parrot needs an instance of the Language interface, and tries to find a way to retrieve it. However, since Language is an interface, Dagger needs to know which concrete type to use.
Since you cannot edit the EnglishLanguage class, you'll need to write a #Provider method for it:
#Module
public class PetshopModule {
#Provides
Language provideLanguage() {
return SystemApp.getSystemLanguage();
}
}
Now, you should be able to get an instance of Parrot out of your ObjectGraph, like this:
ObjectGraph graph = ObjectGraph.create(new PetshopModule());
Parrot parrot = graph.get(Parrot.class);
I have not tested this, bit this should work.
If you were able to modify the EnglishLanguage class, you could do the following. Annotate the EnglishLanguage constructor with #Inject, and request an EnglishLanguage instance in the Language provider method:
public class EnglishLanguage implements Language {
#Inject
public EnglishLanguage() {
}
#Override
public String getSalute() {
return "Hello!";
}
}
#Module
public class PetshopModule {
#Provides
Language provideLanguage(EnglishLanguage language) {
return language;
}
}
In this case, Dagger looks for the Language provider method, and instantiates an EnglishLanguage to return.
The existing answer is perfect, but didn't solve my obscure case.
If you have a base Activity with a couple of DI helper classes, make sure your Activity that extends this does so properly!
Base Activity class:
private fun initializeObjectGraph() {
activityObjectGraph = (application as App).getObjectGraph()
.plus(ActivityModule(this))
.plus(*getAdditionalModulesForObjectGraph())
}
protected open fun getAdditionalModulesForObjectGraph() = arrayOf<Any>()
abstract fun injectDependencies(activityObjectGraph: ObjectGraph): LifecycleReceiver
Overrides needed in extension Activity:
override fun injectDependencies(activityObjectGraph: ObjectGraph): LifecycleReceiver {
activityObjectGraph.plus(MyModule(this)).inject(this)
return DummyLifecycleReceiver
}
override fun getAdditionalModulesForObjectGraph(): Array<Any> = arrayOf(MyModule(this))

Categories