You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by Jim Talbut <jt...@spudsoft.co.uk> on 2014/03/03 22:48:44 UTC

Yet another FIQL question

Hi,

Using CXF 2.7.10, if I have:
class Job {
     public List<Task> getTasks(){}
     public void setTasks(List<Task> tasks){}
}
class Task {
     public List<Item> getItems(){}
     public void setItems(List<Item> items){}
     public String getTaskName(){}
     public void setTaskName(String taskName){}
}
class Item {
     public String getItemName(){}
     public void setItemName(String itemName){}
}

With a map that contains:
         map.put("taskName","tasks.taskName");
         map.put("itemName","tasks.items.itemName");

I can do:
"&_s=taskName==Bob"
but if I do
"&_s=itemName==Bob"
it fails because it tries to instantiate a List object - if I force that 
to be an ArrayList it then tries to setItemName on the ArrayList.

Is it currently possible to make FIQL work through two layers of 
collections?

Thanks

Jim

Re: Yet another FIQL question

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi

I haven't had a time to look into yet, just did a quick read, you 
mention below you are OK with using interfaces, not implementations.
CXF 2.7.10 does support interfaces, it creates proxies.

Can you try working directly with interfaces ?

Thanks, Sergey

On 07/03/14 07:14, Jim Talbut wrote:
> Hi Sergey,
>
> This is an example of what I'm trying to do with interfaces.
> Unfortunately I can't give you an example that will definitely not work
> because it depends on the ordering of methods by reflection.
>
> interface Job {
>      List<? extends Task> getTasks();
>      // I won't bother detailing these any further, the point is that
>      // the interface doesn't want a setTasks(List) method
>      void addTask(Task task);
>      void removeTask(Task task);
>      Task findTask(String name);
>      Task createTask(String name);
> }
> interface Task {
>      List<? extends Item> getItems();
>      //  As with Job, there is no Task.setItems(List) method
>      void addItem(Item item);
>      void removeItem(Item item);
>      Task findItem(String name);
>      Task createItem(String name);
>
>      String getTaskName();
>      void setTaskName(String taskName);
> }
> interface Item {
>       String getItemName();
>       void setItemName(String itemName);
> }
>
>
> Implementations:
> class Job_Impl implements Job, Serializable {
>      public List<Task_Impl> getTasks(){}
>      // setTasks is ONLY used by the FIQL processing
>      public void setTasks(List<Task_Impl> tasks){}
>      public void addTask(Task task){}
>      public void removeTask(Task task){}
>      public Task findTask(String name){}
>      public Task createTask(String name){}
> }
> class Task_Impl implements Task, Serializable {
>      public List<Item_Impl> getItems();
>      // setItems is ONLY used by the FIQL processing
>      public void setItems(List<Item_Impl> tasks){}
>      public void addItem(Item item){}
>      public void removeItem(Item item){}
>      public Task findItem(String name){}
>      public Task createItem(String name){}
>
>      public String getTaskName(){}
>      public void setTaskName(String taskName){}
> }
> class Item_Impl implements Item, Serializable {
>       public String getItemName(){}
>       public void setItemName(String itemName){}
> }
>
>
> Note that I'd be perfectly happy with the Impl methods were based on
> interfaces:
>      public List<? extends Task> getTasks(){}
>      public void setTasks(List<? extends Task> tasks){}
>
> So the problems (in no particular order):
> * FIQL decides the type based on the return value of the getter, which
> means it needs to find a getter that returns a concrete type (because
> it's going to try to construct it).
> * Something (not sure what) requires the getter & setter to use the same
> types, throwing an exception if they are different (even if they are
> compatible).
> * Reflection (the JVM, I believe) considers a method with a different
> return type to be a different method even though there is no way to
> distinguish between them in Java.
> * Methods are returned from reflection in an unpredictable order, which
> means that some classes might find the getter with the implementation
> class return type whilst others find the getter with the interface
> return type.
>
> The best I can come up with for a solution is:
> 1. Decide the type based on the setter.
> 2. Permit setters and getters to return different types as long as the
> setter argument type extends the getter return type.
> 3. Make method selection much more complicated by looking for the best
> getter to match the setter.
>
> Jim


Re: Yet another FIQL question

Posted by Jim Talbut <jt...@spudsoft.co.uk>.
Hi Sergey,

This is an example of what I'm trying to do with interfaces.
Unfortunately I can't give you an example that will definitely not work 
because it depends on the ordering of methods by reflection.

interface Job {
     List<? extends Task> getTasks();
     // I won't bother detailing these any further, the point is that
     // the interface doesn't want a setTasks(List) method
     void addTask(Task task);
     void removeTask(Task task);
     Task findTask(String name);
     Task createTask(String name);
}
interface Task {
     List<? extends Item> getItems();
     //  As with Job, there is no Task.setItems(List) method
     void addItem(Item item);
     void removeItem(Item item);
     Task findItem(String name);
     Task createItem(String name);

     String getTaskName();
     void setTaskName(String taskName);
}
interface Item {
      String getItemName();
      void setItemName(String itemName);
}


Implementations:
class Job_Impl implements Job, Serializable {
     public List<Task_Impl> getTasks(){}
     // setTasks is ONLY used by the FIQL processing
     public void setTasks(List<Task_Impl> tasks){}
     public void addTask(Task task){}
     public void removeTask(Task task){}
     public Task findTask(String name){}
     public Task createTask(String name){}
}
class Task_Impl implements Task, Serializable {
     public List<Item_Impl> getItems();
     // setItems is ONLY used by the FIQL processing
     public void setItems(List<Item_Impl> tasks){}
     public void addItem(Item item){}
     public void removeItem(Item item){}
     public Task findItem(String name){}
     public Task createItem(String name){}

     public String getTaskName(){}
     public void setTaskName(String taskName){}
}
class Item_Impl implements Item, Serializable {
      public String getItemName(){}
      public void setItemName(String itemName){}
}


Note that I'd be perfectly happy with the Impl methods were based on 
interfaces:
     public List<? extends Task> getTasks(){}
     public void setTasks(List<? extends Task> tasks){}

So the problems (in no particular order):
* FIQL decides the type based on the return value of the getter, which 
means it needs to find a getter that returns a concrete type (because 
it's going to try to construct it).
* Something (not sure what) requires the getter & setter to use the same 
types, throwing an exception if they are different (even if they are 
compatible).
* Reflection (the JVM, I believe) considers a method with a different 
return type to be a different method even though there is no way to 
distinguish between them in Java.
* Methods are returned from reflection in an unpredictable order, which 
means that some classes might find the getter with the implementation 
class return type whilst others find the getter with the interface 
return type.

The best I can come up with for a solution is:
1. Decide the type based on the setter.
2. Permit setters and getters to return different types as long as the 
setter argument type extends the getter return type.
3. Make method selection much more complicated by looking for the best 
getter to match the setter.

Jim

Re: Yet another FIQL question

Posted by Sergey Beryozkin <sb...@gmail.com>.
> I saw your email earlier on but could *not* easily reproduce. Can you type
> some sample code hierarchy right here, as you did for Job/Task/Item ?

>
> Cheers, Sergey
>
>> Thanks.
>>
>> Jim
>>

Re: Yet another FIQL question

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi Jim
On 04/03/14 07:06, Jim Talbut wrote:
> Overnight I realised that, for my current job, I don't actually need the
> nested lists to work - because if I'm specifying the Item I can approach
> the whole query from the Task rather than the Job.
> So don't rush out a fix for my benefit.
>
Thanks for reporting these issues, it is important the typed approach 
just works with all type of bean properties. I've got this issue fixed,
can you try 2.7.11-SNAPSHOT, you may need to rebuild it locally


> I was very pleased to see that you've fixed the lower casing issue,
> which simplifies my bean name maps a lot, thanks for that.
> And whilst debugging I found the lovely new property for permitting
> exceptions to be thrown, though there are still some places where you
> are silently swallowing exceptions (i.e. FiqlParser.parseType line 314).
>
It's gone on the trunk, after Andriy refactored the code to support 
OData filter expressions, but yea, I saw it in 2.7.x, fixed too
> The other problem that still gets in my way (I've got workarounds, but
> it stops me using the object model I'd like) is the handling of models
> made up of interfaces.
> The big issue is that my getters and setters are defined on the
> interface to handle interfaces, but the implementation can either cast
> or overload the return value.
> Unfortunately there is a check somewhere in there that requires the
> getters and setters to use the same types (rather than just compatible
> types) and overloading the return value creates two methods for
> reflection in an unpredictable order.
> It wouldn't be difficult to change the getter/setter type check to just
> ensure the types are compatible (though I'm not sure that check is in
> your code), but handling the multiple different getters that return
> different types is going to be ugly :(.
>
> Currently I end up having vague get/setParent methods on the interface
> and actual get/setJob methods on the implementation, which works but
> causes big problems on the things with two types of child :)
>
I saw your email earlier on but could easily reproduce. Can you type 
some sample code hierarchy right here, as you did for Job/Task/Item ?

Cheers, Sergey

> Thanks.
>
> Jim
>
>
>
> On 03/03/2014 22:42, Jim Talbut wrote:
>> Just tried it (using List, not ArrayList) same problem.
>> I think it relates to this bit from parseType, where you explicitly
>> check whether you're on the last but one component (couldn't that be
>> names.length > 1)?
>> Not that I'm claiming to have understood what's going on here fully
>> yet :)
>>
>> boolean isPrimitive = InjectionUtils.isPrimitive(returnType) ||
>> returnType.isEnum();
>> boolean lastTry = names.length == 2
>>         && (isPrimitive || returnType == Date.class || returnCollection);
>>
>> Jim
>>
>> On 03/03/2014 22:24, Sergey Beryozkin wrote:
>>> I wonder if it will work with 2.7.11-SNAPSHOT...It rings a bell, seems
>>> like I had to deal with the similar issue recently...
>>>
>>> Can you try the snapshot for now ?
>>>
>>> Cheers. Sergey
>>> On 03/03/14 21:48, Jim Talbut wrote:
>>>> Hi,
>>>>
>>>> Using CXF 2.7.10, if I have:
>>>> class Job {
>>>>      public List<Task> getTasks(){}
>>>>      public void setTasks(List<Task> tasks){}
>>>> }
>>>> class Task {
>>>>      public List<Item> getItems(){}
>>>>      public void setItems(List<Item> items){}
>>>>      public String getTaskName(){}
>>>>      public void setTaskName(String taskName){}
>>>> }
>>>> class Item {
>>>>      public String getItemName(){}
>>>>      public void setItemName(String itemName){}
>>>> }
>>>>
>>>> With a map that contains:
>>>>          map.put("taskName","tasks.taskName");
>>>>          map.put("itemName","tasks.items.itemName");
>>>>
>>>> I can do:
>>>> "&_s=taskName==Bob"
>>>> but if I do
>>>> "&_s=itemName==Bob"
>>>> it fails because it tries to instantiate a List object - if I force
>>>> that
>>>> to be an ArrayList it then tries to setItemName on the ArrayList.
>>>>
>>>> Is it currently possible to make FIQL work through two layers of
>>>> collections?
>>>>
>>>> Thanks
>>>>
>>>> Jim
>>>
>>>
>>
>



Re: Yet another FIQL question

Posted by Jim Talbut <jt...@spudsoft.co.uk>.
Overnight I realised that, for my current job, I don't actually need the 
nested lists to work - because if I'm specifying the Item I can approach 
the whole query from the Task rather than the Job.
So don't rush out a fix for my benefit.

I was very pleased to see that you've fixed the lower casing issue, 
which simplifies my bean name maps a lot, thanks for that.
And whilst debugging I found the lovely new property for permitting 
exceptions to be thrown, though there are still some places where you 
are silently swallowing exceptions (i.e. FiqlParser.parseType line 314).

The other problem that still gets in my way (I've got workarounds, but 
it stops me using the object model I'd like) is the handling of models 
made up of interfaces.
The big issue is that my getters and setters are defined on the 
interface to handle interfaces, but the implementation can either cast 
or overload the return value.
Unfortunately there is a check somewhere in there that requires the 
getters and setters to use the same types (rather than just compatible 
types) and overloading the return value creates two methods for 
reflection in an unpredictable order.
It wouldn't be difficult to change the getter/setter type check to just 
ensure the types are compatible (though I'm not sure that check is in 
your code), but handling the multiple different getters that return 
different types is going to be ugly :(.

Currently I end up having vague get/setParent methods on the interface 
and actual get/setJob methods on the implementation, which works but 
causes big problems on the things with two types of child :)

Thanks.

Jim



On 03/03/2014 22:42, Jim Talbut wrote:
> Just tried it (using List, not ArrayList) same problem.
> I think it relates to this bit from parseType, where you explicitly 
> check whether you're on the last but one component (couldn't that be 
> names.length > 1)?
> Not that I'm claiming to have understood what's going on here fully 
> yet :)
>
> boolean isPrimitive = InjectionUtils.isPrimitive(returnType) || 
> returnType.isEnum();
> boolean lastTry = names.length == 2
>         && (isPrimitive || returnType == Date.class || returnCollection);
>
> Jim
>
> On 03/03/2014 22:24, Sergey Beryozkin wrote:
>> I wonder if it will work with 2.7.11-SNAPSHOT...It rings a bell, seems
>> like I had to deal with the similar issue recently...
>>
>> Can you try the snapshot for now ?
>>
>> Cheers. Sergey
>> On 03/03/14 21:48, Jim Talbut wrote:
>>> Hi,
>>>
>>> Using CXF 2.7.10, if I have:
>>> class Job {
>>>      public List<Task> getTasks(){}
>>>      public void setTasks(List<Task> tasks){}
>>> }
>>> class Task {
>>>      public List<Item> getItems(){}
>>>      public void setItems(List<Item> items){}
>>>      public String getTaskName(){}
>>>      public void setTaskName(String taskName){}
>>> }
>>> class Item {
>>>      public String getItemName(){}
>>>      public void setItemName(String itemName){}
>>> }
>>>
>>> With a map that contains:
>>>          map.put("taskName","tasks.taskName");
>>>          map.put("itemName","tasks.items.itemName");
>>>
>>> I can do:
>>> "&_s=taskName==Bob"
>>> but if I do
>>> "&_s=itemName==Bob"
>>> it fails because it tries to instantiate a List object - if I force 
>>> that
>>> to be an ArrayList it then tries to setItemName on the ArrayList.
>>>
>>> Is it currently possible to make FIQL work through two layers of
>>> collections?
>>>
>>> Thanks
>>>
>>> Jim
>>
>>
>


Re: Yet another FIQL question

Posted by Jim Talbut <jt...@spudsoft.co.uk>.
Just tried it (using List, not ArrayList) same problem.
I think it relates to this bit from parseType, where you explicitly 
check whether you're on the last but one component (couldn't that be 
names.length > 1)?
Not that I'm claiming to have understood what's going on here fully yet :)

boolean isPrimitive = InjectionUtils.isPrimitive(returnType) || 
returnType.isEnum();
boolean lastTry = names.length == 2
         && (isPrimitive || returnType == Date.class || returnCollection);

Jim

On 03/03/2014 22:24, Sergey Beryozkin wrote:
> I wonder if it will work with 2.7.11-SNAPSHOT...It rings a bell, seems
> like I had to deal with the similar issue recently...
>
> Can you try the snapshot for now ?
>
> Cheers. Sergey
> On 03/03/14 21:48, Jim Talbut wrote:
>> Hi,
>>
>> Using CXF 2.7.10, if I have:
>> class Job {
>>      public List<Task> getTasks(){}
>>      public void setTasks(List<Task> tasks){}
>> }
>> class Task {
>>      public List<Item> getItems(){}
>>      public void setItems(List<Item> items){}
>>      public String getTaskName(){}
>>      public void setTaskName(String taskName){}
>> }
>> class Item {
>>      public String getItemName(){}
>>      public void setItemName(String itemName){}
>> }
>>
>> With a map that contains:
>>          map.put("taskName","tasks.taskName");
>>          map.put("itemName","tasks.items.itemName");
>>
>> I can do:
>> "&_s=taskName==Bob"
>> but if I do
>> "&_s=itemName==Bob"
>> it fails because it tries to instantiate a List object - if I force that
>> to be an ArrayList it then tries to setItemName on the ArrayList.
>>
>> Is it currently possible to make FIQL work through two layers of
>> collections?
>>
>> Thanks
>>
>> Jim
>
>


