Java integration: Swing string problem

View: New views
8 Messages — Rating Filter:   Alert me  

Java integration: Swing string problem

by Hugh Winkler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

I'm mainly just recording some information here in a place where it
might be useful. I can't drill deeper at the moment. There's no bug
here, but there may be an opportunity to improve Java integration.

I encountered a case where Swing compares a passed in string, against
a static final field, using ==. IOW, they're using it like an enum. In
Java SE 6 it's in AbstractButton.actionPropertyChanged.

So my JRuby program calls AbstractAction putValue(String key, object
value). The action object notifies listeners, including JMenuItems
which inherit from AbstractButton.

This is how how you change menu text in Swing, so the undo menu reads
"Undo Typing" or whatever.

My call in jruby looked like: my_action.put_value(Action::NAME, "Undo Typing")

The AbstractButton code, however, fails the comparison of this key
against Action.NAME. I guess JRuby is passing a copy of the string
around. That's why it fails ==.

My workaround is:
ActionNameKey =
javax.swing.Action.java_class.declared_field('NAME').static_value
my_action.put_value(ActionNameKey, "Undo Typing")

If I had the time right now I'd dig into whether Jruby could make
cases like this succeed by always calling String.intern before passing
the string around. 1. I don't know that jruby doesn't do this, 2. I
don't know that it would fix the problem. But it would be nice to
reduce the friction if possible.


Hugh

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


Re: Java integration: Swing string problem

by Bill Dortch :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

This fails in your original case because the Java String is converted to a Ruby String when the constant is set.  Ruby string values are stored as ByteLists (wrapping a byte array), and are divorced from the original Java string.  The second case works because JavaField#static_value just wraps the original Java string in a JavaObject (low-level wrapper).

JavaObject is likely to go away (or at least be re-purposed) post-1.1, so we'll need to be mindful of this use case.  If we do the refactoring right, it should be possible to have an alternative String implementation for strings returned from Java that retains the original value (converting to ByteList only if necessary), which will make sense because in many (most?) cases, strings returned from Java are just going to be passed back in without further manipulation.

-Bill

On 1/5/08, Hugh Winkler <hughw@...> wrote:
Hi,

I'm mainly just recording some information here in a place where it
might be useful. I can't drill deeper at the moment. There's no bug
here, but there may be an opportunity to improve Java integration.

I encountered a case where Swing compares a passed in string, against
a static final field, using ==. IOW, they're using it like an enum. In
Java SE 6 it's in AbstractButton.actionPropertyChanged.

So my JRuby program calls AbstractAction putValue(String key, object
value). The action object notifies listeners, including JMenuItems
which inherit from AbstractButton.

This is how how you change menu text in Swing, so the undo menu reads
"Undo Typing" or whatever.

My call in jruby looked like: my_action.put_value(Action::NAME, "Undo Typing")

The AbstractButton code, however, fails the comparison of this key
against Action.NAME. I guess JRuby is passing a copy of the string
around. That's why it fails ==.

My workaround is:
ActionNameKey =
javax.swing.Action.java_class.declared_field('NAME').static_value
my_action.put_value(ActionNameKey, "Undo Typing")

If I had the time right now I'd dig into whether Jruby could make
cases like this succeed by always calling String.intern before passing
the string around. 1. I don't know that jruby doesn't do this, 2. I
don't know that it would fix the problem. But it would be nice to
reduce the friction if possible.


Hugh

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email



Re: Java integration: Swing string problem

by Hugh Winkler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Thanks for these comments.

On Jan 5, 2008 11:25 PM, Bill Dortch <bill.dortch@...> wrote:
> This fails in your original case because the Java String is converted to a
> Ruby String when the constant is set.  Ruby string values are stored as
> ByteLists (wrapping a byte array), and are divorced from the original Java
> string.

So to clarify, I'm thinking that when JRuby passes one of these
ByteLists back to java, it could call String.intern on it before
passing it. The following test passes. I suspect if we call intern()
on the ByteList copy, it would make the Swing calls succeed.

public class Main {

    public static final String ORIGINAL="something";

    public static void main(String[] args) {
        char[] bytes = {'s','o','m','e','t','h','i','n','g'};
        String copy = new String(bytes);
        String interned = copy.intern();
        assert(copy.equals(ORIGINAL));
        assert(copy != ORIGINAL);
        assert(interned == ORIGINAL);
    }
}



The second case works because JavaField#static_value just wraps the

> original Java string in a JavaObject (low-level wrapper).
>
> JavaObject is likely to go away (or at least be re-purposed) post-1.1, so
> we'll need to be mindful of this use case.  If we do the refactoring right,
> it should be possible to have an alternative String implementation for
> strings returned from Java that retains the original value (converting to
> ByteList only if necessary), which will make sense because in many (most?)
> cases, strings returned from Java are just going to be passed back in
> without further manipulation.
>
> -Bill
>
>

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


Re: Java integration: Swing string problem

by Marcin Mielżyński-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hugh Winkler pisze:

> So to clarify, I'm thinking that when JRuby passes one of these
> ByteLists back to java, it could call String.intern on it before
> passing it. The following test passes. I suspect if we call intern()
> on the ByteList copy, it would make the Swing calls succeed.
>
> public class Main {
>
>     public static final String ORIGINAL="something";
>
>     public static void main(String[] args) {
>         char[] bytes = {'s','o','m','e','t','h','i','n','g'};
>         String copy = new String(bytes);
>         String interned = copy.intern();
>         assert(copy.equals(ORIGINAL));
>         assert(copy != ORIGINAL);
>         assert(interned == ORIGINAL);
>     }
> }
>  
Interning arbitrary strings is evil, we should avoid this as much as
possible.


Marcin.

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


Re: Java integration: Swing string problem

by Hugh Winkler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Jan 6, 2008 9:02 AM, Marcin Mielżyński <lopx@...> wrote:

> Interning arbitrary strings is evil, we should avoid this as much as
> possible.
>

What is your reason?

Re: Java integration: Swing string problem

by Marcin Mielżyński-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hugh Winkler pisze:

> On Jan 6, 2008 9:02 AM, Marcin Mielżyński <lopx@...> wrote:
>
>  
>> Interning arbitrary strings is evil, we should avoid this as much as
>> possible.
>>
>>    
>
> What is your reason?
>  
Interned Strings are not GCable, this leads to permgen issues (JRuby has
big permgen footprint already).

Marcin.

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email


Re: Java integration: Swing string problem

by Hugh Winkler :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Jan 6, 2008 9:45 AM, Marcin Mielżyński <lopx@...> wrote:

> Hugh Winkler pisze:
>
> > On Jan 6, 2008 9:02 AM, Marcin Mielżyński <lopx@...> wrote:
> >
> >
> >> Interning arbitrary strings is evil, we should avoid this as much as
> >> possible.
> >>
> >>
> >
> > What is your reason?
> >
> Interned Strings are not GCable, this leads to permgen issues (JRuby has
> big permgen footprint already).
>
>


Oh. Well that is a very good reason.

1. It could be a runtime switch to jruby :
-Djruby.intern.java.string=true. (along with -XX:MaxPermGen=...) Then
the developer takes responsibility.

2. Jruby might apply intern only to "public static final" strings that
originated with a java import, because that is the real use case. It
would have to set a flag that travels around with the RubyString
somehow, to remember to intern the string when needed.

3. I tried to find a way to call intern from JRuby, but I ended up
with Ruby strings. My jruby java integration knowledge isn't great,
and I suspect there might be a way to do that. I'm not sure how much
an improvement that would be over my workaround, though. It's still
going to be a surprise for anyone developing a Swing app, some APIs
they will have to learn to treat specially.

Hugh


> Marcin.
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>     http://xircles.codehaus.org/manage_email
>
>

Re: Java integration: Swing string problem

by Thomas E Enebo :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

As Bill had mentioned above we have a JavaObject wrapper needs to go
away and when it does we can fix this problem.   We won't need to
convert the Java String into a Ruby String and then recreate it as a
Java String on the way out.   It will just be the Java String the
whole time.

-Tom

On Jan 6, 2008 10:08 AM, Hugh Winkler <hughw@...> wrote:

> On Jan 6, 2008 9:45 AM, Marcin Mielżyński <lopx@...> wrote:
> > Hugh Winkler pisze:
> >
> > > On Jan 6, 2008 9:02 AM, Marcin Mielżyński <lopx@...> wrote:
> > >
> > >
> > >> Interning arbitrary strings is evil, we should avoid this as much as
> > >> possible.
> > >>
> > >>
> > >
> > > What is your reason?
> > >
> > Interned Strings are not GCable, this leads to permgen issues (JRuby has
> > big permgen footprint already).
> >
> >
>
>
> Oh. Well that is a very good reason.
>
> 1. It could be a runtime switch to jruby :
> -Djruby.intern.java.string=true. (along with -XX:MaxPermGen=...) Then
> the developer takes responsibility.
>
> 2. Jruby might apply intern only to "public static final" strings that
> originated with a java import, because that is the real use case. It
> would have to set a flag that travels around with the RubyString
> somehow, to remember to intern the string when needed.
>
> 3. I tried to find a way to call intern from JRuby, but I ended up
> with Ruby strings. My jruby java integration knowledge isn't great,
> and I suspect there might be a way to do that. I'm not sure how much
> an improvement that would be over my workaround, though. It's still
> going to be a surprise for anyone developing a Swing app, some APIs
> they will have to learn to treat specially.
>
> Hugh
>
>
>
> > Marcin.
> >
> > ---------------------------------------------------------------------
> > To unsubscribe from this list please visit:
> >
> >     http://xircles.codehaus.org/manage_email
> >
> >
>



--
Blog: http://www.bloglines.com/blog/ThomasEEnebo
Email: enebo@... , tom.enebo@...