Problème incompréhensible dans les mécanismes d'héritage d'objets métiers implémentés en Java

Bonsoir,
J’ai un objet BCSISpecBusnDomain (hérité) et un autre objet BCSIWKFSpecBusnDomain (héritier) tous deux implémentés en Java.

Il semble que lors de l’instanciation du modèle,

  • L’objet BCSIWKFSpecBusnDomain (héritier de BCSISpecBusnDomain) est instancié en tant que BCSIWKFSpecBusnDomain et exécute son propre code (cf. traces WARN postLoad de 18:43:53,458; jusque là, pas de pb)
  • L’objet BCSISpecBusnDomain est instancié en tant que lui-même et exécute son propre code (cf. traces INFO postLoad de 18:43:52,941; toujours pas de problème)

MAIS

  • L’objet BCSIWKFSpecBusnDomain (héritier de BCSISpecBusnDomain) est instancié en tant que BCSISpecBusnDomain mais exécute le code BCSIWKFSpecBusnDomain (cf. traces INFO/WARN postLoad de 18:43:51,331)

Ce comportement est observé sur tous nos objets hérités/héritiers / tous les hooks et provoque des erreurs liées au fait que le code héritier opère des champs spécifiques à l’héritier (et donc inconnus de l’hérité) et que l’exécution du code héritier sur une instance configurée telle que getName=hérité mais getClass=héritier plante car c’est le modèle hérité qui est chargé…

Je n’arrive pas à comprendre d’où cela peut venir. Nous sommes en full Java, plus aucun code Rhino.
Nous reproduisons ce comportement également avec des héritages d’objets socle (objet User par exemple, dont le code avait été implémenté avant l’avènement du nouvel objet SimpleUser).

Je sèche complètement :sob:

public class BCSISpecBusnDomain extends ObjectDB {
    private static final long serialVersionUID = 1L;