Re: Yet another FIQL question

Posted by Sergey Beryozkin <sb...@gmail.com>.
I wonder if it will work with 2.7.11-SNAPSHOT...It rings a bell, seems 
like I had to deal with the similar issue recently...

Can you try the snapshot for now ?

Cheers. Sergey
On 03/03/14 21:48, Jim Talbut wrote:
> Hi,
>
> Using CXF 2.7.10, if I have:
> class Job {
>      public List<Task> getTasks(){}
>      public void setTasks(List<Task> tasks){}
> }
> class Task {
>      public List<Item> getItems(){}
>      public void setItems(List<Item> items){}
>      public String getTaskName(){}
>      public void setTaskName(String taskName){}
> }
> class Item {
>      public String getItemName(){}
>      public void setItemName(String itemName){}
> }
>
> With a map that contains:
>          map.put("taskName","tasks.taskName");
>          map.put("itemName","tasks.items.itemName");
>
> I can do:
> "&_s=taskName==Bob"
> but if I do
> "&_s=itemName==Bob"
> it fails because it tries to instantiate a List object - if I force that
> to be an ArrayList it then tries to setItemName on the ArrayList.
>
> Is it currently possible to make FIQL work through two layers of
> collections?
>
> Thanks
>
> Jim


-- 
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com