Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@JsonProperty not being mapped properly in PropertyUtils Katharsis (v 3.0.1) #433

Open
sankar1v opened this issue Jun 6, 2017 · 5 comments

Comments

@sankar1v
Copy link

sankar1v commented Jun 6, 2017

In my application we use @JsonProperty on field names in our POJOs to be JSON API compliant by following the recommended naming conventions. This worked perfectly fine earlier, but in 3.0.1.

We recently upgraded to version 3.0.1. @JsonProperty mapping of an "embedded" POJO breaks in PropertyUtils if the JSON property name differs from the POJO name.

So in our case we have something like below:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@JsonApiResource(type = "segments")
public class Segment implements Serializable {

    @Id
    @JsonApiId
    private String segmentId;

    @JsonProperty("name")
    private String name;

    private String language;
    private String country;

    @OneToMany(targetEntity = SubSegment.class, mappedBy = "segmentId", fetch = FetchType.EAGER)
    @Fetch(value = FetchMode.SUBSELECT)
    @JsonApiRelation(lookUp = LookupIncludeBehavior.AUTOMATICALLY_ALWAYS, serialize = SerializeType.ONLY_ID, opposite = "segment")
    @JsonProperty("sub-segments")
    private List<SubSegment> subSegments;

}
@Component
@Log4j
public class SegmentRepository extends ResourceRepositoryBase<Segment, String> {

    @Autowired
    SegmentService segmentService;

    public SegmentRepository() {
        super(Segment.class);
    }

    @Override
    public synchronized ResourceList<Segment> findAll(QuerySpec querySpec) {
        return segmentService.findAll(querySpec);
    }

    @Override
    public Segment findOne(String id, QuerySpec querySpec) {
        log.info("Received request to findOne by id:" + id);
        Segment greeting = segmentService.findOne(id);
        ResourceList<Segment> list = querySpec.apply(Arrays.asList(greeting));
        Segment result = list.get(0);
        return result;
    }

    @Override
    public Segment save(Segment segment) {
        return segmentService.save(segment);
    }
}
public interface SegmentCrudRepository extends CrudRepository<Segment, String> {
}

When I try to access SomeObject resource that contains this property outputPorts, below exception is thrown by Katharsis:

java.lang.IllegalStateException: field Segment.subSegments not found
at io.katharsis.repository.RelationshipRepositoryBase.getOppositeName(RelationshipRepositoryBase.java:240)
at io.katharsis.repository.RelationshipRepositoryBase.findTargets(RelationshipRepositoryBase.java:178)
at io.katharsis.core.internal.repository.adapter.RelationshipRepositoryAdapter$7.invoke(RelationshipRepositoryAdapter.java:223)
at io.katharsis.core.internal.repository.adapter.ResponseRepositoryAdapter$RepositoryBulkRequestFilterChainImpl.doFilter(ResponseRepositoryAdapter.java:222)
at io.katharsis.core.internal.repository.adapter.RelationshipRepositoryAdapter.findBulkManyTargets(RelationshipRepositoryAdapter.java:228)
at io.katharsis.core.internal.resource.IncludeLookupSetter.lookupRelationshipField(IncludeLookupSetter.java:238)
at io.katharsis.core.internal.resource.IncludeLookupSetter.populate(IncludeLookupSetter.java:133)
at io.katharsis.core.internal.resource.IncludeLookupSetter.setIncludedElements(IncludeLookupSetter.java:77)
at io.katharsis.core.internal.resource.DocumentMapper.addRelationDataAndInclusions(DocumentMapper.java:70)
at io.katharsis.core.internal.resource.DocumentMapper.toDocument(DocumentMapper.java:63)
at io.katharsis.core.internal.resource.DocumentMapper.toDocument(DocumentMapper.java:50)
at io.katharsis.core.internal.dispatcher.controller.CollectionGet.handle(CollectionGet.java:59)
at io.katharsis.core.internal.dispatcher.RequestDispatcher$DefaultFilterChain.doFilter(RequestDispatcher.java:126)
at io.katharsis.core.internal.dispatcher.RequestDispatcher.dispatchRequest(RequestDispatcher.java:80)
at io.katharsis.spring.KatharsisFilterV2.dispatchRequest(KatharsisFilterV2.java:131)
at io.katharsis.spring.KatharsisFilterV2.invoke(KatharsisFilterV2.java:105)
at io.katharsis.spring.KatharsisFilterV2.doFilter(KatharsisFilterV2.java:90)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at io.katharsis.spring.ErrorHandlerFilter.doFilterInternal(ErrorHandlerFilter.java:41)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
12:07:42.904 [http-nio-9090-exec-1] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
io.katharsis.invoker.internal.KatharsisInvokerException: java.lang.IllegalStateException: field SomeObject.outputPorts not found
at io.katharsis.spring.KatharsisFilterV2.invoke(KatharsisFilterV2.java:107)
at io.katharsis.spring.KatharsisFilterV2.doFilter(KatharsisFilterV2.java:90)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at io.katharsis.spring.ErrorHandlerFilter.doFilterInternal(ErrorHandlerFilter.java:41)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

So it seems like Katharsis is no longer deserializing that lookup to be at com.san.katharsis.example.SomeObject.outputPorts, but instead looking for a POJO with the @ JsonProperty annotated value.

Thanks all!

FYI, it seems similar issue exists in 2.8.2(#267), which is fixed later. But I could see the issue 3.0.1.

@chb0github
Copy link
Contributor

You're @Id annotation is not defined - I will go on the assumption that you meant @JsonApiId

Also, you're OutputPort is not defined. And, your repository class is incomplete.

If you have a working example of the problem I can have a look

@sankar1v
Copy link
Author

sankar1v commented Jun 7, 2017

Hi,

Please find the code here. I have renamed class names SomeObject to Segment & OutputPort to SubSegment. Code will work fine if I comment //@JsonProperty("sub-segments") in Segment.java. Otherwise it will fail when I try to access the url http://localhost:9090/api/segments.

java.lang.IllegalStateException: field Segment.subSegments not found
at 

san-katharsis-sample.zip

@sankar1v
Copy link
Author

@chb0github , Did you manage to check this ?

@chb0github
Copy link
Contributor

chb0github commented Jun 20, 2017 via email

@taylormathewson
Copy link

taylormathewson commented Nov 15, 2017

I've encountered a similar issue after switching jackson to kebab-case instead of default camel case.

Issue is where RelationshipRepositoryBase.getOppositeName calls resourceInformation.findRelationshipFieldByName which calls getJsonField which looks up by the name, not the underlying name. I have switched to dasherized/kebab-case serialization which is causing this problem. The resource information has a name like some-field and not someField, whereas underlyingName shows someField.

IMO, RelationshipRepositoryBase.getOppositeName should lead to a lookup by the underlyingName otherwise the serialization config (dasherized vs camel case) creeps into dtos/entities.

EDIT: clarity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants