It was suggested that I post this to this group instead of the general
Google Web Toolkit group, so apologies to those who may have read it
there.
I've looked through various posts around this subject, which have been
helpful, however I'm not able to use any work-arounds suggested.
I have Swing GUI application that I want to also render through dynamic
web pages. I am keen to do this with as much of the same code base as
possible.
I have factored the application to follow the MVC pattern and have
emerged with a set of fairly thin view classes. My idea is to have GWT
versions of these view classes sit alongside their Swing equivalents
and that this should be the only difference between the deployments of
the app to desktop and web users.
I have written some test classes in GWT as well as some framework
classes. I have got over the issue of not being able to "push" changes
out to a UI in the way that you do with setNNN() type calls in Swing by
having a GWT class use an AJAX thread to pull back a list of "changes"
(i.e. details of setNNN() calls) and realise them on the local
(Javascript/HTML) interface.
In order to do this, I use a pseudo-reflection-type mechanism in a GWT
class...
public static native void invokeNativeMethod(Object current, String
methodName, Object arg0) /*-{
this["_$"+methodName](current, arg0);
}-*/;
... I know this requires me to tweak the GWT compiler with -style
PRETTY so the method names remain - however this is not my biggest
problem at present.
Because the GWT Compiler removes any un-referenced methods; I'm forced
to introduce an "init" stage to my view class so that when the
invocation of setNNN() occurs, there's a method there to invoke.
This is all very well in my simple test view class; however in even a
modest dialog in the app, this is going to be a substantial "init"
method which doesn't actually buy me anything from a code perspective,
and is going to be difficult to keep in synch with future changes.
Ideally; I'm looking for some way in which to mark the setNNN() methods
in my view class as "do not remove" that doesn't require me to
reference the methods in an init method (or other static-referencing
mechanism). All these methods exist in an interface that I have defined
for my view which both Swing_VIEW_IMPL and GWT_VIEW_IMPL implement, so
I guess what I'm asking for might be something along the lines of an
extra directive in the .gwt.xml file... so that I could write something
like:
<entry-point class='GWTViewOneImpl' honour-interfaces='ViewOne,
ViewOneA' />
..and that the compiler would mark any methods in GWTViewOneImpl that
are implementations of ViewOne or ViewOneA as 'not to be removed'
Of course the other thing I really ought to deal with is a nicer way in
which to still be able to invoke these methods by their
non-pretty-name, but I think I need to solve this problem first.
If anyone has any alternative suggestions as to how I might accomplish
what I'm trying to do, I'd be very grateful, or if this
"honour-interfaces" sounds reasonable please let me know - I'm happy to
help with any dev work for this if it's a popular and workable
solution.
I hope not to have given too much detail, but if any more detail is
required, please reply on this thread.
Thanks in advance,
Zuhayr
Your post was clear, but I missed one thing: why doesn't the compiler think you're calling those setNNN() methods to begin with? Normally, you'd have code that references them in one way or another that the compiler should recognize as being callable.
In terms of a programmatic dispatcher, I'm not sure where this would
best be done. I suspect to do it within Java code destined for
compilation to Javascript might be a problem as I don't think there's
support for the Method class etc..
Putting a form of dispatcher into the compilation stage I guess is what
I was suggesting when I talked about "honour-interfaces".
I've worked on this as little in the GWT compiler code and although
I've had to modify a few ideas that I had originally I've managed to
take this forward to some extent.
I've defined my entry-point with:
<entry-point class='viewone.GWTViewOneImpl'>
<honoured-interface name='sampleclient.client.ViewOne'/>
</entry-point>
... and added the appropriate code in ModuleDef and ModuleDefSchema for
honoured-interface.
In the compile() method in JavaToJavaScriptCompiler I've added a call
to a new method that I wrote that looks for each method defined in the
referenced-interface and adds them to a store in the JProgram.
Then I've added code into the Pruner that rescues the specified methods
etc. At the moment I've also built a static method to delegate to this
rescued method (so I can "derive" the method name easily) - but I
suspect a mechanism closer to your dispatcher's philosophy would be
better so I can get back to a situation where I can turn on
obfuscation.
The code fell out fairly nicely and it fits what I consider a nice
philosophy which is not to repeat the "specification" of the methods
(beyond the interface) in order to specify these methods as 'available'
so if it's deemed useful by others, I'd be happy to contribute this
work - with some guidance over the naming side of things.
To be clear as to what I'm suggesting I'll summarize:
In order to provide a mechanism for declaring that some methods defined
in a Java class that is destined to be compiled into JavaScript, I
propose the capability to specify any number of "honoured-interface"
elements within a defined "entry-point" such that all defined methods
(and their associated code-paths) are prevented from being removed from
the generated program.
Use of the mechanism will allow for run time programatic method
invocation similar to:
myMethod.invoke(obj, new Object[] { arg1, arg2 });
... even when myMethod() has no other direct reference to it in the
class.
Hope this is clear; let me know what you think - I'm happy to discuss
changes/alternatives to this if you think there are ways better aligned
to GWT's goals.
Zuhayr
To give some context on this, our general design philosophy is to not
put special magic into the compiler, if for no other reason than it
just doesn't scale. So we see if there are alternatives to specific
compiler changes that would do what we want.
In your case, here are what I see as the most relevant points:
1) You want to preserve certain methods from being pruned.
2) You want a way ot call them programmatically via string lookup.
3) You know the set of interfaces comprising the methods you care
about at compile time.
To me, this looks like a job for deferred binding with a generator.
Here's the basic plan that I think would do what you want:
abstract class Dispatcher {
abstract void invoke(Object thisObj, String methodName, Object[] args);
}
Then you add some deferred binding rules to use a generator on this
type to your module:
<generate-with class="com.example.dispatch.rebind.DispatchGenerator">
<when-type-assignable class="com.example.dispatch.client.Dispatcher"/>
</generate-with>
To use the service, the user could create an abstract subclass
specified to implement all of the interfaces they care about:
abstract class MyFooBarDispatcher extends Dispatcher implements Foo, Bar {
}
In this case, Foo and Bar are markers for the interfaces you want to
delegate to.
You would create the object as such:
Dispatcher myFooBarDispatcher =
(Dispatcher)GWT.create(MyFooBarDispatcher.class);
Now the DispatcherGenerator class would simply be responsible for
inspecting the user-specific MyFooBarDispatcher class and producing a
Dispatcher subclass that implements the invoke() method and can
delegate to any of the specified interfaces.
Scott
I've begun looking at how I should implemenet the Generator, I'm
investigating whether I'll need to create sub-classes of Dispatcher for
each of the classes I want to use the dispatcher in as you suggest, or
whether I could get away with a single Dispatcher class and put more
intelligence in the Generator.
Given that my GWTViewOneImpl class already implements ViewOne; I'd like
to not have to duplicate this information in ViewOneDispatcher if I can
help it.
If I have a single Dispatcher class, as a Generator I would need to
understand that I am generating code on behalf of the class (e.g.
GWTViewOneImpl) in which I've got:
Dispatcher myFooBarDispatcher =
(Dispatcher)GWT.create(MyFooBarDispatcher.class);
.. looking through the context available to the Generator.generate() I
can't see an obvious reference to the Cud (or some other reference)
that would allow me to find the class I'm generating code on behalf of
... am I missing something or is subclassing Dispatcher the only way
for me to reference the interfaces.
Zuhayr
One thing I did find however, was that although the ModuleDefSchema
etc. seems to allow for comma-separated values; by the time the
property is set in the PropertyOracle, it only had my first value.
I think I can live with the restriction that you can only honour one
interface because I can have it extend any component interfaces, or am
I missing something subtle in the definition of a property?
Unless I'm misunderstanding you, I think you might be on the wrong
track. The name of the class you are "generating code on behalf" of
is the last parameter to Generator.generate(). That is, in the above
sample code the String passed into Generator.generate() would be the
fully-qualified class name of MyFooBarDispatcher. To get the JType
for that class, call typeOracle.findType(requestedClass). You can
then use the JType API to find out what interfaces the requested type
implements.
Scott
GWT.create(MyFooBarDispatcher.class);
This will result in the fully-qualified class name being passed to
generate() - this would all work fine - but what I was hoping to do was
to have just one Dispatcher class and hence...
GWT.create(Dispatcher.class);
... then infer from the fact that this GWT.create() line is within
class GWTViewOneImpl.class that the interface(s) that I should
"implement" are those which GWTViewOneImpl implements.
As I mentioned; I've addressed this at present with a define-property
I was only wondering if while my Generator.generate() call was in
progress; could I use some context (GeneratorContext?) to discover the
class that had the GWT.create().
Still looks a bit esoteric I guess, but hopefull it's a bit clearer
There is no way to infer what class made the GWT.create() call, by
design. One reason for this is that deferred binding rules are only
run once per target class per compilation. You might have 20
GWT.create(Foo.class) calls in your app, but the deferred binding
rules and generation will only happen once for Foo.
> What I was hoping to do was to have just one Dispatcher class and hence...
>
> GWT.create(Dispatcher.class);
>
> ... then infer from the fact that this GWT.create() line is within
> class GWTViewOneImpl.class that the interface(s) that I should
> "implement" are those which GWTViewOneImpl implements.
>
> As I mentioned; I've addressed this at present with a define-property
While I'm sure you can make this go, I would strongly advocate against
such a design. There really isn't a good reason to restrict the user
to only using your Dispatcher as a one-shot deal per app.
Scott
Thanks for your help with this - much appreciated.
Zuhayr