Great news Sergey; thanks once again!

On Tue, Oct 28, 2014 at 1:00 PM, Sergey Beryozkin <[email protected]>
wrote:

> You are right, the issue was resolved as part of
> https://issues.apache.org/jira/browse/CXF-6067
>
> Many thanks for your help with identifying the issue
> Please try 2.7.14-SNAPSHOT a bit later
> Cheers, Sergey
>
> On 24/10/14 15:16, Lambert, Michael wrote:
>
>> Correction the problem method is ProviderFactory.getGenericInterfaces.
>> Its
>> not the comparator.
>>
>> On Thu, Oct 23, 2014 at 4:55 PM, Lambert, Michael <
>> [email protected]> wrote:
>>
>>  Fleshed it out a bit more with comment. It looks like the
>>> ProviderFactory.Comparator will choose whichever class in the
>>> parent/child
>>> lineage which implements ExceptionMapper and base its ordering on that.
>>> This means that a parent class cannot implement ExceptionMapper unless
>>> the
>>> child ALSO implments ExceptionMapper.
>>>
>>> package foo.test;
>>>
>>> import static org.junit.Assert.*;
>>>
>>> import javax.ws.rs.core.Response;
>>> import javax.ws.rs.ext.ExceptionMapper;
>>> import javax.ws.rs.ext.Provider;
>>>
>>> import org.apache.cxf.jaxrs.provider.ProviderFactory;
>>> import org.apache.cxf.message.MessageImpl;
>>>
>>> public class ExceptionMapperTest {
>>>   @org.junit.Before
>>> public void setup() {
>>> ProviderFactory.getInstance().clearProviders();
>>> }
>>>   @org.junit.Test
>>> public void testBadCustomExceptionMappersHierarchyWithGenerics() throws
>>> Exception {
>>>   ProviderFactory pf = ProviderFactory.getInstance();
>>>   BadExceptionMapperA badExceptionMapperA = new BadExceptionMapperA();
>>> pf.registerUserProvider(badExceptionMapperA);
>>>   BadExceptionMapperB badExceptionMapperB = new BadExceptionMapperB();
>>> pf.registerUserProvider(badExceptionMapperB);
>>>   Object mapperResponse1 =
>>> pf.createExceptionMapper(RuntimeExceptionA.class, new MessageImpl());
>>> assertSame(badExceptionMapperA, mapperResponse1);
>>>   Object mapperResponse2 =
>>> pf.createExceptionMapper(RuntimeExceptionB.class, new MessageImpl());
>>> assertSame(badExceptionMapperB, mapperResponse2);
>>>   Object mapperResponse3 =
>>> pf.createExceptionMapper(RuntimeExceptionAA.class, new MessageImpl());
>>> assertSame(badExceptionMapperA, mapperResponse3);
>>>   Object mapperResponse4 =
>>> pf.createExceptionMapper(RuntimeExceptionBB.class, new MessageImpl());
>>> assertSame(badExceptionMapperB, mapperResponse4);
>>> }
>>>   /**
>>>   * To fix the problem the mapper must NOT extend from a class that
>>> implements the ExceptionMapper interface. The parent class should be
>>>   * a normal java class and the mapper should implement the
>>> ExceptionMapper
>>> interface DIRECTLY!
>>>   *
>>>   * @throws Exception
>>>   */
>>> @org.junit.Test
>>> public void testFixedCustomExceptionMappersHierarchyWithGenerics()
>>> throws
>>> Exception {
>>>   ProviderFactory pf = ProviderFactory.getInstance();
>>>   FixedExceptionMapperA exceptionMapperA = new FixedExceptionMapperA();
>>> pf.registerUserProvider(exceptionMapperA);
>>>   FixedExceptionMapperB exceptionMapperB = new FixedExceptionMapperB();
>>> pf.registerUserProvider(exceptionMapperB);
>>>   Object mapperResponse1 =
>>> pf.createExceptionMapper(RuntimeExceptionA.class, new MessageImpl());
>>> assertSame(exceptionMapperA, mapperResponse1);
>>>   Object mapperResponse2 =
>>> pf.createExceptionMapper(RuntimeExceptionB.class, new MessageImpl());
>>> assertSame(exceptionMapperB, mapperResponse2);
>>>   Object mapperResponse3 =
>>> pf.createExceptionMapper(RuntimeExceptionAA.class, new MessageImpl());
>>> assertSame(exceptionMapperA, mapperResponse3);
>>>   Object mapperResponse4 =
>>> pf.createExceptionMapper(RuntimeExceptionBB.class, new MessageImpl());
>>> assertSame(exceptionMapperB, mapperResponse4);
>>> }
>>>
>>>
>>> @org.junit.Test
>>> public void testGoodExceptionMappersHierarchyWithGenerics() throws
>>> Exception {
>>>   ProviderFactory pf = ProviderFactory.getInstance();
>>>   GoodRuntimeExceptionAMapper runtimeExceptionAMapper = new
>>> GoodRuntimeExceptionAMapper();
>>> pf.registerUserProvider(runtimeExceptionAMapper);
>>>   GoodRuntimeExceptionBMapper runtimeExceptionBMapper = new
>>> GoodRuntimeExceptionBMapper();
>>> pf.registerUserProvider(runtimeExceptionBMapper);
>>>   Object mapperResponse1 =
>>> pf.createExceptionMapper(RuntimeExceptionA.class, new MessageImpl());
>>> assertSame(runtimeExceptionAMapper, mapperResponse1);
>>>   Object mapperResponse2 =
>>> pf.createExceptionMapper(RuntimeExceptionB.class, new MessageImpl());
>>> assertSame(runtimeExceptionBMapper, mapperResponse2);
>>>   Object mapperResponse3 =
>>> pf.createExceptionMapper(RuntimeExceptionAA.class, new MessageImpl());
>>> assertSame(runtimeExceptionAMapper, mapperResponse3);
>>>   Object mapperResponse4 =
>>> pf.createExceptionMapper(RuntimeExceptionBB.class, new MessageImpl());
>>> assertSame(runtimeExceptionBMapper, mapperResponse4);
>>> }
>>>   private class RuntimeExceptionA extends RuntimeException {
>>> private static final long serialVersionUID = 1L;
>>> }
>>>   private class RuntimeExceptionAA extends RuntimeExceptionA {
>>> private static final long serialVersionUID = 1L;
>>> }
>>>   private class RuntimeExceptionB extends RuntimeException {
>>> private static final long serialVersionUID = 1L;
>>> }
>>>   private class RuntimeExceptionBB extends RuntimeExceptionB {
>>> private static final long serialVersionUID = 1L;
>>> }
>>>   private class GoodRuntimeExceptionAMapper implements
>>> ExceptionMapper<RuntimeExceptionA> {
>>>
>>> @Override
>>> public Response toResponse(RuntimeExceptionA exception) {
>>> // TODO Auto-generated method stub
>>> return null;
>>> }
>>> }
>>>   private class GoodRuntimeExceptionBMapper implements
>>> ExceptionMapper<RuntimeExceptionB> {
>>>
>>> @Override
>>> public Response toResponse(RuntimeExceptionB exception) {
>>> // TODO Auto-generated method stub
>>> return null;
>>> }
>>> }
>>>   /**
>>>   * The parent implements ExceptionMapper and as a result will be what
>>> the
>>> ProviderFactory.Comparator class incorrectly chooses when sort ordering
>>>   * the mappers rather than its children
>>>   *
>>>   */
>>> private abstract class BadParentExceptionMapper<T extends Throwable>
>>> implements ExceptionMapper<T> {
>>> }
>>>   /**
>>>   * This does not implement ExceptionMapper and so its parent will be
>>> incorrectly selected by he ProviderFactory.Comparator class for sort
>>> ordering
>>>   *
>>>   */
>>> @Provider
>>> private class BadExceptionMapperA extends
>>> BadParentExceptionMapper<RuntimeExceptionA> {
>>>
>>> @Override
>>> public Response toResponse(RuntimeExceptionA exception) {
>>> return null;
>>> }
>>> }
>>>   /**
>>>   * This does not implement ExceptionMapper and so its parent will be
>>> incorrectly selected by he ProviderFactory.Comparator class for sort
>>> ordering
>>>   *
>>>   */
>>> @Provider
>>> private class BadExceptionMapperB extends
>>> BadParentExceptionMapper<RuntimeExceptionB> {
>>>
>>> @Override
>>> public Response toResponse(RuntimeExceptionB exception) {
>>> return null;
>>> }
>>> }
>>>   /**
>>>   * This does NOT implement ExceptionMapper
>>>   *
>>>   */
>>> private abstract class FixedParentExceptionMapper {
>>> }
>>>   /**
>>>   * The mapper implements ExceptionMapper directly
>>>   *
>>>   */
>>> @Provider
>>> private class FixedExceptionMapperA extends FixedParentExceptionMapper
>>> implements ExceptionMapper<RuntimeExceptionA> {
>>>
>>> @Override
>>> public Response toResponse(RuntimeExceptionA exception) {
>>> return null;
>>> }
>>> }
>>>   /**
>>>   * The mapper implements ExceptionMapper directly
>>>   *
>>>   */
>>> @Provider
>>> private class FixedExceptionMapperB extends FixedParentExceptionMapper
>>> implements ExceptionMapper<RuntimeExceptionB> {
>>>
>>> @Override
>>> public Response toResponse(RuntimeExceptionB exception) {
>>> return null;
>>> }
>>> }
>>> }
>>>
>>>
>>> On Thu, Oct 23, 2014 at 4:30 PM, Lambert, Michael <
>>> [email protected]> wrote:
>>>
>>>  Sergei,
>>>>
>>>> Here is a test case that demonstrates the problem. Note that the only
>>>> difference between the good test and the bad test is that the mapper
>>>> itself
>>>> is derived from a parent class. This test is based on cxf 2.7.11 but i
>>>> presume it will also work with 3.0 by swapping out the providerfactory
>>>> class with serviceproviderfactory.
>>>>
>>>>
>>>> package foo.test;
>>>>
>>>> import static org.junit.Assert.*;
>>>>
>>>> import javax.ws.rs.core.Response;
>>>> import javax.ws.rs.ext.ExceptionMapper;
>>>> import javax.ws.rs.ext.Provider;
>>>>
>>>> import org.apache.cxf.jaxrs.provider.ProviderFactory;
>>>> import org.apache.cxf.message.MessageImpl;
>>>>
>>>> public class ExceptionMapperTest {
>>>>   @org.junit.Before
>>>> public void setup() {
>>>> ProviderFactory.getInstance().clearProviders();
>>>> }
>>>>   @org.junit.Test
>>>> public void testBadCustomExceptionMappersHierarchyWithGenerics() throws
>>>> Exception {
>>>>   ProviderFactory pf = ProviderFactory.getInstance();
>>>>   BadExceptionMapperA badExceptionMapperA = new BadExceptionMapperA();
>>>> pf.registerUserProvider(badExceptionMapperA);
>>>>   BadExceptionMapperB badExceptionMapperB = new BadExceptionMapperB();
>>>> pf.registerUserProvider(badExceptionMapperB);
>>>>   Object mapperResponse1 =
>>>> pf.createExceptionMapper(RuntimeExceptionA.class, new MessageImpl());
>>>> assertSame(badExceptionMapperA, mapperResponse1);
>>>>   Object mapperResponse2 =
>>>> pf.createExceptionMapper(RuntimeExceptionB.class, new MessageImpl());
>>>> assertSame(badExceptionMapperB, mapperResponse2);
>>>>   Object mapperResponse3 =
>>>> pf.createExceptionMapper(RuntimeExceptionAA.class, new MessageImpl());
>>>> assertSame(badExceptionMapperA, mapperResponse3);
>>>>   Object mapperResponse4 =
>>>> pf.createExceptionMapper(RuntimeExceptionBB.class, new MessageImpl());
>>>> assertSame(badExceptionMapperB, mapperResponse4);
>>>> }
>>>>
>>>> @org.junit.Test
>>>> public void testGoodExceptionMappersHierarchyWithGenerics() throws
>>>> Exception {
>>>>   ProviderFactory pf = ProviderFactory.getInstance();
>>>>   GoodRuntimeExceptionAMapper runtimeExceptionAMapper = new
>>>> GoodRuntimeExceptionAMapper();
>>>> pf.registerUserProvider(runtimeExceptionAMapper);
>>>>   GoodRuntimeExceptionBMapper runtimeExceptionBMapper = new
>>>> GoodRuntimeExceptionBMapper();
>>>> pf.registerUserProvider(runtimeExceptionBMapper);
>>>>   Object mapperResponse1 =
>>>> pf.createExceptionMapper(RuntimeExceptionA.class, new MessageImpl());
>>>> assertSame(runtimeExceptionAMapper, mapperResponse1);
>>>>   Object mapperResponse2 =
>>>> pf.createExceptionMapper(RuntimeExceptionB.class, new MessageImpl());
>>>> assertSame(runtimeExceptionBMapper, mapperResponse2);
>>>>   Object mapperResponse3 =
>>>> pf.createExceptionMapper(RuntimeExceptionAA.class, new MessageImpl());
>>>> assertSame(runtimeExceptionAMapper, mapperResponse3);
>>>>   Object mapperResponse4 =
>>>> pf.createExceptionMapper(RuntimeExceptionBB.class, new MessageImpl());
>>>> assertSame(runtimeExceptionBMapper, mapperResponse4);
>>>> }
>>>>   private class RuntimeExceptionA extends RuntimeException {
>>>> private static final long serialVersionUID = 1L;
>>>> }
>>>>   private class RuntimeExceptionAA extends RuntimeExceptionA {
>>>> private static final long serialVersionUID = 1L;
>>>> }
>>>>   private class RuntimeExceptionB extends RuntimeException {
>>>> private static final long serialVersionUID = 1L;
>>>> }
>>>>   private class RuntimeExceptionBB extends RuntimeExceptionB {
>>>> private static final long serialVersionUID = 1L;
>>>> }
>>>>   private class GoodRuntimeExceptionAMapper implements
>>>> ExceptionMapper<RuntimeExceptionA> {
>>>>
>>>> @Override
>>>> public Response toResponse(RuntimeExceptionA exception) {
>>>> // TODO Auto-generated method stub
>>>> return null;
>>>> }
>>>> }
>>>>   private class GoodRuntimeExceptionBMapper implements
>>>> ExceptionMapper<RuntimeExceptionB> {
>>>>
>>>> @Override
>>>> public Response toResponse(RuntimeExceptionB exception) {
>>>> // TODO Auto-generated method stub
>>>> return null;
>>>> }
>>>> }
>>>>   public abstract class BadParentExceptionMapper<T extends Throwable>
>>>> implements ExceptionMapper<T> {
>>>> }
>>>>   @Provider
>>>> public class BadExceptionMapperA extends
>>>> BadParentExceptionMapper<RuntimeExceptionA> {
>>>>
>>>> @Override
>>>> public Response toResponse(RuntimeExceptionA exception) {
>>>> return null;
>>>> }
>>>> }
>>>>   @Provider
>>>> public class BadExceptionMapperB extends
>>>> BadParentExceptionMapper<RuntimeExceptionB> {
>>>>
>>>> @Override
>>>> public Response toResponse(RuntimeExceptionB exception) {
>>>> return null;
>>>> }
>>>> }
>>>> }
>>>>
>>>>
>>>>
>>>> On Wed, Oct 22, 2014 at 8:06 AM, Lambert, Michael <
>>>> [email protected]> wrote:
>>>>
>>>>  Good to know Sergey we will look at it again today. Definitely
>>>>> experiencing the behavior I described so we will look for some delta
>>>>> between our code and the test
>>>>> On Oct 22, 2014 5:50 AM, "Sergey Beryozkin" <[email protected]>
>>>>> wrote:
>>>>>
>>>>>  ProviderFactoryTest has the following:
>>>>>>
>>>>>> private static class RuntimeExceptionMapper1
>>>>>>          extends AbstractTestExceptionMapper<RuntimeException> {
>>>>>>
>>>>>>      }
>>>>>>
>>>>>>      private static class RuntimeExceptionMapper2
>>>>>>          extends AbstractTestExceptionMapper<WebApplicationException>
>>>>>> {
>>>>>>
>>>>>>      }
>>>>>>
>>>>>>      private static class AbstractTestExceptionMapper<T extends
>>>>>> RuntimeException>
>>>>>>          implements ExceptionMapper<T> {
>>>>>>
>>>>>>          public Response toResponse(T arg0) {
>>>>>>              // TODO Auto-generated method stub
>>>>>>              return null;
>>>>>>          }
>>>>>>
>>>>>>      }
>>>>>>
>>>>>> This is identical to your example where you say the bug is expected,
>>>>>> also note WebApplicationException is RuntimeException.
>>>>>>
>>>>>> @Test
>>>>>>      public void testExceptionMappersHierarchyWithGenerics() throws
>>>>>> Exception {
>>>>>>          ServerProviderFactory pf = ServerProviderFactory.
>>>>>> getInstance();
>>>>>>          RuntimeExceptionMapper1 exMapper1 = new
>>>>>> RuntimeExceptionMapper1();
>>>>>>          pf.registerUserProvider(exMapper1);
>>>>>>          RuntimeExceptionMapper2 exMapper2 = new
>>>>>> RuntimeExceptionMapper2();
>>>>>>          pf.registerUserProvider(exMapper2);
>>>>>>          assertSame(exMapper1, pf.createExceptionMapper(
>>>>>> RuntimeException.class,
>>>>>> new MessageImpl()));
>>>>>>          Object webExMapper = pf.createExceptionMapper(
>>>>>> WebApplicationException.class,
>>>>>> new MessageImpl());
>>>>>>          assertSame(exMapper2, webExMapper);
>>>>>>      }
>>>>>>
>>>>>> When RuntimeExceptionMapper2 is registered, RuntimeExceptionMapper1 is
>>>>>> not picked for WebApplicationException. Updating the test not to
>>>>>> register
>>>>>> RuntimeExceptionMapper2 leads to RuntimeExceptionMapper1 being
>>>>>> selected for
>>>>>> WebApplicationException.
>>>>>>
>>>>>> Looks like it is all correct to me, may be the issue is there in
>>>>>> 2.7.11
>>>>>> but def not on the trunk/3.0.2
>>>>>>
>>>>>> Cheers, Sergey
>>>>>>
>>>>>> On 21/10/14 20:04, Lambert, Michael wrote:
>>>>>>
>>>>>>  To be clear:
>>>>>>>
>>>>>>> This bug will only manifest itself if two exception mapper are both
>>>>>>> derived
>>>>>>> from the same interface:
>>>>>>>
>>>>>>>       public class AbstractMapper<T extends Throwable> implements
>>>>>>> ExceptionMapper<T> {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>>       public class FooMapper extends AbstractMapper<FooException> {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>>       // ProviderFactory.ClassComparator.compare will return "0"
>>>>>>> when
>>>>>>> BarMapper is compared to FooMapper
>>>>>>>       public class BarMapper extends AbstractMapper<BarException> {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>> If each exception mapper instance is derived from a DIFFERENT
>>>>>>> INTERFACE
>>>>>>> everything works fine:
>>>>>>>
>>>>>>>       public interface IFooMapper extends
>>>>>>> ExceptionMapper<FooException>
>>>>>>> {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>>       public class FooMapper extends implements IFooMapper  {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>>       public interface IBarMapper extends
>>>>>>> ExceptionMapper<BarException>
>>>>>>> {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>>       // ProviderFactory.ClassComparator.compare will return "1" or
>>>>>>> -1
>>>>>>> when
>>>>>>> BarMapper is compared to FooMapper
>>>>>>>       public class BarMapper extends implements IBarMapper {
>>>>>>>           ...
>>>>>>>       }
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Oct 21, 2014 at 2:34 PM, Lambert, Michael <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>   Guys... it looks like there is a bug in ProviderFactory$
>>>>>>>
>>>>>>>> ClassComparator.
>>>>>>>> Given two mappers it always ends up comparing them as if both
>>>>>>>> mappers
>>>>>>>> were
>>>>>>>> ExceptionMapper<Throwable>. There must be a problem in the way the
>>>>>>>> interfaces and "actual types" are derived (which is done in the
>>>>>>>> InjectionUtils class).
>>>>>>>>
>>>>>>>> -Mike
>>>>>>>>
>>>>>>>> On Tue, Oct 21, 2014 at 1:24 PM, Lambert, Michael <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>   Hello gentleman!
>>>>>>>>
>>>>>>>>>
>>>>>>>>> My impression was that when implementing ExceptionMapper that the
>>>>>>>>> mapper
>>>>>>>>> of the nearest superclass of an exception would be used if an exact
>>>>>>>>> exceptionmapper match to the exception didnt exist.
>>>>>>>>>
>>>>>>>>> For example if I created a FooException which extended BarException
>>>>>>>>> I
>>>>>>>>> would only have to create a BarExceptionMapper to catch and map
>>>>>>>>> both
>>>>>>>>> types
>>>>>>>>> of exceptions; the BarExceptionMapper would be used when a
>>>>>>>>> FooException was
>>>>>>>>> thrown.
>>>>>>>>>
>>>>>>>>> The red hat docs seem to reinforce this opinion:
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>      -
>>>>>>>>>
>>>>>>>>>      When any exception other than a WebApplicationException
>>>>>>>>> exception, or
>>>>>>>>>      one of its subclasses, is thrown, the runtime will check for
>>>>>>>>> an
>>>>>>>>> appropriate
>>>>>>>>>      exception mapper. An exception mapper is selected if it
>>>>>>>>> handles
>>>>>>>>> the
>>>>>>>>>      specific exception thrown. If there is not an exception mapper
>>>>>>>>> for the
>>>>>>>>>      specific exception that was thrown, the exception mapper for
>>>>>>>>> the
>>>>>>>>> nearest
>>>>>>>>>      superclass of the exception is selected.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> https://access.redhat.com/documentation/en-US/Red_Hat_
>>>>>>>>> JBoss_Fuse/6.1/html/Apache_CXF_Development_Guide/files/
>>>>>>>>> RESTExceptionMapper.html
>>>>>>>>>
>>>>>>>>> The reality is different for us however. In fact, if we create a
>>>>>>>>> custom
>>>>>>>>> WebApplicationExceptionMapper ClientErrorExceptions are not caught
>>>>>>>>> by the
>>>>>>>>> mapper. CXF seems to default to attempt to use whichever Mapper is
>>>>>>>>> the
>>>>>>>>> first one we registered (e.g. if we defined a FooExceptionMapper
>>>>>>>>> and
>>>>>>>>> registered it, CXF would attempt to use that and throw an error
>>>>>>>>> rather than
>>>>>>>>> try to use the WebApplicationExceptionMapper).
>>>>>>>>>
>>>>>>>>> Can someone tell me what the expected behavior is? Is my question
>>>>>>>>> clear?
>>>>>>>>>
>>>>>>>>> We are using CXF version 2.7.11.
>>>>>>>>>
>>>>>>>>> Thanks!
>>>>>>>>>
>>>>>>>>> Mike
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>> --
>>>>>> Sergey Beryozkin
>>>>>>
>>>>>> Talend Community Coders
>>>>>> http://coders.talend.com/
>>>>>>
>>>>>> Blog: http://sberyozkin.blogspot.com
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>

Reply via email to