How to controll the lifecycle of a custom scope in Spring - java

I have been trying to create a custom scope in Spring Framework, but I fail to understand how is the scope actually instantiated and destroyed.
Assuming I want to create an EventScope and I have a com.example.EventHandler.handleEvent(Event event) method that handles an incoming event.
How do I ensure that every Event starts/stops a scope when it is being handled?
If I have several EventScoped beans wired in, How do I make sure they are using the same scope instance during an event? It seems like a new EventScopeImpl would be created for every bean?
If I have to use a ThreadLocal or some other static context, what is even the point of using a custom scope when I can use the ContextHolder class directly?
My code is as follows?
EventScopeImpl.java
public class EventScopeImpl implements Scope {
private final Map<String, Object> scopedObjects
= Collections.synchronizedMap(new HashMap<String, Object>());
private final Map<String, Runnable> destructionCallbacks
= Collections.synchronizedMap(new HashMap<String, Runnable>());
#Override
public Object get(final String name, final ObjectFactory<?> objectFactory) {
if(!scopedObjects.containsKey(name)) {
scopedObjects.put(name, objectFactory.getObject());
}
return scopedObjects.get(name);
}
#Override
public Object remove(final String name) {
Optional.ofNullable(destructionCallbacks.remove(name)).ifPresent(Runnable::run);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(final String name, final Runnable callback) {
destructionCallbacks.put(name, callback);
}
#Override
public Object resolveContextualObject(final String key) {
return EventContextHolder.instance().getValue(key);
}
#Override
public String getConversationId() {
return null;
}
}
EventScope.java
#Qualifier
#Scope(value = "event", proxyMode = ScopedProxyMode.TARGET_CLASS)
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
public #interface EventScope{}
EventContextHolder.java
public class EventContextHolder{
#Getter
#Accessors(fluent = true)
public static final EventContextHolder instance = new EventContextHolder();
private final static ThreadLocal<Context> context = ThreadLocal.withInitial(Context::new);
private EventContextHolder(){}
public void setValue(final String key, final Object value){
context.get().data().put(key,value);
}
public Object getValue(final String key){
return context.get().data().get(key);
}
public void clear(){
context.get().data().clear();
}
private static class Context{
#Accessors(fluent = true)
#Getter
private final ConcurrentHashMap<String,Object> data = new ConcurrentHashMap<>();
}
}
EventAspect.java
#Aspect
#Component
public class EventAspect {
#Pointcut("execution(com.example.EventHandler.handleEvent(..))")
public void eventHandlerMethod() {};
#Around("eventHandlerMethod()")
public Object startMessageContext(ProceedingJoinPoint pjp) throws Throwable {
final Event event;
if(pjp.getArgs() > 0 && Event.class.isInstance(pjp.getArgs()[0]){
event = (Event)pjp.getArgs()[0];
}
#Cleanup("clear") EventContextHolder context = EventContextHolder.instance();
context.setValue("event",event);
Object retVal = pjp.proceed();
return retVal;
}
}
EventContext.java
#RequiredArgsConstructor
#Getter
#Component
#EventScope
#Lazy
public class EventContext {
#Lazy
#Value("#{event}")
private Event event;
}
EventScopeConfiguration.java
#Configuration
public class EventScopeConfiguration {
#Bean
public BeanFactoryPostProcessor eventScopeBeanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope(
"event", new EventScopeImpl());
}
}

Related

How to get the field returned by a method invocation

I am writing a custom SonarQube rule for java, where I want to check an object is created with an argument having a specific annotation.
the file i am testing against
class MyClass {
public void doSomething() {
final var v = new Dto();
new MyObject(v.value1()); // Compliant since value1 has #MyAnnotation
new MyObject(v.value2()); // Noncompliant
}
public static class MyObject {
private final String value;
public MyObject(String value) {
this.value = value;
}
}
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
}
public static class Dto {
#MyAnnotation
private String value1;
private String value2;
public String value1() {
return this.value1;
}
public String value2() {
return this.value2;
}
}
}
the check
public class MyObjectCheck extends IssuableSubscriptionVisitor {
#Override
public List<Kind> nodesToVisit() {
return Collections.singletonList(Kind.NEW_CLASS);
}
#Override
public void visitNode(Tree tree) {
NewClassTree ctor = (NewClassTree) tree;
if(!ctor.identifier().symbolType().name().contains("MyObject")) { //to change
return;
}
if(ctor.arguments().size() == 1) {
final ExpressionTree expressionTree = ctor.arguments().get(0);
if(expressionTree.is(Kind.METHOD_INVOCATION)) {
MethodInvocationTree methodInvocation = (MethodInvocationTree) expressionTree;
}
}
}
}
from the methodInvocation, I can manage to call methodSelect to have a MethodInvocationTree but then I can't figure how to go to the field returned by the method.
I had to make concession where I consider the class of the method invoked being a POJO or a java record. This way I was able to fetch the linked field and annotations :
String methodName = methodInvocationTree.symbol().name();
final Symbol.TypeSymbol methodClass = (Symbol.TypeSymbol) methodInvocationTree.symbol().owner();
final List<SymbolMetadata.AnnotationInstance> annotations = methodClass.lookupSymbols(methodName).iterator().next().metadata().annotations();

Abstract properties in an abstract class [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I have an abstract class with some configuration properties value which are set using #Value. I want to reuse the abstract class but with another set of configuration properties. The issue is that, those properties value have already been set in the abstract class and all of the concrete classes have inherited it.
I have thought about:
creating another set of config values in the abstract class, but that seems to be creating duplicate, but this is not quite extensible when in future there is yet a third set of config values.
changing the accessibly of the config value in the abstract class from private to protected and have the concrete class to override it. But I'm not sure this kind of overriding is good as it seems create confusion as to what is the actual config value.
create another abstract class which is similar as "AbstractService" but injecting the different set of config value using #Value. This also seems to create duplication.
public abstract class AbstractService {
#Value("${config1}")
private String config1;
#Value("${config2}")
private String config2;
public void serviceMethod() {
//using config1 and config 2 values
}
}
public class concreteServiceA extends AbstractService {
public void serviceA() {
// using serviceMethod in the abstract class
}
}
public class concreteServiceB extends AbstractService {
public void serviceB() {
// using serviceMethod in the abstract class
}
}
Would it be a good way if using constructor to pass the required parameters in the abstract class, and let the concrete classes to use constructor injection and #Value to set those values ? though if there are long list of config values this may not scale well.
public abstract class AbstractService {
private String config1;
private String config2;
public AbstractService(String config1, String config2) {
this.config1 = config1;
this.config2 = config2;
}
public void serviceMethod() {
//using config1 and config2 values
}
}
public concreteServiceA extends AbstractService {
public concreteServiceA(#Value("${config1}") String config1,
#Value("${config2}") String config2) {
super(config1, config2);
}
public void serviceA() {
//using serviceMethod in the abstract class
}
}
public concreteServiceB extends AbstractService {
public concreteServiceB(#Value("${configB1}") String config1,
#Value("${configB2}") String config2) {
super(config1, config2);
}
public void serviceB() {
//using serviceMethod in the abstract class
}
}
You can go like following:
public abstract class AbstractService {
public void serviceMethod() {
String config1 = getConfig1();
String config2 = getConfig2();
//using config1 and config 2 values
}
public abstract String getConfig1();
public abstract String getConfig2();
}
public class concreteServiceA extends AbstractService {
#Value("${config1}") private String config1;
#Value("${config2}") private String config2;
public String getConfig1(){
return config1;
}
public String getConfig2(){
return config2;
}
public void serviceA() { // using serviceMethod in the abstract class }
}
public class concreteServiceB extends AbstractService {
#Value("${config1.1}") private String config1;
#Value("${config2.1}") private String config2;
public String getConfig1(){
return config1;
}
public String getConfig2(){
return config2;
}
public void serviceB() { // using serviceMethod in the abstract class }
}
You could either use setter injection or (probably more elegant) constructor injection like this:
public abstract class AbstractService {
protected AbstractService(String config1, String config2) {
this.config1 = config1;
this.config2 = config2;
}
private String config1;
private String config2;
public void serviceMethod() {
//using config1 and config 2 values
}
}
public class ConcreteServiceA extends AbstractService {
public ConcreteServiceA(#Value("${config1a}") String config1, #Value("${config2a}") String config2) {
super(config1, config2);
}
public void serviceA() {
// using serviceMethod in the abstract class
}
}
public class ConcreteServiceB extends AbstractService {
public ConcreteServiceB(#Value("${config1b}") String config1, #Value("${config2b}") String config2) {
super(config1, config2);
}
public void serviceB() {
// using serviceMethod in the abstract class
}
}
But if you have lots of values you can also use setter injection and override the setters in each subclass. Or you can still use constructor injection but pass a container class holding the config like this:
public class ServiceConfig {
private String config1;
private String config2;
// getters, setters and more properties
}
Then pass it like this
public abstract class AbstractService {
private ServiceConfig config;
protected AbstractService(ServiceConfig config) {
this.config = config;
}
}
public class ConcreteServiceA extends AbstractService {
public ConcreteServiceA(#Value("${configA}") ServiceConfig config) {
super(config);
}
}
You can externalize your properties to specific beans which will be autowired to the concrete classes.
Spring annotation #ConfigurationProperties allows you to initialise simple POJO properties based on properties prefix.
First create your POJO which we will inject in the concrete services :
public class ServiceProperties {
private String config1;
private String config2;
//getters and setters
}
Then create a configuration class in a package scanned by spring :
#Configuration
public class ServicePropertiesConfiguration {
#Bean
#ConfigurationProperties(prefix = "service-a")
public ServiceProperties serviceAProperties() {
return new ServiceProperties();
}
#Bean
#ConfigurationProperties(prefix = "service-b")
public ServiceProperties serviceBProperties() {
return new ServiceProperties();
}
}
As you can see, prefix tells to spring where he has to search the properties. Your application.properties will look like this :
service-a.config1=serviceAConfig1
service-a.config2=serviceAConfig2
service-b.config1=serviceBConfig1
service-b.config2=serviceBConfig2
At this stage, you will have two beans of type ServiceProperties with specific values inside
The abstract service looks like this :
public abstract class AbstractService {
private final ServiceProperties serviceProperties;
protected AbstractService(ServiceProperties serviceProperties) {
this.serviceProperties = serviceProperties;
}
public void serviceMethod() {
//using config1 and config 2 values
// serviceProperties.getConfig1();
// serviceProperties.getConfig2();
}
}
In the concrete service, you have to use #Qualifier annotation with name of created bean
#Service
public class ConcreteServiceA extends AbstractService{
public ConcreteServiceA(#Qualifier("serviceAProperties") ServiceProperties serviceProperties) {
super(serviceProperties);
}
}
#Service
public class ConcreteServiceB extends AbstractService{
protected ConcreteServiceB(#Qualifier("serviceBProperties") ServiceProperties serviceProperties) {
super(serviceProperties);
}
}

Quarkus does not select bean programatically

I'm trying to select the bean programatically, but quarkus does not injected the bean and throw an exception. It's not supported ?
public enum ReportType {
ONE,
TWO
}
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, PARAMETER, FIELD, TYPE})
#Documented
public #interface Report {
ReportType value();
public static final class Literal extends AnnotationLiteral<Report> implements Report {
private final ReportType value;
public static Literal of(ReportType value) {
return new Literal(value);
}
private Literal(ReportType value) {
this.value = value;
}
public ReportType value() {
return value;
}
}
}
public interface CommonnInterface {
void call();
}
#Report(value = ReportType.ONE)
public class ReportOneBusiness implements CommonnInterface {
#Override
public void call() {
System.out.println("Hello");
}
}
And when we call
CommonnInterface commonnInterface = CDI.current()
.select(
CommonnInterface.class,
Report.Literal.of(ReportType.ONE)
).get();
No bean found for required type [interface org.business.CommonnInterface] and qualifiers [[#org.cdi.Report(value=ONE)]]
You likely need to make the beans unremoveable using the #io.quarkus.arc.Unremovable annotation.
See this for more details.
geoand was right, and I forgot to put #Dependent in the ReportOneBusiness.
The right code for ReportOneBusiness is
#Unremovable
#Dependent
#Report(value = ReportType.ONE)
public class ReportOneBusiness extends CommonnInterface {
#Override
public void call() {
System.out.println("Hello");
}
}

Single provider that creates different types of object based on class binding

I'm trying to inject several datastax Mappers but the Provider creation code is always the same and writing a provider for each type is redundant.
The provider code is
public class FooMapperProvider extends Provider<Mapper<Foo>> () {
private final MappingManager mappingManager
#Inject
FooMapperProvider(MappingManager) {
this.mappingManager = mappingManager;
}
#Override
public Mapper<Foo> get() {
mappingManager.mapper(Foo.class);
}
}
Is it possible to bind or create the provider for
bind(Foo.class).toProvider(GenericMapperProvider.class)
bind(Bar.class).toProvider(GenericMapperProvider.class)
so that get is called in a way mappingManager.mapper can create a mapper based on the class for that specific binding?
I thought about trying something like
public class MapperProvider<T> implements Provider<Mapper<T>> {
private final MappingManager mappingManager;
private final Class klass;
#Inject
public MapperProvider(MappingManager mappingManager, Class klass) {
this.mappingManager = mappingManager;
this.klass = klass;
}
#Override
public Mapper<T> get() {
return mappingManager.mapper(klass);
}
}
but I can't figure out how to specify the class and inject the dependency
public class MapperProvider<T> implements Provider<Mapper<T>> {
private final MappingManager mappingManager;
private final TypeLiteral<T> type;
#Inject
public MapperProvider(MappingManager mappingManager, TypeLiteral<T> type) {
this.mappingManager = mappingManager;
this.type = type;
}
#Override
public Mapper<T> get() {
return mappingManager.mapper(type.getRawType());
}
}
bind(new TypeLiteral<Mapper<Foo>>(){})
.toProvider(new TypeLiteral<MapperProvider<Foo>>(){});

Which pattern to use to avoid code duplication with object value transformer

I want to get rid of the following code duplication within the MyFacadeBean. Consider the following situation:
public class FacadeBean implements Facade {
#EJB
private CrudService crudService;
#Inject
private FirstAssembler firstAssembler;
#Inject
private SecondAssembler secondAssembler;
#Inject
private ThirdAssembler thridAssembler;
#Inject
private FourthAssembler fourthAssembler;
#Override
public void save(FirstValue value) {
FirstEntity entity = this.firstAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
#Override
public void save(SecondValue value) {
SecondEntity entity = this.secondAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
#Override
public void save(ThirdValue value) {
ThirdEntity entity = this.thirdAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
#Override
public void save(FourthValue value) {
FourthEntity entity = this.fourthAssembler.transformToEntity(value);
this.crudService.persist(entity);
}
}
public interface MyFacade {
void save(FirstValue value);
void save(SecondValue value);
}
With the CrudService:
public interface CrudService {
void persist(Object entity);
}
#Stateless
#Local(CrudService.class)
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public class CrudServiceBean implements CrudService {
public static final String PERSISTENCE_UNIT_NAME = "my_persistence_unit";
private EntityManager entityManager;
#PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Override
public void persist(Object entity) {
this.entityManager.persist(entity);
}
}
With the following assemblers:
public class FirstAssembler extends AbstractAssembler<FirstEntity> {
public FirstEntity transformToEntity(FirstValue value) {
if (value == null)
return null;
FirstEntity entity = new FirstEntity();
transformAbstractValueToAbstractObject(value, entity);
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
public class SecondAssembler extends AbstractAssembler<SecondEntity> {
public SecondEntity transformToEntity(SecondValue value) {
if (value == null)
return null;
SecondEntity entity = new SecondEntity();
transformAbstractValueToAbstractObject(value, entity);
entity.setTransactionType(value.getTransactionType());
entity.setValueDate(value.getValueDate());
return entity;
}
}
public abstract class AbstractAssembler<T extends AbstractEntity> {
protected void transformAbstractValueToAbstractObject(AbstractValue value, T object) {
object.setUniqueId(value.getUniqueId());
object.setNominalAmountValue(value.getNominalAmountValue());
}
}
With the following entities:
#Entity
public class FirstEntity extends AbstractEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private Long id;
#Column(name = "START_DATE")
#Temporal(TemporalType.DATE)
private Date startDate;
#Column(name = "FIXED_RATE")
#Digits(integer = 1, fraction = 10)
private BigDecimal fixedRate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public BigDecimal getFixedRate() {
return fixedRate;
}
public void setFixedRate(BigDecimal fixedRate) {
this.fixedRate = fixedRate;
}
}
#Entity
public class SecondEntity extends AbstractEntity {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
private Long id;
#Column(name = "VALUE_DATE")
#Temporal(TemporalType.DATE)
private Date valueDate;
#Column(name = "TRANSACTION_TYPE")
#Enumerated(EnumType.STRING)
private TransactionType transactionType;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getValueDate() {
return valueDate;
}
public void setValueDate(Date valueDate) {
this.valueDate = valueDate;
}
public TransactionType getTransactionType() {
return transactionType;
}
public void setTransactionType(TransactionType transactionType) {
this.transactionType = transactionType;
}
}
#MappedSuperclass
public abstract class AbstractEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "TRANSACTION_NOM_AMOUNT_VALUE")
#Digits(integer = 18, fraction = 5)
#Min(0)
private BigDecimal nominalAmountValue;
public BigDecimal getNominalAmountValue() {
return nominalAmountValue;
}
public void setNominalAmountValue(BigDecimal nominalAmountValue) {
this.nominalAmountValue = nominalAmountValue;
}
}
I tried the following approach:
public class FacadeBean implements Facade {
#Inject
private Assembler assembler;
#Inject
private AssemblerFactory assemblerFactory;
#Override
public <T extends AbstractValue> void save(T value) {
Assembler assembler = assemblerFactory.createAssembler(value);
AbstractEntity entity = assembler.transformToEntity(value);
this.crudService.persist(entity);
}
}
Problems are the AssemblerFactoryImpl and the AssemblerImpl in which I have to do instanceOf checks and castings...
Another idea would be to let the value know which transformer to use (or how to transform). But I want the value to be "dumb".
#Glenn Lane
public AbstractValue save(AbstractValue value) {
AbstractAssembler<AbstractValue, AbstractEntity> assembler = new FirstAssembler();
AbstractEntity entity = assembler.transformToEntity(value);
AbstractValue result = assembler.transformToValue(entity);
return result;
}
does not work, because of
Type mismatch: cannot convert from FirstAssembler to AbstractAssembler
I'm posting this as a separate answer, since I don't really think there's anything wrong with having a save method for every AbstractValue type.
First we'll establish your base value class for this example. I'm using an interface just so we don't muddy the waters. Your AbstractValue interface:
public interface AbstractValue
{
int getUniqueId();
double getNominalValue();
<T> T accept(AbstractValueVisitor<T> visitor);
}
And the "visitor interface":
public interface AbstractValueVisitor<T>
{
T visit(FirstValue value);
T visit(SecondValue value);
T visit(ThirdValue value);
T visit(FourthValue value);
}
I know you don't want intelligence baked into AbstractValue, but we are going to add one specification... that all concrete implementations of AbstractValue (all four) implement the accept method exactly this way:
#Override
public <T> T accept(AbstractValueVisitor<T> visitor)
{
return visitor.visit(this);
}
So that method is implemented four times: in all four value classes, exactly the same way. Because the visitor interface is aware of all concrete implementations, the appropriate method will be called for each particular value type. All three of these parts put together is the "visitor pattern".
Now we'll make an entity factory. Its job is to create the appropriate AbstractEntity when provided an AbstractValue:
public class AbstractEntityFactory
implements AbstractValueVisitor<AbstractEntity>
{
private static final AbstractEntityFactory INSTANCE;
static
{
INSTANCE = new AbstractEntityFactory();
}
// Singleton pattern
private AbstractEntityFactory()
{
}
public static AbstractEntity create(AbstractValue value)
{
if (value == null)
{
return null;
}
AbstractEntity e = value.accept(INSTANCE);
e.setNominalValue(value.getNominalValue());
e.setUniqueId(value.getUniqueId());
return e;
}
#Override
public AbstractEntity visit(FirstValue value)
{
FirstEntity entity = new FirstEntity();
// Set all properties specific to FirstEntity
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
#Override
public AbstractEntity visit(SecondValue value)
{
SecondEntity entity = new SecondEntity();
// Set all properties specific to SecondEntity
entity.setTransactionType(value.getTransactionType());
entity.setValueDate(value.getValueDate());
return entity;
}
#Override
public AbstractEntity visit(ThirdValue value)
{
ThirdEntity entity = new ThirdEntity();
// Set all properties specific to ThirdEntity
return entity;
}
#Override
public AbstractEntity visit(FourthValue value)
{
FourthEntity entity = new FourthEntity();
// Set all properties specific to FourthEntity
return entity;
}
}
Now your facade implementation takes an AbstractValue, and you got that one save method you're looking for:
public class FacadeBean implements Facade
{
#EJB
private CrudService crudService;
#Override
public void save(AbstractValue value)
{
AbstractEntity entity = AbstractEntityFactory.create(value);
crudService.persist(entity);
}
}
Because your AbstractValue now follows the visitor pattern, you can do all sorts of polymorphic behavior. Such as:
public class AbstractValuePrinter implements AbstractValueVisitor<Void>
{
private final Appendable out;
public AbstractValuePrinter(Appendable out)
{
this.out = out;
}
private void print(String s)
{
try
{
out.append(s);
out.append('\n');
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
#Override
public Void visit(FirstValue value)
{
print("I'm a FirstValue!");
print("Being a FirstValue is groovy!");
return null;
}
#Override
public Void visit(SecondValue value)
{
print("I'm a SecondValue!");
print("Being a SecondValue is awesome!");
return null;
}
#Override
public Void visit(ThirdValue value)
{
print("I'm a ThirdValue!");
print("Meh.");
return null;
}
#Override
public Void visit(FourthValue value)
{
print("I'm a ThirdValue!");
print("Derp.");
return null;
}
}
In this example, this visitor isn't returning anything... it's "doing" something, so we'll just set the return value as Void, since it's non-instantiatable. Then you print the value simply:
// (value must not be null)
value.accept(new AbstractValuePrinter(System.out));
Finally, the coolest part of the visitor pattern (in my opinion): you add FifthValue. You add the new method to your visitor interface:
T visit(FifthValue value);
And suddenly, you can't compile. You must address the lack of this handling in two places: AbstractEntityFactory and AbstractValuePrinter. Which is great, because you should consider it in those places. Doing class comparisons (with either instanceof or rinde's solution of a class-to-factory map) is likely to "miss" the new value type, and now you have runtime bugs... especially if you're doing 100 different things with these value types.
Anyhoo, I didn't want to get into this, but there you go :)
Use a generic method with a bound type parameter in order to spare yourself the repetition:
public <T extends AbstractValue> T save(T value) {...}
Within the method body, you'll be able to reference the argument value with all methods pertaining to AbstractValue.
Notes
Since your save methods seem to be overrides in this example, you might need to change the design of the parent class or interface too.
You could also use a generic class to start with (instead of a generic method in a non-necessarily generic class), depending on your use case.
I think a problem in your code is that the generic type of AbstractAssembler is that of the output of the transform method, not the input. If you change it as follows:
public abstract class AbstractAssembler<T extends AbstractValue> {
protected void transformAbstractValueToAbstractObject(AbstractEntity entity, T value) {
entity.setUniqueId(value.getUniqueId());
entity.setNominalAmountValue(value.getNominalAmountValue());
}
public abstract AbstractEntity transformToEntity(T value);
}
Then you can change the FacadeBean to the following.
public class FacadeBean {
#EJB
private CrudService crudService;
final Map<Class<?>, AbstractAssembler<?>> knownAssemblers;
FacadeBean() {
knownAssemblers = new LinkedHashMap<>();
knownAssemblers.put(FirstValue.class, new FirstAssembler());
knownAssemblers.put(SecondValue.class, new SecondAssembler());
// add more assemblers here
}
public <T extends AbstractValue> void save(T value, Class<T> type) {
#SuppressWarnings("unchecked") // safe cast
final AbstractAssembler<T> assembler =
(AbstractAssembler<T>) knownAssemblers.get(type);
final AbstractEntity entity = assembler.transformToEntity(value);
this.crudService.persist(entity);
}
}
Notice that I changed the signature of the save(..) method such that we have the type of the object that needs to be saved. With this type we can simply lookup the right assembler that should be used. And because the assembler is now generic on its input type, we can do a safe cast (be careful to keep your map consistent).
This implementation avoids duplication of code as you only need one save method. The use of the instanceof operator is prevented by changing the generic type of AbstractAssembler and storing all assemblers in a map.
The assemblers can look like this:
public class FirstAssembler extends AbstractAssembler<FirstValue> {
#Override
public FirstEntity transformToEntity(FirstValue value) {
final FirstEntity entity = new FirstEntity();
// do transformational stuff
super.transformAbstractValueToAbstractObject(entity, value);
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
public class SecondAssembler extends AbstractAssembler<SecondValue> {
#Override
public SecondEntity transformToEntity(SecondValue value) {
final SecondEntity entity = new SecondEntity();
// do transformational stuff
super.transformAbstractValueToAbstractObject(entity, value);
return entity;
}
}
Note: I'm not familiar with Java beans so you probably have to adapt the code a little if you want to use the #Injected assemblers instead of calling the constructors directly.
You're getting close to gold-plating here, but there is a bit of reduction you can do, specifically the null-check and calling the common field-setting method from each extension.
public abstract class AbstractAssembler<V extends AbstractValue, E extends AbstractEntity>
{
public final E transformToEntity(V value)
{
if (value == null)
{
return null;
}
E entity = createEntity(value);
entity.setUniqueId(value.getUniqueId());
entity.setNominalAmountValue(value.getNominalAmountValue());
return entity;
}
/**
* #return
* Appropriate entity object, with the fields not common to all AbstractEntity
* already set
*/
protected abstract E createEntity(V value);
}
And then the extended assembler:
public class FirstAssembler extends AbstractAssembler<FirstValue, FirstEntity>
{
#Override
protected FirstEntity createEntity(FirstValue value)
{
FirstEntity entity = new FirstEntity();
entity.setFixedRate(value.getFixedRate());
entity.setStartDate(value.getStartDate());
return entity;
}
}
If you really want a single factory class to handle all your values/entities, I would look into the visitor pattern, enhanced with a generic type parameter on the visitor interface (and the entity/value accept methods return a type based on the visiting interface). I won't show an example here simply because I don't think it's warranted in your case.
You can have one save method from the point of view of the classes that save those values, but you still have to implement three individual save methods.
Implement a class with all three save methods. For example:
public class ValuePersister {
#Inject
private Assembler1 assembler1;
#Inject
private Assembler2 assembler2;
#Inject
private Assembler3 assembler3;
public Value1 save(Value1 value1, CrudService crudService) {
Entity1 entity1 = assembler1.transformToObject(value1);
crudService.persist(entity1);
return assembler1.transformToValue(entity1);
}
public Value2 save(Value2 value2, CrudService crudService) {
Entity2 entity2 = assembler2.transformToObject(value2);
crudService.persist(entity2);
return assembler2.transformToValue(entity2);
}
public Value3 save(Value3 value3, CrudService crudService) {
Entity3 entity3 = assembler3.transformToObject(value3);
crudService.persist(entity3);
return assembler3.transformToValue(entity3);
}
}
Add an abstract method to AbstractValue:
public abstract AbstractValue save(ValuePersister valuePersister, CrudService crudService);
Implement that method in each class that extends AbstractValue:
#Override
public AbstractValue save(ValuePersister valuePersister, CrudService crudService) {
return valuePersister.save(this, crudService);
}
Inject ValuePersister and implement your original generic save method:
#Inject
private ValuePersister valuePersister;
#Override
public AbstractValue save(AbstractValue value) {
return value.save(valuePersister, crudService)
}

Categories