EntityManagerHandler fails on search for INamedEntity

Bug and problem reporting on Cyclos 4 version

Moderators: hugo, alexandre, rmvanarkel

Post Reply
martin.rueegg
Posts: 80
Joined: Thu Aug 11, 2016 12:38 pm
Location: Bristol, UK
Contact:

EntityManagerHandler fails on search for INamedEntity

Post by martin.rueegg »

Hello

I'm trying to get an object of a CustomScript:

Code: Select all

import org.cyclos.entities.system.CustomScript;

CustomScript o = entityManagerHandler.find(CustomScript, 'scriptName');
According to the documentation, EntityManagerHandler should search using the 'name' property, if entityClass implements INamedEntity (which is the case for CustomScript). However, above script throws and IllegalArgumentException (independent of whether or not the script with the given name exists):

Code: Select all

Caused by: javax.script.ScriptException: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [FROM CustomScript e WHERE e.internalName = :fieldValue AND (e.network IS NULL OR e.network = :currentNetwork)].
[26, 40] The state field path 'e.internalName' cannot be resolved to a valid type.
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:320)
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233)
        at org.cyclos.impl.system.CustomScriptServiceImpl.lambda$0(CustomScriptServiceImpl.java:615)
        ... 78 more
Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Problem compiling [FROM CustomScript e WHERE e.internalName = :fieldValue AND (e.network IS NULL OR e.network = :currentNetwork)].
[26, 40] The state field path 'e.internalName' cannot be resolved to a valid type.
        at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1743)
        at org.cyclos.impl.utils.persistence.EntityManagerHandlerImpl.performFind(EntityManagerHandlerImpl.java:457)
        at org.cyclos.impl.utils.persistence.EntityManagerHandlerImpl.lambda$0(EntityManagerHandlerImpl.java:416)
        at org.cyclos.impl.InvocationContext.getAttribute(InvocationContext.java:655)
        at org.cyclos.impl.utils.persistence.EntityManagerHandlerImpl.doFind(EntityManagerHandlerImpl.java:415)
        at org.cyclos.impl.utils.persistence.EntityManagerHandlerImpl.find(EntityManagerHandlerImpl.java:212)
        at org.cyclos.impl.utils.persistence.EntityManagerHandlerImpl.find(EntityManagerHandlerImpl.java:175)
        at org.cyclos.impl.utils.persistence.EntityManagerHandler$find.call(Unknown Source)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
        at Script4.run(Script4.groovy:3)
        at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
        ... 81 more
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.0.v20170811-d680af5): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Problem compiling [FROM CustomScript e WHERE e.internalName = :fieldValue AND (e.network IS NULL OR e.network = :currentNetwork)].
[26, 40] The state field path 'e.internalName' cannot be resolved to a valid type.
        at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:155)
        at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:347)
        at org.eclipse.persistence.internal.jpa.jpql.HermesParser.populateQueryImp(HermesParser.java:278)
        at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildQuery(HermesParser.java:163)
        at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:140)
        at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:116)
        at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:102)
        at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:86)
        at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1741)
        ... 93 more
tested on v4.9, v4.8.2. v4.7.3.

Warm regards,
Martin.
Cyclos v4.8.2 w/ Social license
Technical Manager, Bristol Pound CIC, Bristol, UK, http://bristolpound.org

martin.rueegg
Posts: 80
Joined: Thu Aug 11, 2016 12:38 pm
Location: Bristol, UK
Contact:

Re: EntityManagerHandler fails on search for INamedEntity

Post by martin.rueegg »

As a work-around, the following code can be used:

Code: Select all

import org.cyclos.entities.system.CustomScript;
import org.cyclos.model.system.scripts.ScriptType;

CustomScript cs = binding.customScriptService.listByType(ScriptType.EXTENSION_POINT).find{it.name == 'epLoanGenerateNumber'};
Which is obviously not as elegant, as you have to know both that you're looking for a CustomScript and the script type. Sounds ridicule here, but if you use a generic function, in that function, you don't necessarily know ...

Also it's less efficient as it instantiates all the scripts of the given type, while you only really want one.

Martin.
Cyclos v4.8.2 w/ Social license
Technical Manager, Bristol Pound CIC, Bristol, UK, http://bristolpound.org

martin.rueegg
Posts: 80
Joined: Thu Aug 11, 2016 12:38 pm
Location: Bristol, UK
Contact:

Re: EntityManagerHandler fails on search for INamedEntity

Post by martin.rueegg »

Unfortunately, CustomScriptQuery also doesn't support name as a query parameter. Otherwise something like the following might be possible (obviously, also a hack with load and toEntity ...):

Code: Select all

import org.cyclos.entities.system.CustomScript;
import org.cyclos.model.system.scripts.*;

def o = binding.customScriptService.search(
		new CustomScriptQuery(
            name: 'scriptName'
		)
).getPageItems().getAt(0)

return o ? binding.customScriptService.toEntity(binding.customScriptService.load(o.id)) : null;
Cyclos v4.8.2 w/ Social license
Technical Manager, Bristol Pound CIC, Bristol, UK, http://bristolpound.org

luis
Posts: 190
Joined: Fri Feb 17, 2006 11:01 am

Re: EntityManagerHandler fails on search for INamedEntity

Post by luis »

Actually, EntityManagerHandler can find entities by internal name, not by name.
In fact, the interface is not INamedEntity, but IInternalNamedEntity instead.
And CustomScript doesn't implement IInternalNamedEntity, it has no internal name.
If you need a script by name, you can use this query instead:

Code: Select all

import org.cyclos.entities.system.QCustomScript

def s = QCustomScript.customScript
def script = entityManagerHandler.from(s)
    .where(s.name.eq('Script Name'))
    .singleResult(s)
Luis Fernando Planella Gonzalez
Cyclos development team

martin.rueegg
Posts: 80
Joined: Thu Aug 11, 2016 12:38 pm
Location: Bristol, UK
Contact:

Re: EntityManagerHandler fails on search for INamedEntity

Post by martin.rueegg »

Hi Luis,

Thanks for your response.
luis wrote:Actually, EntityManagerHandler can find entities by internal name, not by name.
Then this is not in line with the documentation:
Interface EntityManagerHandler (emphasis added) wrote:
find(java.lang.Class<E> entityClass, java.lang.String internalName)

Finds the given entity by internal name in the current network.
If the entity was not found and entityClass is a INamedEntity then it try to find by name.
So I'd argue that either this hint should be removed from the documentation, or the functionality implemented.
luis wrote: In fact, the interface is not INamedEntity, but IInternalNamedEntity instead.
And CustomScript doesn't implement IInternalNamedEntity, it has no internal name.
I'm aware of that. But if there is a contradiction between two sources of information (documentation text vs implemented interface) I personally assume that the documentation states the "should-be" state, which is not matched. Hence, my report.

Hugo made the point, that you did not implement the search for names, as the name might change due to translation. I don't think that's a valid argument, as the name is the unique identifier within any given type implementing org.cyclos.entities.NamedEntity. Independently to whether or not it also implements IInternalNamedEntity. The only other unique identifier is the id. But the id does not need to be consistent over two Cyclos instances, and cannot be the same on two different networks on the same instance.

Hence it would really be helpful, if the implementation of find would also be able to look up the name. You could do two separate overloadings of the function:

Code: Select all

IInternalNamedEntity find(java.lang.Class<T extends IInternalNamedEntity> entityClass, java.lang.String internalName)
INamedEntity         find(java.lang.Class<T extends INamedEntity>         entityClass, java.lang.String name)

Great, thanks for this:
luis wrote: If you need a script by name, you can use this query instead:

Code: Select all

import org.cyclos.entities.system.QCustomScript

def s = QCustomScript.customScript
def script = entityManagerHandler.from(s)
    .where(s.name.eq('Script Name'))
    .singleResult(s)

Warm regards,
Martin.
Cyclos v4.8.2 w/ Social license
Technical Manager, Bristol Pound CIC, Bristol, UK, http://bristolpound.org

Post Reply