Skip to content

Instantly share code, notes, and snippets.

@hakanai
Created February 14, 2017 00:38
Show Gist options
  • Select an option

  • Save hakanai/058191de1312c8dad2b6795e5c7e3c45 to your computer and use it in GitHub Desktop.

Select an option

Save hakanai/058191de1312c8dad2b6795e5c7e3c45 to your computer and use it in GitHub Desktop.
Artifactory comparison failure
2017-02-14 11:21:45,907 [http-nio-8081-exec-375] [ERROR] (o.a.r.c.e.GlobalExceptionMapper:48) - Comparison method violates its general contract!
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:744) ~[na:1.8.0_102]
at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:481) ~[na:1.8.0_102]
at java.util.ComparableTimSort.mergeCollapse(ComparableTimSort.java:406) ~[na:1.8.0_102]
at java.util.ComparableTimSort.sort(ComparableTimSort.java:213) ~[na:1.8.0_102]
at java.util.Arrays.sort(Arrays.java:1312) ~[na:1.8.0_102]
at java.util.Arrays.sort(Arrays.java:1506) ~[na:1.8.0_102]
at java.util.ArrayList.sort(ArrayList.java:1454) ~[na:1.8.0_102]
at java.util.Collections.sort(Collections.java:141) ~[na:1.8.0_102]
at org.artifactory.repo.service.RepositoryBrowsingServiceImpl.getRemoteRepoBrowsableChildrenData(RepositoryBrowsingServiceImpl.java:262) ~[artifactory-core-5.0.0.jar:na]
at org.artifactory.repo.service.RepositoryBrowsingServiceImpl.getRemoteRepoBrowsableChildren(RepositoryBrowsingServiceImpl.java:212) ~[artifactory-core-5.0.0.jar:na]
at org.artifactory.repo.service.RepositoryBrowsingServiceImpl.addVirtualBrowsableItemsFromRemote(RepositoryBrowsingServiceImpl.java:593) ~[artifactory-core-5.0.0.jar:na]
at org.artifactory.repo.service.RepositoryBrowsingServiceImpl.getVirtualBrowsableItemFromLocalAndRemote(RepositoryBrowsingServiceImpl.java:453) ~[artifactory-core-5.0.0.jar:na]
at org.artifactory.repo.service.RepositoryBrowsingServiceImpl.getVirtualRepoBrowsableChildrenData(RepositoryBrowsingServiceImpl.java:345) ~[artifactory-core-5.0.0.jar:na]
at org.artifactory.repo.service.RepositoryBrowsingServiceImpl.getVirtualRepoBrowsableChildren(RepositoryBrowsingServiceImpl.java:319) ~[artifactory-core-5.0.0.jar:na]
at org.artifactory.ui.rest.model.artifacts.browse.treebrowser.nodes.JunctionNode.getChildren(JunctionNode.java:115) ~[artifactory-rest-ui-5.0.0.jar:na]
at org.artifactory.ui.rest.model.artifacts.browse.treebrowser.nodes.JunctionNode.fetchItemTypeData(JunctionNode.java:88) ~[artifactory-rest-ui-5.0.0.jar:na]
at org.artifactory.ui.rest.service.artifacts.browse.treebrowser.tree.BrowseTreeNodesService.execute(BrowseTreeNodesService.java:41) ~[artifactory-rest-ui-5.0.0.jar:na]
at org.artifactory.rest.common.service.ServiceExecutor.process(ServiceExecutor.java:38) ~[artifactory-rest-common-5.0.0.jar:na]
at org.artifactory.rest.common.resource.BaseResource.runService(BaseResource.java:92) ~[artifactory-rest-common-5.0.0.jar:na]
at org.artifactory.ui.rest.resource.artifacts.browse.treebrowser.tree.TreeBrowserResource.browseTreeNodes(TreeBrowserResource.java:54) ~[artifactory-rest-ui-5.0.0.jar:na]
at sun.reflect.GeneratedMethodAccessor176.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_102]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_102]
at com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:302) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84) ~[jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1542) [jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1473) [jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1419) [jersey-server-1.19.jar:1.19]
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1409) [jersey-server-1.19.jar:1.19]
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:409) [jersey-servlet-1.19.jar:1.19]
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:558) [jersey-servlet-1.19.jar:1.19]
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:733) [jersey-servlet-1.19.jar:1.19]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [servlet-api.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) [catalina.jar:8.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [catalina.jar:8.0.39]
at org.artifactory.webapp.servlet.RepoFilter.execute(RepoFilter.java:185) [artifactory-web-application-5.0.0.jar:na]
at org.artifactory.webapp.servlet.RepoFilter.doFilter(RepoFilter.java:94) [artifactory-web-application-5.0.0.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [catalina.jar:8.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [catalina.jar:8.0.39]
at org.artifactory.webapp.servlet.AccessFilter.useAuthentication(AccessFilter.java:399) [artifactory-web-application-5.0.0.jar:na]
at org.artifactory.webapp.servlet.AccessFilter.doFilterInternal(AccessFilter.java:211) [artifactory-web-application-5.0.0.jar:na]
at org.artifactory.webapp.servlet.AccessFilter.doFilter(AccessFilter.java:165) [artifactory-web-application-5.0.0.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [catalina.jar:8.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [catalina.jar:8.0.39]
at org.artifactory.webapp.servlet.RequestFilter.doFilter(RequestFilter.java:67) [artifactory-web-application-5.0.0.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [catalina.jar:8.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [catalina.jar:8.0.39]
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:164) [spring-session-1.2.2.RELEASE.jar:na]
at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:80) [spring-session-1.2.2.RELEASE.jar:na]
at org.artifactory.webapp.servlet.SessionFilter.doFilter(SessionFilter.java:62) [artifactory-web-application-5.0.0.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [catalina.jar:8.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [catalina.jar:8.0.39]
at org.artifactory.webapp.servlet.ArtifactoryFilter.doFilter(ArtifactoryFilter.java:116) [artifactory-web-application-5.0.0.jar:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [catalina.jar:8.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [catalina.jar:8.0.39]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) [catalina.jar:8.0.39]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [catalina.jar:8.0.39]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) [catalina.jar:8.0.39]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [catalina.jar:8.0.39]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [catalina.jar:8.0.39]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:509) [catalina.jar:8.0.39]
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1104) [tomcat-coyote.jar:8.0.39]
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:684) [tomcat-coyote.jar:8.0.39]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520) [tomcat-coyote.jar:8.0.39]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476) [tomcat-coyote.jar:8.0.39]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_102]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_102]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.0.39]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_102]
@hakanai
Copy link
Copy Markdown
Author

