How to inject a Core Service in an Entity class?

Hello Team,
we are trying to inject a standard CUBA Service in an Entity class but getting the following error when the application is initialized:

09:14:31.523 INFO  c.h.cuba.core.sys.MetadataImpl          - Initializing metadata
09:14:32.013 ERROR c.h.c.c.s.AbstractWebAppContextLoader   - Error initializing application
java.lang.IllegalStateException: Can't find range class 'com.xxxx.yyyyy.service.ValidateService' for property yyyyy_AmortizationPercentage.validateService'
	at com.haulmont.cuba.core.sys.MetaModelLoader$RangeInitTask.execute(MetaModelLoader.java:922) ~[cuba-global-7.2.5.jar:7.2.5]
	at com.haulmont.cuba.core.sys.MetaModelLoader.loadModel(MetaModelLoader.java:135) ~[cuba-global-7.2.5.jar:7.2.5]
	at com.haulmont.cuba.core.sys.MetadataLoader.loadMetadata(MetadataLoader.java:114) ~[cuba-global-7.2.5.jar:7.2.5]
	at com.haulmont.cuba.core.sys.MetadataImpl.initMetadata(MetadataImpl.java:111) ~[cuba-global-7.2.5.jar:7.2.5]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:305) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:190) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:153) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:403) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:360) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]
	at com.haulmont.cuba.core.sys.EventsImpl.publish(EventsImpl.java:33) ~[cuba-global-7.2.5.jar:7.2.5]
	at com.haulmont.cuba.web.sys.WebEvents.publish(WebEvents.java:36) ~[cuba-web-7.2.5.jar:7.2.5]

The Service is defined as follows:

package com.xxxx.yyyy.service;

import com.xxxx.yyyy.entity.master_data.MasterDataVersion;

public interface ValidateService {
    String NAME = "costbudg_ValidateService";

    public void checkMasterDataVersion(MasterDataVersion mdv);
}

its implementation is:

package com.xxxx.yyyy.service;

import com.xxxx.yyyy.entity.master_data.MasterDataVersion;
import com.xxxx.yyyy.entity.master_data.versioned.AmortizationPercentage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component
@Service(ValidateService.NAME)
public class ValidateServiceBean implements ValidateService {
    private static final Logger log = LoggerFactory.getLogger(AmortizationPercentage.class);

    @Override
    public void checkMasterDataVersion(MasterDataVersion mdv) {
        log.info("Antonis 3");
    }
}

and the Entity class code is like:

package com.xxxx.yyyy.entity.master_data.versioned;
...
@Entity(name = "costbudg_AmortizationPercentage")
public class AmortizationPercentage extends StandardEntity {
    private static final long serialVersionUID = -6129526212838073685L;

    private static final Logger log = LoggerFactory.getLogger(AmortizationPercentage.class);


    @Transient
    @MetaProperty
    @Inject
    private ValidateService validateService;
...
    @PostUpdate
    public void postUpdate() {
        log.info("Updating AmortizationPercentage");
        validateService.checkMasterDataVersion(getMasterDataVersion());
    }

Obviously doing something the wrong way but what??

Thanks,
Antonis

Hi, try this:

    @PostUpdate
    public void postUpdate() {
        log.info("Updating AmortizationPercentage");
        AppBeans.get(ValidateService.class).checkMasterDataVersion(getMasterDataVersion());
    }

Just don’t use injection in Entitites.

Bye, Paolo

1 Like

Excellent, it works just fine using the AppBeans method.
Thanks a lot!

-Antonis

Hi,

In Cuba 7.2 it is also possible to inject beans as parameters in @PostConsturct annotated methods.

Cheers
Mario

2 Likes

Good to know, even if I’m not a big fan of these annotations, 'cause I’ve seen a lot of SRP violations thanks to them (business logic in the entities, someone? :wink: )

P.

Hi,

I’d recommend you considering Entity Listeners. I guess you should be able to inject service to a listener and it will help you to decouple business logic and entity content.