    @Override
    public void postLoad() {
    	log("postLoad", "BCSISpecBusnDomain.postLoad getName() before = " + getName(), this,0);
    	...
public class BCSIWKFSpecBusnDomain extends com.simplicite.objects.BCSIModule_BusnObject.BCSISpecBusnDomain {
	private static final long serialVersionUID = 1L;

	@Override
	public void postLoad() {
		if (!getName().startsWith("BCSIWKF")) {
			warn("postLoad", "DEBT/UNEXPECTED CALL to WKF overriden hook from non WKF inherited object ("+getName()+","+getClass().getName()+")", this);
			super.postLoad();
			return;
		} else {
			warn("postLoad", "DEBT/EXPECTED CALL to WKF overriden hook from WKF inherited object ("+getName()+","+getClass().getName()+")", this);
		}
		...
2021-11-10 18:43:53,458|SIMPLICITE|WARN||http://renault.simplicite.io:10028||WARN|a068181|com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain|postLoad||Evénement: DEBT/EXPECTED CALL to WKF overriden hook from WKF inherited object (BCSIWKFSpecBusnDomain,com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain)
...
2021-11-10 18:43:52,941|SIMPLICITE|INFO||http://renault.simplicite.io:10028||INFO|a068181|com.simplicite.objects.BCSIModule_BusnObject.BCSISpecBusnDomain|postLoad||Evénement: BCSISpecBusnDomain.postLoad getName() getName=BCSISpecBusnDomain getClass=com.simplicite.objects.BCSIModule_BusnObject.BCSISpecBusnDomain
...
2021-11-10 18:43:51,331|SIMPLICITE|INFO||http://renault.simplicite.io:10028||INFO|system|com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain|postLoad||Event: BCSISpecBusnDomain.postLoad getName() getName=BCSISpecBusnDomain getClass=com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain
...
2021-11-10 18:43:51,331|SIMPLICITE|WARN||http://renault.simplicite.io:10028||WARN|system|com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain|postLoad||Event: DEBT/UNEXPECTED CALL to WKF overriden hook from non WKF inherited object (BCSISpecBusnDomain,com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain)

Bonjour,

Ca ne me dit rien. Quand Simplicité instancie un objet, il charge récursivement les définitions des objets (grand-)parents jusqu’à l’objet lui-même (pour surcharger la listes champs…).

  • Au niveau paramétrage logique, est ce que l’objet BCSIWKFSpecBusnDomain référence bien le bon héritage = BCSISpecBusnDomain ? et BCSISpecBusnDomain aucun parent ?
  • Aucun “setName” dans votre code qui forcerait un nom d’objet différent au chargement ou après ?
  • Existe-t-il des références FK entre l’objet hérité et son père ?
  • Forcez une erreur dans le UNEXPECTED avec un catch qui affichera la stack dans les logs pour savoir par quel chemin d’appels il arrive là.
public class BCSIWKFSpecBusnDomain extends com.simplicite.objects.BCSIModule_BusnObject.BCSISpecBusnDomain {
	private static final long serialVersionUID = 1L;

	@Override
	public void postLoad() {
		if (!getName().startsWith("BCSIWKF")) {
			try {
				throw new Exception(
"DEBT/UNEXPECTED CALL to WKF " +
"overriden hook from non WKF inherited object ("+getName()+","+getClass().getName()+")"); 
			} catch(Exception e) {
				AppLog.error("wrong postLoad stack", e, null):
			}
			super.postLoad();
			return;
		} else {
			warn("postLoad", "DEBT/EXPECTED CALL to WKF overriden hook from WKF inherited object ("+getName()+","+getClass().getName()+")", this);
		}
		...

Bonjour François,
merci beaucoup pour ton retour rapide.

Au niveau paramétrage logique, est ce que l’objet BCSIWKFSpecBusnDomain référence bien le bon héritage = BCSISpecBusnDomain ? et BCSISpecBusnDomain aucun parent ?

Oui, notre code déclare bien la classe héritière comme ça :

public class BCSIWKFSpecBusnDomain extends com.simplicite.objects.BCSIModule_BusnObject.BCSISpecBusnDomain

Aucun “setName” dans votre code qui forcerait un nom d’objet différent au chargement ou après ?

Non, aucun. Il y a néanmoins des instanciations d’objets temporaires (getTmpObject) sur lesquelles nous faisons un setInstanceName(...) pour les identifier spécifiquement (il s’agit d’un code de validation en cascade impliquant plusieurs objets corrélés dont un objet de même nature via une relation réflexive de composition). Cependant, ce code n’est pas exécuté au moment où le problème survient dans le postLoad.

Existe-t-il des références FK entre l’objet hérité et son père ?

Je ne suis pas sûr de bien comprendre la question concernant le père de l’hérité… L’hérité hérite lui-même directement d’ObjectDB.
Néanmoins, cf. point précédent, oui, l’hérité a dans son modèle une relation réflexive de composition. L’héritier en hérite donc. Par ailleurs, une relation similaire est redéfinie dans l’héritier (relation réflexive héritier-héritier) qui porte donc dans son modèle une fk vers l’hérité et une fk vers l’héritier.

Forcez une erreur dans le UNEXPECTED avec un catch qui affichera la stack dans les logs pour savoir par quel chemin d’appels il arrive là.

Voici la stacktrace :

2021-11-15 10:00:13,663|SIMPLICITE|ERROR||http://renault.simplicite.io:10028||ERROR|system|com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain|postLoad||Event: wrong postLoad stack
    java.lang.Exception: DEBT/UNEXPECTED CALL to WKF overriden hook from non WKF inherited object (BCSISpecBusnDomain,com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain)
     at com.simplicite.objects.BCSIModule_BusnObject.BCSIWKFSpecBusnDomain.postLoad(BCSIWKFSpecBusnDomain.java:29)
     at com.simplicite.util.ObjectHooks.postLoad(ObjectHooks.java:29)
     at com.simplicite.util.engine.ObjectLoader.load(ObjectLoader.java:104)
     at com.simplicite.util.engine.ObjectDirect.init(ObjectDirect.java:48)
     at com.simplicite.util.ObjectDB.init(ObjectDB.java:241)
     at com.simplicite.util.ObjectDB.load(ObjectDB.java:219)
     at com.simplicite.util.engine.CoreCache.instantiateObject(CoreCache.java:3863)
     at com.simplicite.util.engine.CoreCache.getObject(CoreCache.java:3805)
     at com.simplicite.util.engine.GrantManager.getObject(GrantManager.java:82)
     at com.simplicite.util.engine.GrantDirect.getObject(GrantDirect.java:602)
     at com.simplicite.util.Grant.getObject(Grant.java:2131)
     at com.simplicite.util.tools.IndexCore.getIndexableObjects(IndexCore.java:44)
     at com.simplicite.util.GrantCore.accessIndexedObjects(GrantCore.java:2316)
     at com.simplicite.util.tools.JSONTool.indexMetaDataToJson(JSONTool.java:3597)
     at com.simplicite.webapp.tools.JSONServletTool.indexsearch(JSONServletTool.java:869)
     at com.simplicite.webapp.tools.JSONServletTool.indexsearch(JSONServletTool.java:845)
     at com.simplicite.webapp.tools.JSONServletTool.applicationService(JSONServletTool.java:136)
     at com.simplicite.webapp.servlets.AbstractJSONServlet.service(AbstractJSONServlet.java:91)
     at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
     at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
     at com.simplicite.webapp.filters.AuthMethodFilter.doFilter(AuthMethodFilter.java:169)
     at com.simplicite.webapp.filters.AbstractFilter.doFilter(AbstractFilter.java:37)
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
     at com.simplicite.webapp.filters.HTTPHeadersFilter.doFilter(HTTPHeadersFilter.java:39)
     at com.simplicite.webapp.filters.AbstractFilter.doFilter(AbstractFilter.java:37)
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
     at com.simplicite.webapp.filters.RewriteFilter.doFilter(RewriteFilter.java:86)
     at com.simplicite.webapp.filters.AbstractFilter.doFilter(AbstractFilter.java:37)
     at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
     at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
     at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:183)
     at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
     at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
     at com.simplicite.tomcat.valves.APISessionValve.invoke(APISessionValve.java:198)
     at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
     at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
     at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
     at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
     at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
     at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
     at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
     at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
     at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1722)
     at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
     at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
     at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
     at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
     at java.base/java.lang.Thread.run(Thread.java:833)

Merci pour ces éléments.

  • Au niveau paramétrage logique, je parlais de celui paramétré dans les 2 définitions d’objet, pas du code Java.
  • Peut-on voir tout le modèle logique/UML qui montre l’héritage et les FK ? Simplicité charge peut être la réflexive avec le mauvais nom à cause de l’héritage ou quelque chose de cet ordre. on pourra reproduire le modèle et tester l’ordre des chargements.
  • Pour instancier un objet avec un nom d’instance spécifique, il faut passer par getGrant().getObject(nom instance, nom objet) directement,
  • La stack trace ne montre rien d’anormal

Mettre des if pour tester l’existence d’un champ avant de l’utiliser
if (getField("xxx", false)!=null) { ... }

false : pour ne pas faire de trace dans les logs.
ça corrigera en attendant d’en savoir un peu plus sur ce dysfonctionnement.

Ok, donc la définition de BCSIWKFSpecBusnDomain est bien "Code héritier de BCSIWKFSpecBusnDomain) et BCSISpecBusnDomain n’a rien dans le champ “Code Héritier de”.

Voici un extrait du modèle logique:

Les débrayages/cloisonnements via des if sont en place a priori dans tous les contextes où le problème se pose (en tout cas dans notre code).

Si je comprends bien, la fk “vers héritier” serait castée à tort “vers hérité” et le problème surviendrait lors du chargement du modèle du “fk vers héritier” ?

Ok on va implémenter un modèle avec les mêmes liens et voir ce que ça donne au chargement. Ca ne me dit rien mais le loader n’est pas forcement encore au courant de l’héritage quand il charge le lien réflexif, ça se verra au debugger.

Sinon je ne comprends pas l’intérêt d’une seconde relation réflexive dans l’héritier puisque le parent en a déjà une, le fils se retrouve au final avec 2 réflexives = il y a 2 arbres différents d’un point de vu métier ?

Ok merci.

Sinon je ne comprends pas l’intérêt d’une seconde relation réflexive dans l’héritier puisque le parent en a déjà une, le fils se retrouve au final avec 2 réflexives = il y a 2 arbres différents d’un point de vu métier ?

Oui,

  • l’héritier porte un workflow qui isole une transaction de modification (partagée entre les contributeurs) dont le cycle de vie est globalement [Draft]->[En validation]->[Validé]->[Draft]->…
  • l’hérité porte la dernière version validée (mise à jour lors de la transition vers [Validé]) et est consultable par tous les autres users
  • dans le cadre du workflow (dans l’héritier), il est possible de changer la filiation (composition) mais cette modification de filiation doit être validée par les gestionnaires de l’objet concerné donc tant qu’elle n’est pas validée, c’est l’ancienne filiation qui est présentée (dans l’hérité)

J’ai tout mis en ligne sur l’instance https://bcsi.renault.simplicite.io.
NB: Il n’y a aucune donnée métier dans l’environnement (les listes sont donc vides).

Les objets concernés sont accessibles à designer via le menu ci-dessous (selon leur profil / habilitation workflow, les users ne voient qu’une seule entrée de menu pour chaque objet) :

Problème reproduit avec le même modèle.

En fait cela ne dépend pas des liens réflexifs, mais juste de l’ordre du chargement récursif des héritages d’objets (instanciation des parents seuls ou de l’héritier et ses parents en premier au clear-cache).

Le parent se retrouvait bien cloné avec la classe de l’objet fils au niveau du core-cache (optimisation pour ne pas rechercher toute la définition en base à chaque instanciation).

On va devoir pousser le fix en 5.1.13 et en V4.P25.

Super, merci beaucoup pour ton analyse et la solution rapide!

Bonjour François,
j’ai lancé un upgrade de l’instance bcsi.renault.simplicite.io mais elle reste en version 5.1.12.
Quand la 5.1.13 sera-t-elle disponible ?

Oui effectivement on a pas encore releasé la 5.1.13. Ce sera fait dans la semaine.

OK super, merci beaucoup… je n’aurai qu’à attendre l’auto-upgrade c’est ça ? (aucune manip de mon côté)

Sur les SIM les instances marquées “autoupdate” se mettent à jour quand nécessaire pendant la nuit

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.