hakanai commented Feb 14, 2017

Quick code audit. Essentially the comparators are not implemented the same as the equals methods, but also, in the case of VirtualBrowsableItem, the comparison isn't comparing apples with apples.

BrowsableItem:

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (!(o instanceof BrowsableItem)) {
        return false;
    }

    BrowsableItem that = (BrowsableItem) o;

    if (name != null ? !name.equals(that.name) : that.name != null) {
        return false;
    }

    return true;
}

@Override
public int compareTo(BaseBrowsableItem o) {
    if (name.equals(o.name)) {                  // <- Seems to be a hack to make equal names return 0
        return 0;
    }
    if (name.equals(UP) || (isFolder() && !o.isFolder())) {   // <- Suspicious - equals(Object) should also be checking isFolder()
        return -1;
    }
    if (o.name.equals(UP) || (!isFolder() && o.isFolder())) {
        return 1;
    }
    return name.compareTo(o.name);
}

VirtualBrowsableItem:

@Override
public boolean equals(Object o) {
    if (this == o) {
        return true;
    }
    if (o == null || getClass() != o.getClass()) {
        return false;
    }
    VirtualBrowsableItem item = (VirtualBrowsableItem) o;
    return repoPath.getPath().equals(item.getRelativePath());      // <- Suspicious - Comparing two different things.
                                                                   // Maybe repoPath.getPath() returns the same thing but it would still
                                                                   // be less suspicious to call getRelativePath().
}

@Override
public int compareTo(BaseBrowsableItem o) {
                                                // <- Suspicious - Hack applied to BrowsableItem is not present here.

    if (name.equals(UP) || (isFolder() && !o.isFolder())) {   // <- Suspicious - equals(Object) should also be checking isFolder()
        return -1;
    }

    if (o.name.equals(UP) || (!isFolder() && o.isFolder())) {
        return 1;
    }

    return this.repoPath.getPath().compareTo(o.getRelativePath());
}

@hakanai
Copy link
Copy Markdown
Author

hakanai commented Feb 14, 2017

What I would do:

  • Consider taking Comparable off the class. If you use a Comparator to sort instead, the equals method and the comparator's compare method do not have to be consistent, removing the problem. It also allows use of Java 8 lambda-style comparator building, which reduces the opportunity for errors.
  • If that isn't possible, implement equals(other) in the abstract class by returning compareTo(other) == 0, and make it final, which guarantees that all subclasses will be consistent. But having an abstract class implement Comparable is questionable anyway, unless the implementation of compareTo is also on the abstract class, so maybe move that up to the abstract class too.

The side-note is that I don't get why you would want to sort folders first anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment