Back when GWT was
all new and shiny (almost three years ago), I played with it
and put
together a tiny library of Ant tasks - nothing earth-shattering,
really basically only a few macrodef
s over
Ant's java
task.
Strangely I still get quite a few hits by searches for "GWT and Ant", so it seems as if nobody else has given GWT and Ant integration a try.
I don't use GWT. Not at all. When I create webapps at $JOB GWT isn't the right choice, either its technology is no fit (we mainly are a .NET shop after all) or the layout requirements can't be achieved by any component based framework I've tried (so far it is Velocity at the view layer for me).
Almost exactly two years ago Fernando Meyer sent me a patch to the
Antlib that changed the hard coded directory names for source and
destination into attributes on the gwt:shell
and gwt:compile
tasks. About a year ago I was asked
how one could increase the memory size
for gwt:compile
. Somewhere very deeply buried on my
TODO list I took note of them.
When I woke up this morning Apache's svn was read-only and suddenly I had time (I planned to hack on Gump) but no ASF project to keep me occupied so I finally managed to integrate Fernando's patch and make room for arbitrary extension arguments.
Code and sources are here and while I was at it, there is a darcs repository1 for it (I've been using darcs for years and didn't feel like installing a different SCM backend).
gwt:compile
and gwt:shell
have new
attributes sourcedir
that default to the old hard-coded
value "src" for the source directory and both have a new nested
element jvmargs
that can be used to pass arbitrary
nested elements supported by
Ant's java
task to the underlying task. I.e. one can now write
<gwt:compile outDir="www" gwtHome="${gwt.home}" classBase="de.samaflost.gwt.Foo"> <gwt:jvmargs> <jvmarg value="-Xmx1024m"/> </gwt:jvmargs> </gwt:compile>
in order to bump the max memory to one
gig. gwt:shell
now also has an optional bindir
attribute defaulting to the old hard-coded "bin".
The Antlib has seen some minimal manual testing against GWT 1.5.
It should be obvious that I don't really maintain these tasks, and to be honest I don't expect I'll ever do. If anybody wants to fork the code, reuse it, whatever, feel free to do so. If you are planning to maintain a more useful Ant integration than my hackish version did, please let me know so I can send people looking for "GWT and Ant" your way.
[1] The directory is not browseable, use
darcs get http://stefan.samaflost.de/repos/gwttasks/
in order to grab the contents.
path: /en/Java/GWT | #
As mentioned
I stumbled over a few things while writing my
first NamespaceHandler
for Spring.
My very first experiments looked as if Spring simply was ignoring my namespace and I ended up having the XML parser complain about not finding my schema definition. It seemed to be some kind of infrastructural problem and not a code problem, so I tried to get the simple dateformat example from the docs working.
Unfortunately the example doesn't work without changes since the
four different places you have to specify the XML Namespace URI or
XSD location use two different root URIs
(http://www.mycompany.com/schema/myns
and
http://www.springframework.org/schema/myns
).
Bug reported.
After some futzing around I realized that my XSD file wasn't ending
up on Spring's CLASSPATH and thus Spring didn't find it. I would
have expected Spring to complain if I specify a location mapping
in META-INF/spring.schemas
that it cannot resolve.
Unfortunately it fails silently.
Writing the NamespaceHandler
is really simple, as is
writing a BeanDefinitionParser
, at least if you don't
need to do fancy things.
Not being familiar with Spring's internals I kept looking around
the BeanDefinitionBuilder
class for a way to provide an
id
for the bean under construction. Maybe I should
have looked closer or the documentation isn't that obvious. If you
extend AbstractBeanDefinitionParser
(maybe indirectly)
you set the bean's id by overriding resolveId
.
Up to here I was
extending AbstractSingleBeanDefinitionParser
because it
did all the things I needed.
Next step: I want to define a bean that has a parent bean.
Searching through BeanDefinitionBuilder
's API docs lead
to the childBeanDefinition
factory method, which
clearly is the way to go.
Unfortunately AbstractSingleBeanDefinitionParser
hasn't
been designed for my use case so I ended up writing my own
AbstractBeanDefinitionParser
subclass that is almost
identical to AbstractSingleBeanDefinitionParser
but
adds another protected getParentName(Element)
method
that will lead to the creation of a child bean if it returns
non-null (and neither getBeanClass
nor getBeanClassName
return non-null).
If anybody is interested in the code, you can find it here - Apache License 2.0.
path: /en/Java/Spring | #
In Spring you can have ApplicationContext
s form a
hierarchy, one contexts is said to be the parent of another one.
There isn't much documentation about what this means in practice.
Basically it means "if you ask the child context for a bean and it
doesn't have a definition for it, it will consult its parent
context". But how does this extend to abstract beans or
dependencies?
What I want to achieve is that I define a concrete implementation of a bean in the parent context and allow the child context to override some of its settings. My preference would be an option where the child context cannot change the class backing the bean and where I can restrict the properties the child context is allowed to override to a certain set, this is what I tried:
<beans ...> <!-- parent --> <bean id="abstractHello" abstract="true"> <property name="message" value="inside parent context"/> </bean> <bean id="concreteHello" parent="abstractHello" class="springtests.Hello" scope="prototype"/> </beans> <beans ...> <!-- child --> <bean id="abstractHello" abstract="true"> <property name="message" value="inside child context"/> </bean> </beans>
with a simple bean that has a setter for a String
"message" property. Unfortunately this doesn't work as I hoped,
"concreteHello" retrieved from the child context has a message of
"inside parent context".
So what about dependencies instead of inheritance? I introduce a simple "holder" object and inject it into my bean:
<beans ...> <!-- parent --> <bean id="holder" class="springtests.MessageHolder"> <property name="message" value="inside parent context"/> </bean> <bean id="helloFromHolder" class="springtests.Hello" scope="prototype"> <property name="messageHolder" ref="holder"/> </bean> </beans> <beans ...> <!-- child --> <bean id="holder" class="springtests.MessageHolder"> <property name="message" value="inside child context"/> </bean> </beans>
Which leads to the same result. When "helloFromHolder" gets configured, it won't see the "holder" reference of the child context.
To be honest, the behavior I've seen is what I had expected, but I thought I could try anyway.
I settled with allowing the child context to completely override
the parent bean. But I wrote my own NamespaceHandler
code (and bumped into a bunch of gotchas for another post) which
prevents setting of properties that shouldn't be overridden and
doesn't allow setting the class. Something like
<beans ...> <!-- parent --> <bean id="abstractHello" class="springtests.Hello" abstract="true"> <property name="message" value="inside parent context"/> </bean> <bean id="hello" parent="abstractHello"/> </beans> <beans ...> <!-- child --> <override:hello> <override:message>inside child context</override:message> </override:hello> </beans>
Here the "abstractHello"
bean provides the class and
default parameters and the NamespaceHandler
doesn't
allow setting id (it will always be "hello"
), parent
(always abstractHello
), class or any of the other
attributes that might be available. Unfortunately I can't prevent
the child context from using "plain" Spring bean definitions to
completely override "hello"
.
path: /en/Java/Spring | #
First I must admit that I don't buy GWT's approach. There is a reason why we don't write HTML code from servlets anymore. Programmers usually are bad UI designers (look at my website for something to back my claim ;-) and great UI designers usually don't want to write Java code. With the GWT model you write your UI in Java and automatically translate it to HTML. This means layout changes require the Java coder to perform it, which is bad. Maybe you can get around most problems with the help of CSS, but I doubt that.
Anyway, I downloaded the toolkit and played with it last night, and found it to be a bit cumbersome to work with. Scripts that generate scripts and no integration with Ant to speak of.
I stopped after my first few steps and started to put together a little Ant library that makes using GWT a bit less cumbersome - at least to me. This is very rough alpha level code, but it works for me. I'm not sure I'll take this any further, but if anybody is interested in it, feel free to use the code - the license is pretty permissive. Source and binaries are available here.
There is no documentation right now. A quick rundown:
- This is an Ant library which means you need Ant 1.6.0 or better. It lives in the antlib:de.samaflost.gwttasks namespace by default.
- Throughout the rest of this blog entry I'll assume you have something like xmlns:gwt="antlib:de.samaflost.gwttasks" on your <project> tag.
- All tasks have a mandatory
gwtHome
attribute that points to your GWT installation. - gwt:projectCreator is almost as pointless as the corresponding script. The major difference is that it doesn't create any Eclipse stuff and that the generated Ant build file is a tiny bit cleaner. Don't use this task. 8-)
- gwt:applicationCreator is similar to the corresponding
script but creates an Ant build file which includes the compile,
clean and package targets projectCreator would have created but also
contains a bunch of additional targets leveraging the other
tasks.
This task has an optional
dir
attribute pointing to the directory that you want to hold your GWT project structure and a mandatoryclassName
attribute (corresponding to the className argument the applicationCreator script of GWT requires).There also is an optional boolean attribute that controls the
-eclipse
command line argument of the applicationCreator script that is called under the covers.The
outDir
is only used while writing the build file, it provides the value for the next two tasks'outDir
attributes and defaults to "www"./p>Finally there is a
template
attribute that can be used to point to a build file template if you don't like the one created by this task. The token@PROJECT@
will be replaced by the last part of the className attribute,@GWT_HOME@
will point to gwtHome,@CLASS_BASE@
is className without ".client" and@OUT@
is the outDir attribute. - gwt:compile runs the Java -> JavaScript compiler.
The
outDir
andclassBase
attributes make up the output directory - in the script generated by the applicationCreator script they are "www" and className without ".client" repectively.There is an optional
dir
attribute if your GWT application's src directory is not directly below your basedir. - gwt:shell starts the GWT shell and takes the same
arguments as the compile task - only
classBase
is replaced bystartPage
which is built by appending the unqualified class name to what would be compile'sclassBase
plus a slash and adding an ".html" extension.
Reading this a second time, it is a bit confusing, I guess.
You must provide a class name to Google's applicationCreator
script, let's say you've chosen de.samaflost.client.Dummy
(the .client. piece is recommended by Google), then
gwt:compile
's classBase
would be
de.samaflost.Dummy
and startPage
would be
de.samaflost.Dummy/Dummy.html
.
Putting the stuff together. To seed a new GWT application you'd use
<target name="applicationCreate"> <mkdir dir="project"/> <gwt:applicationCreator dir="project" gwtHome="C:/OSS/gwt-windows-1.0.20/" className="de.samaflost.client.Dummy"/> </target>
This will generate the initial directory structure in the
project
subdirectory and will also generate an Ant build
file that contains (among other things)
<target name="gwt-compile" depends="compile"> <gwt:compile outDir="www" gwtHome="C:\OSS\gwt-windows-1.0.20" classBase="de.samaflost.Dummy"/> </target> <target name="gwt-shell" depends="compile"> <gwt:shell outDir="www" gwtHome="C:\OSS\gwt-windows-1.0.20" startPage="de.samaflost.Dummy/Dummy.html"/> </target>
This means you don't need to worry about classBase or startPage at all. It also means you now have the Java -> JavaScript step as part of your Ant build process and available to your continuous integration runs.
I haven't found the time for an actual RPC example yet, but expect
it will lead me to a special gwt:war
task that knows what
to package where.
Finally, it may be worth noting that three of the four tasks are
implemented in Ant, not Java (i.e. they are
<macrodef>
's hidden in the antlib descriptor.
path: /en/Java/GWT | #
With Java 5.0, the Comparable interface has been made "generic" and quite a few classes have been retro-fitted to make use of this, for example, java.math.BigDecimal now implements Comparable<BigDecimal>. Unfortunately the developers of the Java classlib forgot to preserve the old signature of compareTo with an Object argument. This means that a method has gone without any deprecation at all. Code that compiles fine in JDK 1.4 no longer compiles and no -source switch is going to help.
For example, see Castor's build in Gump:
[javac] .../src/main/org/exolab/castor/persist/ClassMolder.java:1106: compareTo(java.math.BigDecimal) in java.math.BigDecimal cannot be applied to (java.lang.Object) [javac] if ( (o1 instanceof java.math.BigDecimal) && ((java.math.BigDecimal) o1).compareTo(o2) == 0) {
While I understand why BigDecimal can't implement Comparable<BigDecimal> and Comparable<Object> at the same time (erasure is the key), adding a compareTo(Object) method to Number would have fixed the problem for quite a few classes. I think this is a bug, but the Sun team seems to disagree - or at least think it is not significant.
This means that you need to use explicit casts in your code when you invoke compareTo. Casts that have been unnecessary in JDK 1.4.