{"action":"created","issue":{"url":"https://api.github.com/repos/jruby/jruby/issues/3737","repository_url":"https://api.github.com/repos/jruby/jruby","labels_url":"https://api.github.com/repos/jruby/jruby/issues/3737/labels{/name}","comments_url":"https://api.github.com/repos/jruby/jruby/issues/3737/comments","events_url":"https://api.github.com/repos/jruby/jruby/issues/3737/events","html_url":"https://github.com/jruby/jruby/issues/3737","id":141387351,"number":3737,"title":"java_import can override constants used in dependencies, causing hard-to-debug errors","user":{"login":"aviflax","id":141844,"avatar_url":"https://avatars.githubusercontent.com/u/141844?v=3","gravatar_id":"","url":"https://api.github.com/users/aviflax","html_url":"https://github.com/aviflax","followers_url":"https://api.github.com/users/aviflax/followers","following_url":"https://api.github.com/users/aviflax/following{/other_user}","gists_url":"https://api.github.com/users/aviflax/gists{/gist_id}","starred_url":"https://api.github.com/users/aviflax/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aviflax/subscriptions","organizations_url":"https://api.github.com/users/aviflax/orgs","repos_url":"https://api.github.com/users/aviflax/repos","events_url":"https://api.github.com/users/aviflax/events{/privacy}","received_events_url":"https://api.github.com/users/aviflax/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/jruby/jruby/milestones/11","html_url":"https://github.com/jruby/jruby/milestones/Invalid%20or%20Duplicate","labels_url":"https://api.github.com/repos/jruby/jruby/milestones/11/labels","id":350035,"number":11,"title":"Invalid or Duplicate","description":"Bugs that are duplicates, not actually bugs, or user mistakes should go here.","creator":{"login":"headius","id":10135,"avatar_url":"https://avatars.githubusercontent.com/u/10135?v=3","gravatar_id":"","url":"https://api.github.com/users/headius","html_url":"https://github.com/headius","followers_url":"https://api.github.com/users/headius/followers","following_url":"https://api.github.com/users/headius/following{/other_user}","gists_url":"https://api.github.com/users/headius/gists{/gist_id}","starred_url":"https://api.github.com/users/headius/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/headius/subscriptions","organizations_url":"https://api.github.com/users/headius/orgs","repos_url":"https://api.github.com/users/headius/repos","events_url":"https://api.github.com/users/headius/events{/privacy}","received_events_url":"https://api.github.com/users/headius/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":404,"state":"open","created_at":"2013-06-06T19:52:53Z","updated_at":"2016-03-16T21:33:01Z","due_on":null,"closed_at":null},"comments":1,"created_at":"2016-03-16T20:05:41Z","updated_at":"2016-03-16T21:33:04Z","closed_at":null,"body":"I’m new to Ruby and JRuby, so please forgive any errors.\r\n\r\nI’m working on a JRuby project with a co-worker, and we encountered some very strange behavior in our app. It took him awhile to track it down, but eventually he found that we had inadvertently used `java_import` to redefine the Ruby constant `Set` and thereby broken a dependency of a dependency.\r\n\r\nJust to be clear:\r\n\r\n1. Our file `app.rb` called `require 'faye/websocket'` and then `java_import java.util.Set`\r\n2. The code in `app.rb` called some code in `faye/websocket` that in turn called [`Set.new` in `headers.rb`](https://github.com/faye/websocket-driver-ruby/blob/master/lib/websocket/driver/headers.rb#L16) which returned a new instance of `java.util.Set`\r\n    1. Rather than `Set` from the built-in module [`set`](http://ruby-doc.org/stdlib-2.3.0/libdoc/set/rdoc/index.html) that ships with the Ruby standard library\r\n3. Subsequent to that, something caused the method `add?` [to be called](https://github.com/faye/websocket-driver-ruby/blob/master/lib/websocket/driver/headers.rb#L27) on the value returned in step 2.\r\n4. Since `java.util.Set` has no `add?` method, an exception was raised.\r\n\r\nOnce we figured this out, we were quite surprised. We had had no idea that `java_import` would redefine `Set` globally, even in modules and packages that had already been loaded. Now that I’ve had some time to think it over I think I can see why this would be the case in Ruby, wherein most things by default seem to be in the global scope. But my coworker and I are newbies to Ruby, and I suspect we were mentally mapping `java_import` to how `import` works in Java — now clearly a mistake.\r\n\r\nOK, so on to my point: given this behavior, `java_import` seems really dangerous. It redefines any existing constant in the global scope, and given modern software development practices wherein people regularly require many levels of dependencies and can’t be expected to be familiar with all the symbols used by all those dependencies, it seems quite easy for someone to inadvertently break a dependency, like we did. And it took us a non-neglibile amount of time to debug this situation.\r\n\r\nSo:\r\n\r\n* Is there a generally accepted methodology of avoiding such problems in the Ruby community?\r\n* Is there any way to change the behavior of `java_import` so that it works more like `import` in Java, merely acting as a shorthand for a symbol, and only in the current file?\r\n* I suggest modifying [the docs on `java_import`](https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby#using-a-java-class-without-the-full-path-name) by adding a strong warning about this problem and maybe even advising users to avoid `java_import`.\r\n    * I’d be happy to volunteer if the maintainers would support this.\r\n\r\nOther than this, JRuby is working great so far in our project — thank you for such an impressive system!"},"comment":{"url":"https://api.github.com/repos/jruby/jruby/issues/comments/197557829","html_url":"https://github.com/jruby/jruby/issues/3737#issuecomment-197557829","issue_url":"https://api.github.com/repos/jruby/jruby/issues/3737","id":197557829,"user":{"login":"enebo","id":19819,"avatar_url":"https://avatars.githubusercontent.com/u/19819?v=3","gravatar_id":"","url":"https://api.github.com/users/enebo","html_url":"https://github.com/enebo","followers_url":"https://api.github.com/users/enebo/followers","following_url":"https://api.github.com/users/enebo/following{/other_user}","gists_url":"https://api.github.com/users/enebo/gists{/gist_id}","starred_url":"https://api.github.com/users/enebo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enebo/subscriptions","organizations_url":"https://api.github.com/users/enebo/orgs","repos_url":"https://api.github.com/users/enebo/repos","events_url":"https://api.github.com/users/enebo/events{/privacy}","received_events_url":"https://api.github.com/users/enebo/received_events","type":"User","site_admin":false},"created_at":"2016-03-16T21:33:04Z","updated_at":"2016-03-16T21:33:04Z","body":"java_import setting a constant in the current namespace in and of itself is maybe confusing but it is not really all that much different than frameworks like Rails which define constants in namespaces for you as a side-effect.  In fact, java_import is implemented as a Ruby method.  \r\n\r\nI hope these two examples will show why this is not really something we can provide extra warnings for and how it is really just a Ruby pitfall.\r\n\r\nLet's say we require set and we java_import java.util.Set.  Both define Set in ::Object:\r\n\r\n```ruby\r\nrequire 'java'\r\nrequire 'set';\r\n java_import 'java.util.Set'\r\n```\r\nWhen we run this we see:\r\n\r\n```text\r\nuri:classloader:/jruby/java/core_ext/object.rb:93: warning: already initialized constant Set\r\n```\r\nSo Ruby is working here and showing that you are trying to overwrite an existing constant.  However, let's now consider we generally work within classes or modules?  So we do something like:\r\n\r\n```ruby\r\nrequire 'set'\r\n\r\nmodule Foo\r\n   java_import 'java.util.Set'\r\nend\r\n```\r\nIn this case we get no warning.  If you call Set inside Foo it finds Java and outside Foo it finds Ruby's set.  This is really identical to:\r\n\r\n```ruby\r\nrequire 'set'\r\n\r\nmodule Foo\r\n   Set = 1\r\nend\r\n```\r\n\r\nIf you would like some more clarity or perhaps you have some suggestion in how we can make this better then please comment and potentially reopen.\r\n"}}