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