{"action":"created","comment":{"url":"https://api.github.com/repos/druid-io/druid/pulls/comments/125367680","pull_request_review_id":47750031,"id":125367680,"diff_hunk":"@@ -0,0 +1,50 @@\n+/*\n+ * Licensed to Metamarkets Group Inc. (Metamarkets) under one\n+ * or more contributor license agreements. See the NOTICE file\n+ * distributed with this work for additional information\n+ * regarding copyright ownership. Metamarkets licenses this file\n+ * to you under the Apache License, Version 2.0 (the\n+ * \"License\"); you may not use this file except in compliance\n+ * with the License. You may obtain a copy of the License at\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing,\n+ * software distributed under the License is distributed on an\n+ * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ * KIND, either express or implied. See the License for the\n+ * specific language governing permissions and limitations\n+ * under the License.\n+ */\n+\n+package io.druid.guice.annotations;\n+\n+import java.lang.annotation.ElementType;\n+import java.lang.annotation.Retention;\n+import java.lang.annotation.RetentionPolicy;\n+import java.lang.annotation.Target;\n+\n+/**\n+ * Signifies that the annotated entity is an extension point. Extension points are interfaces or non-final classes that\n+ * may be subclassed in extensions in order to add functionality to Druid. Extension points may change in breaking ways\n+ * only between major Druid release lines (e.g. 0.10.x -> 0.11.0), but otherwise must remain stable. Extension points\n+ * may change at any time in non-breaking ways, however, such as by adding new default methods to an interface.\n+ *\n+ * All public and protected fields, methods, and constructors of annotated classes and interfaces are considered\n+ * stable in this sense. If a class is not annotated, but an individual field, method, or constructor is\n+ * annotated, then only that particular field, method, or constructor is considered an extension API.\n+ *\n+ * Extension points are all considered public APIs in the sense of {@link PublicApi}, even if not explicitly annotated\n+ * as such.\n+ *\n+ * Note that there are number of injectable interfaces that are not annotated with {@code ExtensionPoint}. You may\n+ * still extend these interfaces in extensions, but your extension may need to be recompiled even for a minor\n+ * update of Druid.\n+ *\n+ * @see PublicApi\n+ */\n+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})","path":"api/src/main/java/io/druid/guice/annotations/ExtensionPoint.java","position":46,"original_position":46,"commit_id":"100ca4d5956bff611f8f21fbe9ac5b62831701a9","original_commit_id":"100ca4d5956bff611f8f21fbe9ac5b62831701a9","user":{"login":"leventov","id":609240,"avatar_url":"https://avatars3.githubusercontent.com/u/609240?v=3","gravatar_id":"","url":"https://api.github.com/users/leventov","html_url":"https://github.com/leventov","followers_url":"https://api.github.com/users/leventov/followers","following_url":"https://api.github.com/users/leventov/following{/other_user}","gists_url":"https://api.github.com/users/leventov/gists{/gist_id}","starred_url":"https://api.github.com/users/leventov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leventov/subscriptions","organizations_url":"https://api.github.com/users/leventov/orgs","repos_url":"https://api.github.com/users/leventov/repos","events_url":"https://api.github.com/users/leventov/events{/privacy}","received_events_url":"https://api.github.com/users/leventov/received_events","type":"User","site_admin":false},"body":"How could a field, method or constructor be an extension point?","created_at":"2017-07-03T23:33:23Z","updated_at":"2017-07-03T23:42:22Z","html_url":"https://github.com/druid-io/druid/pull/4433#discussion_r125367680","pull_request_url":"https://api.github.com/repos/druid-io/druid/pulls/4433","_links":{"self":{"href":"https://api.github.com/repos/druid-io/druid/pulls/comments/125367680"},"html":{"href":"https://github.com/druid-io/druid/pull/4433#discussion_r125367680"},"pull_request":{"href":"https://api.github.com/repos/druid-io/druid/pulls/4433"}}},"pull_request":{"url":"https://api.github.com/repos/druid-io/druid/pulls/4433","id":126660537,"html_url":"https://github.com/druid-io/druid/pull/4433","diff_url":"https://github.com/druid-io/druid/pull/4433.diff","patch_url":"https://github.com/druid-io/druid/pull/4433.patch","issue_url":"https://api.github.com/repos/druid-io/druid/issues/4433","number":4433,"state":"open","locked":false,"title":"Add @ExtensionPoint and @PublicApi annotations.","user":{"login":"gianm","id":1214075,"avatar_url":"https://avatars0.githubusercontent.com/u/1214075?v=3","gravatar_id":"","url":"https://api.github.com/users/gianm","html_url":"https://github.com/gianm","followers_url":"https://api.github.com/users/gianm/followers","following_url":"https://api.github.com/users/gianm/following{/other_user}","gists_url":"https://api.github.com/users/gianm/gists{/gist_id}","starred_url":"https://api.github.com/users/gianm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gianm/subscriptions","organizations_url":"https://api.github.com/users/gianm/orgs","repos_url":"https://api.github.com/users/gianm/repos","events_url":"https://api.github.com/users/gianm/events{/privacy}","received_events_url":"https://api.github.com/users/gianm/received_events","type":"User","site_admin":false},"body":"This is an attempt to clarify questions about what is and is not a public API (see https://github.com/druid-io/druid/pull/4131#discussion_r108780846, https://github.com/druid-io/druid/pull/4394#discussion_r123125180, https://github.com/druid-io/druid/pull/4254#discussion_r121013256). It adds two new annotations: `@ExtensionPoint` which signifies something you can subclass, and `@PublicApi` which signifies something you're not meant to subclass, but that you can use for implementation. The idea is that if an extension author sticks to public APIs then their extension will keep working for at least an entire major Druid line.\r\n\r\nExtension authors are of course free to use non-public APIs, but their extensions might break randomly.\r\n\r\nFeedback on the specific list of annotated and non-annotated items is welcome.\r\n\r\nSome notes:\r\n\r\n- The list of public apis is somewhat subjective, but I did my best guess based on the current contents of http://druid.io/docs/latest/development/modules.html and based on recent discussions about what is and isn't public. In general I erred on the side of annotating _fewer_ rather than _more_ entities, since I figured that annotating too many entities would make too much friction for dev of core Druid.\r\n- Speaking of subjectivity: relating to the last comment in the list above (https://github.com/druid-io/druid/pull/4254#discussion_r121013256) I actually think CSVParseSpec shouldn't be considered a public api, since it's not really useful for extensions. So, I didn't annotate it. Tranquility does use it, but Tranquility really blatantly uses a ton of private Druid APIs. It should really have no expectation of being able to run with any Druid release other than the one it was built with.\r\n- Nothing in java-util is annotated since the annotations are in druid-api, and java-util doesn't depend on druid-api. We could deal with this by either saying \"everything in java-util is public\", or alternatively, creating a new package druid-annotations that java-util and druid-api both depend on.\r\n- I'm not sure I got the annotations for the query metrics stuff right (see #3954). @leventov could you double check if I captured your intent? What I did was annotate `GenericQueryMetricsFactory` as public (since AFAIK any extension query would need to call methods on it) but nothing else in the query metrics world.","created_at":"2017-06-21T01:04:42Z","updated_at":"2017-07-03T23:42:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ff57f73e236a9e5e70638f8629285f3a85121aa1","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/druid-io/druid/pulls/4433/commits","review_comments_url":"https://api.github.com/repos/druid-io/druid/pulls/4433/comments","review_comment_url":"https://api.github.com/repos/druid-io/druid/pulls/comments{/number}","comments_url":"https://api.github.com/repos/druid-io/druid/issues/4433/comments","statuses_url":"https://api.github.com/repos/druid-io/druid/statuses/100ca4d5956bff611f8f21fbe9ac5b62831701a9","head":{"label":"gianm:extension-annotations","ref":"extension-annotations","sha":"100ca4d5956bff611f8f21fbe9ac5b62831701a9","user":{"login":"gianm","id":1214075,"avatar_url":"https://avatars0.githubusercontent.com/u/1214075?v=3","gravatar_id":"","url":"https://api.github.com/users/gianm","html_url":"https://github.com/gianm","followers_url":"https://api.github.com/users/gianm/followers","following_url":"https://api.github.com/users/gianm/following{/other_user}","gists_url":"https://api.github.com/users/gianm/gists{/gist_id}","starred_url":"https://api.github.com/users/gianm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gianm/subscriptions","organizations_url":"https://api.github.com/users/gianm/orgs","repos_url":"https://api.github.com/users/gianm/repos","events_url":"https://api.github.com/users/gianm/events{/privacy}","received_events_url":"https://api.github.com/users/gianm/received_events","type":"User","site_admin":false},"repo":{"id":28350072,"name":"druid","full_name":"gianm/druid","owner":{"login":"gianm","id":1214075,"avatar_url":"https://avatars0.githubusercontent.com/u/1214075?v=3","gravatar_id":"","url":"https://api.github.com/users/gianm","html_url":"https://github.com/gianm","followers_url":"https://api.github.com/users/gianm/followers","following_url":"https://api.github.com/users/gianm/following{/other_user}","gists_url":"https://api.github.com/users/gianm/gists{/gist_id}","starred_url":"https://api.github.com/users/gianm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gianm/subscriptions","organizations_url":"https://api.github.com/users/gianm/orgs","repos_url":"https://api.github.com/users/gianm/repos","events_url":"https://api.github.com/users/gianm/events{/privacy}","received_events_url":"https://api.github.com/users/gianm/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gianm/druid","description":"Real²time Exploratory Analytics on Large Datasets","fork":true,"url":"https://api.github.com/repos/gianm/druid","forks_url":"https://api.github.com/repos/gianm/druid/forks","keys_url":"https://api.github.com/repos/gianm/druid/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gianm/druid/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gianm/druid/teams","hooks_url":"https://api.github.com/repos/gianm/druid/hooks","issue_events_url":"https://api.github.com/repos/gianm/druid/issues/events{/number}","events_url":"https://api.github.com/repos/gianm/druid/events","assignees_url":"https://api.github.com/repos/gianm/druid/assignees{/user}","branches_url":"https://api.github.com/repos/gianm/druid/branches{/branch}","tags_url":"https://api.github.com/repos/gianm/druid/tags","blobs_url":"https://api.github.com/repos/gianm/druid/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gianm/druid/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gianm/druid/git/refs{/sha}","trees_url":"https://api.github.com/repos/gianm/druid/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gianm/druid/statuses/{sha}","languages_url":"https://api.github.com/repos/gianm/druid/languages","stargazers_url":"https://api.github.com/repos/gianm/druid/stargazers","contributors_url":"https://api.github.com/repos/gianm/druid/contributors","subscribers_url":"https://api.github.com/repos/gianm/druid/subscribers","subscription_url":"https://api.github.com/repos/gianm/druid/subscription","commits_url":"https://api.github.com/repos/gianm/druid/commits{/sha}","git_commits_url":"https://api.github.com/repos/gianm/druid/git/commits{/sha}","comments_url":"https://api.github.com/repos/gianm/druid/comments{/number}","issue_comment_url":"https://api.github.com/repos/gianm/druid/issues/comments{/number}","contents_url":"https://api.github.com/repos/gianm/druid/contents/{+path}","compare_url":"https://api.github.com/repos/gianm/druid/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gianm/druid/merges","archive_url":"https://api.github.com/repos/gianm/druid/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gianm/druid/downloads","issues_url":"https://api.github.com/repos/gianm/druid/issues{/number}","pulls_url":"https://api.github.com/repos/gianm/druid/pulls{/number}","milestones_url":"https://api.github.com/repos/gianm/druid/milestones{/number}","notifications_url":"https://api.github.com/repos/gianm/druid/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gianm/druid/labels{/name}","releases_url":"https://api.github.com/repos/gianm/druid/releases{/id}","deployments_url":"https://api.github.com/repos/gianm/druid/deployments","created_at":"2014-12-22T17:32:01Z","updated_at":"2016-01-18T01:27:33Z","pushed_at":"2017-07-01T00:36:09Z","git_url":"git://github.com/gianm/druid.git","ssh_url":"git@github.com:gianm/druid.git","clone_url":"https://github.com/gianm/druid.git","svn_url":"https://github.com/gianm/druid","homepage":"http://druid.io","size":122822,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"druid-io:master","ref":"master","sha":"679cf277c07f9df84ea6b2fec11afc44b798341f","user":{"login":"druid-io","id":4203474,"avatar_url":"https://avatars3.githubusercontent.com/u/4203474?v=3","gravatar_id":"","url":"https://api.github.com/users/druid-io","html_url":"https://github.com/druid-io","followers_url":"https://api.github.com/users/druid-io/followers","following_url":"https://api.github.com/users/druid-io/following{/other_user}","gists_url":"https://api.github.com/users/druid-io/gists{/gist_id}","starred_url":"https://api.github.com/users/druid-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/druid-io/subscriptions","organizations_url":"https://api.github.com/users/druid-io/orgs","repos_url":"https://api.github.com/users/druid-io/repos","events_url":"https://api.github.com/users/druid-io/events{/privacy}","received_events_url":"https://api.github.com/users/druid-io/received_events","type":"Organization","site_admin":false},"repo":{"id":6358188,"name":"druid","full_name":"druid-io/druid","owner":{"login":"druid-io","id":4203474,"avatar_url":"https://avatars3.githubusercontent.com/u/4203474?v=3","gravatar_id":"","url":"https://api.github.com/users/druid-io","html_url":"https://github.com/druid-io","followers_url":"https://api.github.com/users/druid-io/followers","following_url":"https://api.github.com/users/druid-io/following{/other_user}","gists_url":"https://api.github.com/users/druid-io/gists{/gist_id}","starred_url":"https://api.github.com/users/druid-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/druid-io/subscriptions","organizations_url":"https://api.github.com/users/druid-io/orgs","repos_url":"https://api.github.com/users/druid-io/repos","events_url":"https://api.github.com/users/druid-io/events{/privacy}","received_events_url":"https://api.github.com/users/druid-io/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/druid-io/druid","description":"Column oriented distributed data store ideal for powering interactive applications","fork":false,"url":"https://api.github.com/repos/druid-io/druid","forks_url":"https://api.github.com/repos/druid-io/druid/forks","keys_url":"https://api.github.com/repos/druid-io/druid/keys{/key_id}","collaborators_url":"https://api.github.com/repos/druid-io/druid/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/druid-io/druid/teams","hooks_url":"https://api.github.com/repos/druid-io/druid/hooks","issue_events_url":"https://api.github.com/repos/druid-io/druid/issues/events{/number}","events_url":"https://api.github.com/repos/druid-io/druid/events","assignees_url":"https://api.github.com/repos/druid-io/druid/assignees{/user}","branches_url":"https://api.github.com/repos/druid-io/druid/branches{/branch}","tags_url":"https://api.github.com/repos/druid-io/druid/tags","blobs_url":"https://api.github.com/repos/druid-io/druid/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/druid-io/druid/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/druid-io/druid/git/refs{/sha}","trees_url":"https://api.github.com/repos/druid-io/druid/git/trees{/sha}","statuses_url":"https://api.github.com/repos/druid-io/druid/statuses/{sha}","languages_url":"https://api.github.com/repos/druid-io/druid/languages","stargazers_url":"https://api.github.com/repos/druid-io/druid/stargazers","contributors_url":"https://api.github.com/repos/druid-io/druid/contributors","subscribers_url":"https://api.github.com/repos/druid-io/druid/subscribers","subscription_url":"https://api.github.com/repos/druid-io/druid/subscription","commits_url":"https://api.github.com/repos/druid-io/druid/commits{/sha}","git_commits_url":"https://api.github.com/repos/druid-io/druid/git/commits{/sha}","comments_url":"https://api.github.com/repos/druid-io/druid/comments{/number}","issue_comment_url":"https://api.github.com/repos/druid-io/druid/issues/comments{/number}","contents_url":"https://api.github.com/repos/druid-io/druid/contents/{+path}","compare_url":"https://api.github.com/repos/druid-io/druid/compare/{base}...{head}","merges_url":"https://api.github.com/repos/druid-io/druid/merges","archive_url":"https://api.github.com/repos/druid-io/druid/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/druid-io/druid/downloads","issues_url":"https://api.github.com/repos/druid-io/druid/issues{/number}","pulls_url":"https://api.github.com/repos/druid-io/druid/pulls{/number}","milestones_url":"https://api.github.com/repos/druid-io/druid/milestones{/number}","notifications_url":"https://api.github.com/repos/druid-io/druid/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/druid-io/druid/labels{/name}","releases_url":"https://api.github.com/repos/druid-io/druid/releases{/id}","deployments_url":"https://api.github.com/repos/druid-io/druid/deployments","created_at":"2012-10-23T19:08:07Z","updated_at":"2017-07-03T22:29:12Z","pushed_at":"2017-07-03T18:41:50Z","git_url":"git://github.com/druid-io/druid.git","ssh_url":"git@github.com:druid-io/druid.git","clone_url":"https://github.com/druid-io/druid.git","svn_url":"https://github.com/druid-io/druid","homepage":"http://druid.io","size":122583,"stargazers_count":5069,"watchers_count":5069,"language":"Java","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1230,"mirror_url":null,"open_issues_count":735,"forks":1230,"open_issues":735,"watchers":5069,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/druid-io/druid/pulls/4433"},"html":{"href":"https://github.com/druid-io/druid/pull/4433"},"issue":{"href":"https://api.github.com/repos/druid-io/druid/issues/4433"},"comments":{"href":"https://api.github.com/repos/druid-io/druid/issues/4433/comments"},"review_comments":{"href":"https://api.github.com/repos/druid-io/druid/pulls/4433/comments"},"review_comment":{"href":"https://api.github.com/repos/druid-io/druid/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/druid-io/druid/pulls/4433/commits"},"statuses":{"href":"https://api.github.com/repos/druid-io/druid/statuses/100ca4d5956bff611f8f21fbe9ac5b62831701a9"}}}}
{"actor":{"display_login":"leventov"}}