Spring How casting dynamic type extends of Interface - spring

I begening to work with Spring Boot after 14 years in C# and litle issue over to be dificult now.
with activity:
#Component
public class GetBillingUserStatusActivity implements ITask<BillingStatus,
String>, ITasks {
#Override
public BillingStatus Execute(String firstInput) {
return BillingStatus.active;
}}
I have wokflow class that would execute the precedent activity like this:
#Component
public class CreateUserTokenWorkflow {
#Autowired
private ITaskResolver taskResolver;
#Autowired
private GetBillingUserStatusActivity getBillingUserStatusActivity;
public String CreateUserToken() {
var task = taskResolver.ResolveTask(getBillingUserStatusActivity);
BillingStatus response = task.Execute("test");
return response.toString();
}}
and my probleme is now on my TaskResolver class
#Component("taskResolver")
public class TaskResolver implements ITaskResolver {
#Override
public <T extends ITasks> T ResolveTask(ITasks t) {
T type = (T) GenericTypeResolver.resolveType(t.getClass(), t.getClass());
return type;
}
}
T type = (T) GenericTypeResolver.resolveType(t.getClass(), t.getClass()) => casting to ITask failed
ErrorMessage:
class java.lang.Class cannot be cast to class com.consumer.workflow.manager.ITasks (java.lang.Class is in module java.base of loader 'bootstrap'; com.consumer.workflow.manager.ITasks is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader #211f766)

Related

How to autowire a method of a generic service

I have a Service class that has a generic type and a setController method that is based on the same generic type. the generic type of the servic object is only known at the time of declaration.
The problem is now when i define a ControllerImpl where the generic type is defined the #Autowired method of setController does not use that component.
Has somebody an idea how to fix it and keep the ServiceImpl generic. (it would work when i define the typ in ServiceImpl as well).
The following example show the problem i'm facing with:
#SpringBootTest
#ActiveProfiles("local")
public class AccessTest {
#Autowired
private ServiceA<BeanA> service;
#Test
void test(){
Assertions.assertNotNull(service.controller);
}
interface ValueGetter{
}
static class BeanA implements ValueGetter{
}
static class AbstractService<B extends ValueGetter>{
Controller<B> controller;
#Autowired
void setController(#Nullable Controller<B> controller){
this.controller = controller;
}
}
interface Controller<B extends ValueGetter>{
void doSomething(B value);
}
//not inner class
#Service
public class ServiceA<B extends AccessTest.ValueGetter> extends AccessTest.AbstractService<B> {
}
//not inner class
#Component
public class ControllerImpl implements AccessTest.Controller<AccessTest.BeanA> {
#Override
public void doSomething(final AccessTest.BeanA value) {
}
}
}

Spring Bean Factory Configuration passing input parameter

I'm trying to create a BeanFactory called TaskBeanFactory that I can Autowire into another prototype class that's running on a thread. I want a different instance of a bean returned by the Factory based on a taskName that i want to pass into it but when i start the application i get a null pointer exception because the taskName is null. I had a look at this article but i'm confused about how I should configure the Factory and then pass in the taskName.
The Factory:
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.stereotype.Component;
#Data
#Component
#NoArgsConstructor
public class TaskBeanFactory extends AbstractFactoryBean<GenericTask>{
private TaskNameEnum taskName;
public TaskBeanFactory(TaskNameEnum taskName) {
setSingleton(false);
}
#Override
public Class<?> getObjectType() {
return GenericTask.class;
}
#Override
protected GenericTask createInstance() throws Exception {
switch (taskName) {
case FILE_OPERATION:
return new FileTask();
case DATA_OPERATION:
return new DataTask();
default:
return new GenericTask();
}
}
}
The classes used by the Factory:
#Data
public class GenericTask {
private String idTask;
public void executeTask(Work work) {};
}
#Component
#Scope(value="prototype")
public class FileTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
#Component
#Scope(value="prototype")
public class DataTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
and the thread that's calling the Factory:
#Slf4j
#Data
#Scope("prototype")
#Component
public class WorkerThread implements Runnable {
#Autowired
private TaskBeanFactory taskBeanFactory;
#Autowired
private DataService dataService;
#Override
public void run() {
//iterate a Map of taskIds from the dataService
taskBeanFactory.setTaskName(TaskNameEnum.valueOf(taskEntry.getKey()));
GenericTask genericTask = taskBeanFactory.getObject();
//expecting genericTask to be of Type FileTask if called with one Key
//or of Type DataTask if called with another
}
}
}

Spring autowire trouble with generic parameter

I try to use generic parameter in autowire but it doesn't work. My goal is to create a generic JSON controller with Spring 4.1.3 and Hibernate
I have a generic abstract class , and I use it to create service via a model objec(the same as my DAO) as generic parameter.
The code of my AbstractService
public interface IGenericService<T extends Serializable> extends IOperations <T>{}
public interface IOperations<T extends Serializable> {
T findOne(final long id);
List<T> findAll();
void create(final T entity);
T update(final T entity);
void delete(final T entity);
void deleteById(final long entityId);
List<T> findByField(String field, String value);
T save(final T entity);
}
//MyAbstractService (generic service)
public abstract class AbstractService<T extends Serializable> implements
IGenericService<T> {
public static final Logger logger = LoggerFactory
.getLogger(AbstractService.class);
public AbstractService(){}
...
#Override
#Transactional
public T update( T entity) {
logger.debug("public T update( T entity)");
return getDao().update(entity);
}
...
}
Now I create a SecuredUserService with this abstract service
#Transactional
#Component //(value = "userService")
#Qualifier("userService")
public class UserService extends AbstractService<SecuredUser> implements
IUserService {
// I override the method upate of the abstract service
#Override
#Transactional
public SecuredUser update(SecuredUser user){
... // password encoding for example
}
}
public interface IUserService extends IGenericService<SecuredUser> {
T findOne(final long id);
...
}
In my JUnit test I made autowire with this code :
#Autowire
IGenericService<SecuredUser> userGenericService;
Or
#Autowire
IUserService userService;
At this point every thing is ok, I use the overrided method of userService and not those of abstractService. I pass my Junit Test. An I create a package.
Now I want to make generic spring mvc controller to handle common Json request GET/PUT/DELETE/POST :
//Generic Controller
public abstract class GenericSecuredController <MODEL extends Serializable> extends CommonSecuredController {
/**
* spring generic service retrieve by MODEL class type
*/
#Autowired
private IGenericService <MODEL> genericService;
/**
* Spring generic URI retrieve by MODEL class type
*/
#Autowired
private IGenericURI<MODEL> genericURI ;
...
}
// interface to manage URI in a generic way
public interface IGenericURI<MODEL extends Serializable> {
// root for the controller
public static String CONTROLLER_MAPPING="" ;
// path to the file system
public static String PATH_MAPPING = "";
// key to retrieve data in path
public static String PATH="{id}";
// Json REST SERVICE MappedUri
public static String JSON_DUMMY = "/dummy";
public static String JSON_GET = "/" + PATH;
public static String JSON_GET_ALL = "";
public static String JSON_CREATE = "";
public static String JSON_DELETE = "/" + PATH;
public static String JSON_UPDATE = "/" + PATH;
public static String HTML_VIEW = "/{view}.view.html";
public String getControllerMapping() ;
public String getPathMapping() ;
}
// The specific URI for the SecuredUser model object
#Component
public class SecuredUserURI implements Serializable, IGenericURI<SecuredUser> {
public static final String CONTROLLER_MAPPING = "/user";
public static final String PATH_MAPPING = "user";
public String getControllerMapping() {
return CONTROLLER_MAPPING;
}
public String getPathMapping() {
return PATH_MAPPING;
}
}
Now I could create a specific controller for SecuredUser like this :
public class UserController extends GenericSecuredController<SecuredUser> {
/**
* creator to set Class type for the GenericSecuredController<MODEL>
*/
public UserController() {
super(SecuredUser.class);
}
}
The problem appear at this point. The autowire of the
IGenericURI<MODEL>
work fine, but the autowiring with
IGenericService <MODEL> genericService;
doesn't use the overrided specific method of the userService but the abstract method with common behaviour!!!
So my question is :
Is it possible to autowire bean with generic parameter like in my example.
Maybe there is to many level for Spring autowiring .
Other information :
As workaround, I try to pass the userService as parameter of the contoller but, the same behaviour: the generic service use the abstract method.
UPDATE : If I autowire IGenericService genericService in the UserController and create a new handler, the specific service is call.
Thanks

Spring 4 bean autowiring with generics

I am using Spring 4 via Spring Boot 1.1.8 and have created a class to cache some data. The class relies on generics to work but I'm having trouble with Spring and autowiring this class as a bean in another service.
I get errors like this:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [orm.repository.BaseRepository] is defined: expected single matching bean but found 2: dataTypeRepository,propertyNameRepository
The class in question:
/**
* The purpose of this class is to get data from a cache backed by a database
* and if it does not exist to create it and insert into the database.
*/
#Service
public class CacheByName<TRepo extends BaseRepository, TItem extends BaseWithName> {
private final TRepo repo;
private final Class<TItem> itemClass;
private final Map<String, TItem> itemsCache; // TODO: change to better caching strategy
#Autowired
public CacheByName(TRepo repo, Class<TItem> itemClass) {
this.repo = repo;
this.itemClass = itemClass;
itemsCache = new HashMap();
}
public TItem getCreateItem(String name) {
TItem item = null;
if(itemsCache.containsKey(name)) {
item = itemsCache.get(name);
} else {
// try and load from db
item = (TItem) repo.findByName(name);
if(item == null) {
try {
item = itemClass.newInstance();
item.setName(name);
repo.saveAndFlush(item);
} catch (InstantiationException | IllegalAccessException ex) {
// TODO: log and handle better
return null;
}
}
itemsCache.put(name, item);
}
return item;
}
}
The class BaseRepository extends JpaRepository as follows. Other actual repositories extend this one.
#NoRepositoryBean
public interface BaseRepository<T extends Object, ID extends Serializable> extends JpaRepository<T, ID> {
public T findByName(String name);
}
The class BaseWithName is a MappedSuperclass that defines a name property and getters/setters for it. Other more concrete entity classes extend this.
I am trying to inject the CacheByName class into another service like the following. Note that I am defining the actual repository and entity class as generics in the constructor:
#Service
public class DataImporter extends BaseImporter {
private static final Logger log = LoggerFactory.getLogger(PropertyImporter.class);
private final PropertyNameRepository propertyNameRepo;
private final CacheByName<DataTypeRepository, DataType> dataTypeCache;
#Autowired
public PropertyImporter(RestTemplate restTemplateD5,
CacheByName<DataTypeRepository, DataType> dataTypeCache) {
super(restTemplateD5);
this.dataTypeCache = dataTypeCache;
}
.
.
.
}
My AppConfig.java looks like the following:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class AppConfig {
#Value("${username}")
private String username;
#Value("${password}")
private String password;
#Bean
public RestTemplate restTemplateD5() {
return RestTemplateFactory.createWithHttpBasicAuth(username, password);
}
}
I haven't been able to find much information about creating beans that use generics. I suspect I need to create another #Bean definition in my AppConfig but I wasn't able to implement anything that worked.
As BaseRepository is also a generic type, I think you missed to add the generic type there. That should help Spring to find a proper bean to inject:
public class CacheByName<TRepo extends BaseRepository<TItem, ? extends Serializable>, TItem extends BaseWithName>
This should also make the cast no longer needed:
item = repo.findByName(name);

Extend class from another extended Parametrized Generic class

I have a genericService support classes ( genericService, GenericServiceImpl, GenericDao,GenericHibernateDao) for generic service,dao layer.
Normaly it works fine, When i extend any type parametrized service from abstract generic services.When i extend this extended services in different service it gives:
Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
error.
My GEnericHibernateDao:
#Transactional
public abstract class GenericHibernateDaoSupport extends HibernateDaoSupport implements GenericDaoTemplate
{
private Class type;
public GenericHibernateDaoSupport() {
this.type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
#Autowired
#Qualifier(value = "productSessionFactory")
public void bindSessionFactory(SessionFactory sessionFactory)
{
setSessionFactory(sessionFactory);
}
public Class<T> getType() {
return type;
}
public void setType(Class<T> type) {
this.type = type;
}
protected Session getCurrentSession() {
return getHibernateTemplate().getSessionFactory().getCurrentSession();
}
public ID persist(T newInstance) {
return (ID) getHibernateTemplate().save(newInstance);
}
public void update(T transientObject) {
getHibernateTemplate().update(transientObject);
}
Here is my standart extended HibernateDao class:
#Repository
public class StandadHibernateDao extends standardHibernateDaoSupport<Standard, Long> implements StandardDao {
Above extend operation works fine, But when i tried something like this:
#Repository
public class ExtendStandardGatewayHibernateDao extends StandadHibernateDao implements ExtendedStandardDao {
it throws :
Caused by: java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType.
Do you have any idea?

Resources