Build simples de GWT, com auxílio de Maven

22px-flag_of_brazilsvg Quem começa com o Google Web Toolkit (GWT), logo percebe que a distribuição não funciona em todos os sistemas operacionais. É preciso fazer o download do gwt-linux, gwt-mac ou gwt-windows. Isso porque a distribuição inclui bibliotecas nativas SWT, Mozilla, e scripts para executar a aplicação no modo “hosted”, que diferem entre os vários sistemas operacionais. Além disso, a distribuição não inclui scripts para criar um WAR “deployável” com a aplicação GWT.

Uma opção ao uso da distribuição do GWT, é o uso de um archetype maven que faz todo o trabalho de gestão das diferenças entres os vários sitemas operacionais, além de oferecer a possibilidade de criar um WAR com uma linha de comando. Para  criar o projeto, basta chamar o archetype:


mvn archetype:create -DarchetypeGroupId=com.totsp.gwt \
-DarchetypeArtifactId=maven-googlewebtoolkit2-archetype \
-DarchetypeVersion=1.0.3 \
-DremoteRepositories=http://gwt-maven.googlecode.com/svn/trunk/mavenrepo \
-DgroupId=br.com.dominio \
-DartifactId=app-gwt

Para executar o projeto, use o goal gwt:

mvn gwt:gwt

Antes de gerar o WAR, é preciso descomentar a linha “<!– <webXml>target/web.xml</webXml>–>” no plugin war e a linha ” <!–mergewebxml–>” do plugin maven-googlewebtoolkit2-plugin, tudo isso no pom.xml gerado. Isso porque o GWT no modo “hosted” utiliza um xml (chamado Application.gwt.xml) para declara configurações client-side E server-side. Caso você esteja utilizando RPC, o plugin maven fará um merge do Application.gwt.xml para um web.xml, migrando todas as declarações de servlet.

E finalmente para gerar o WAR, basta:

mvn package

Advertisements

Java on the Desktop: still a no go

22px-flag_of_englandsvg After the launch of Java 6 update 10 final, and recently JavaFX 1.0, I decided to take a look on how desktop java was going.

I hoped that after the disastrous failures of applets and java web start, Sun would finally get it right, coming up with something that could be a real option to Flash and Ajax, something that could allow us, developers, to write and distribute reliable and fast desktop applications using Java on the Internet.

But it seems that I’ll have to wait some more time 🙂

I went to javafx.com to see the new demos, and received a very cryptic error whenever I clicked in one of them:

fx

To solve this, it’s necessary to go to java console and do some changes in the temporary storage of files (!),  as described here.

I don’t remember having any problems  installing Flash on any operational system, and that includes Linux, Windows, PalmOS, Pocket PC and Symbian, using several browsers such as Internet Explorer, Opera, Firefox, Chrome and Safari.  Flash just works! You go to a page and Flash runs.  If you  don’t  have Flash, it get installed in a matter of seconds.  It works flawlessly and equally everywhere.

That’s  why things like YouTube uses Flash.  Because if a user has to figure out how to solve errors like that, he’ll simply go away.

Extending Confluence using Wicket

Recently I started to work at two projects in my new job, in one of them doing some plugins to Atlassian Confluence (which is a yet-another-java-based-wiki) and participating on another that uses wicket.

In order to add new (visual) functionality to confluence, it’s common to use XWork to create new actions or replacing the existent actions to add new behavior. I particularly don’t like Xwork and in one of the moments to avoid boredom in these two projects I decided to somehow try to do wicket development inside confluence. The advantage would be avoiding completely XWork, possibility to use wicket advanced features and possibility to run the feature outside confluence (more testable).

So I started giving a look at the Confluence PDK (Plug in development kit), and see that it was already possible to define a servlet as part of a plugin

I then started declaring the wicket servet in atlassian-plugin.xml:

And creating the WicketApplication that simply loads my page:

That will use the Confluence API in order to do a paginated ajax table with the names of the plugins available on confluence.

After that, confluence will load the servlet and will make it available on the /plugins/servlet/sampleplugin/  URL.

ISSUES

1. The first problem is that the output will be strictly the page output, without confluence headers and menus. There’s an issue describing that bug, but while it is not fixed commenting the line 16 in the WEB-INF/decorators.xml in your confluence installation will suffice:

<url-pattern>/plugins/*</url-pattern>

That line when uncommented tells that for the URLs starting with “/plugins”, no decorator will be used. So, It’s enough to comment it.

2. The second problem is that confluence does not destroys the servlet when the plugin is uninstalled using maven PDK tool. There is another issue describing that problem. Basically, wicket creates its session object using one classloader. When the plugin is removed and reinstalled, it’ll gain another classloader. So nasty ClassCastExcetions occurs on trying to obtain the wicket session. The workaround for now is to restart the server on reinstall of the plugin.

Here we can see the final result: The standard confluence header and footer, and the wicket page in the center.

Download here the zip file containing the maven project for this example plugin.

Maven Jetty Plugin and double slashes in the url

English The maven jetty plugin is a handy plug-in that allows to run a maven based project that has a web-app inside simply by doing a “mvn jetty:run”, without the hassle of creating a WAR and deploy it to somewhere else.

It happens that the jetty web server, by default, does not handle URLs that contains double slashes “//”, that is, calling http://host/mycontext/media//img/a.gif is not the same as calling http://host/mycontext/media/img/a.gif, and brings a 404 error (as described in http://jira.codehaus.org/browse/JETTY-386)

To enable url compactation in the jetty through maven plug-in, and thus enabling the same behaviour as tomcat:

<plugins>
           ...
           <plugin>
                      <groupId>org.mortbay.jetty</groupId>
                      <artifactId>maven-jetty-plugin</artifactId>
                      <configuration>
                                <scanIntervalSeconds>10</scanIntervalSeconds>
                                <connectors>
                                       <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                                               <port>8080</port>
                                               <maxIdleTime>60000</maxIdleTime>
                                       </connector>
                                </connectors>
                                <webAppConfig>
                                       <contextPath>/mycontext</contextPath>
                                       <compactPath>true</compactPath>
                                </webAppConfig>
                    </configuration>
           </plugin>
           ...
</plugins>

Casifying Luntbuild

England I’ve been working a lot lately in devware which is a vmware virtual machine containing a lot of development tools already installed and integrated. The challenge now is to support Single Sign On, so that only one login is necessary to access all the tools and softwares inside (at least the ones that has a web interface).

It happens that devware is composed of heterogeneous 3rd party tools, and obviously each one uses a different kind of authentication and security schema.

That’s where comes to scene CAS (Central Authentication Service) a very neat solution for single sign on for the web.

Luntbuild, which is a great and full featured build automation server is the first software that was “Casified”. Luntbuild, fortunately, was created using Spring and Acegi, which in turn supports CAS integration. So, this is how I Casified luntbuild:

1. Install and make sure that CAS is working

Install CAS is as hard as drop a WAR inside your favourite web container. The not-so-obvious part are the SSL issues: you and everyone must access cas using a name, and not and IP, and SSL is obligatory. And you must have a certificate (can be self signed) that reflects this name. After that, make sure that you can login in CAS accessing https://<server>/cas/login.

2. Make sure luntbuild is installed and running

The version of luntbuild that I casified was 1.5.5. This process will not work on previous versions, such as 1.3.x ones. Make sure that luntbuild is installed and that it’s possible to do a login using it’s own security scheme.

3. Changing luntbuild’s web.xml

No change is needed! 🙂 luntbuild’s web.xml declares a filterToBean proxy that allows to make all filtering stuff inside spring’s application context.

4. Changing applicationContext.xml

This is where luntbuild’s spring appcontext lies.

Locate the bean “authenticationManager” and add to the list (as the first one) the CAS authentication provider:

<bean id="authenticationManager" class="com.luntsys.luntbuild.security.AuthenticationProviderManager">
                <property name="providers">
                        <list>
                                <ref local="casAuthenticationProvider" />

                                <!-- authentication provider which uses declarative security -->
                                <ref local="inMemoryAuthenticationProvider" />

                                <!-- authentication provider for remember me -->
                                <ref local="rememberMeAuthenticationProvider" />

                                <!-- authentication provider which validates users again internal db -->
                                <ref local="luntbuildAuthenticationProvider" />

                                <!-- authentication provider which validates users again Ldap -->
                                <ref local="ldapAuthenticationProvider" />
                        </list>
                </property>
        </bean>

Search and coment the bean id “exceptionTranslationFilter”:

<!--<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
             <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property>
</bean> -->

Locate the bean “filterChainProxy” and change the URL matching rules to:

<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
     <property name="filterInvocationDefinitionSource">
        <value>
            PATTERN_TYPE_APACHE_ANT
            /*.do=httpSessionContextIntegrationFilter,casProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
            /j_acegi_cas_security_check=httpSessionContextIntegrationFilter,casProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
            /casProxy/receptor=httpSessionContextIntegrationFilter,casProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
       </value>
    </property>
</bean>

And then add the beans of the CASAuthenticationProvider:

<bean id="casAuthenticationProvider" class="org.acegisecurity.providers.cas.CasAuthenticationProvider">
               <property name="casAuthoritiesPopulator">
                        <ref bean="casAuthoritiesPopulator" />
                </property>
                <property name="casProxyDecider">
                        <ref bean="casProxyDecider" />
                </property>
                <property name="ticketValidator">
                        <ref bean="casProxyTicketValidator" />
                </property>
                <property name="statelessTicketCache">
                        <ref bean="statelessTicketCache" />
                </property>
                <property name="key">
                        <value>my_password_for_this_auth_provider_only</value>
                </property>
</bean>

<bean id="casProxyDecider" class="org.acegisecurity.providers.cas.proxy.RejectProxyTickets" />

<bean id="statelessTicketCache" class="org.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache">
                <property name="cache">
                        <ref local="ticketCacheBackend" />
                </property>
</bean>

<bean id="ticketCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
                <property name="cacheManager">
                        <ref local="cacheManager" />
                </property>
                <property name="cacheName">
                        <value>ticketCache</value>
                </property>
</bean>
   <bean id="casProxyTicketValidator" class="org.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator">
                <property name="casValidate">
                        <value>https://devware/cas/proxyValidate</value>
                </property>
                <property name="proxyCallbackUrl">
                        <value>https://devware/luntbuild/casProxy/receptor</value>
                </property>
                <property name="serviceProperties">
                        <ref bean="serviceProperties" />
                </property>
</bean>

<bean id="casAuthoritiesPopulator" class="com.luntsys.luntbuild.security.LuntbuildCasAuthoritiesPopulator">
                <property name="authenticationDao">
                        <ref bean="luntbuildAuthenticationDAO" />
                </property>
</bean>

<bean id="serviceProperties" class="org.acegisecurity.ui.cas.ServiceProperties">
                <property name="service">
                        <value>https://devware/luntbuild/j_acegi_cas_security_check</value>
                </property>
                <property name="sendRenew">
                        <value>false</value>
                </property>
</bean>

<bean id="casProcessingFilter" class="org.acegisecurity.ui.cas.CasProcessingFilter">
                <property name="authenticationManager">
                        <ref bean="authenticationManager" />
                </property>
                <property name="authenticationFailureUrl">
                        <value>/casfailed.jsp</value>
                </property>
                <property name="defaultTargetUrl">
                        <value>/</value>
                </property>
                <property name="filterProcessesUrl">
                        <value>/j_acegi_cas_security_check</value>
                </property>
</bean>

<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
                <property name="authenticationEntryPoint">
                        <ref local="casProcessingFilterEntryPoint" />
                </property>
</bean>

<bean id="casProcessingFilterEntryPoint" class="org.acegisecurity.ui.cas.CasProcessingFilterEntryPoint">
                <property name="loginUrl">
                        <value>https://devware/cas/login</value>
                </property>
                <property name="serviceProperties">
                        <ref bean="serviceProperties" />
                </property>
</bean>

Replace “https://devware&#8221; with your cas host.

The final piece of the puzzle is the casAuthoritiesPopulator declared above. This is necessary to populate the luntbuild user’s credentials accordingly after the user has logged on, and to create the user in the luntbuild database it does not exist already, similar to what happens when integration luntbuild with LDAP. I’ve submitted a patch to luntbuild’s tracker with this class

5. Final remarks

After this procedure, if one logins into cas, and after that enters luntbuild, a second login will not be necessary. If someone tries to enter luntbuild directly, it’ll redirect to CAS login page. And the same happens with every other applications that is “Casified”. Without changing the source code of luntbuild at all (just changing XML and adding one more class), it was possible to change a major application behavior, and that’s the beauty of spring based projects. They are so decoupled that nearly every aspect of an application instantly becomes an extension point!

And the use of CAS allows to have SSO and not depend on things like proprietary realms, intrusive server configurations, and obscure plugins that are target to a certain version of a certain application server maker. CAS is very portable, the only requisite is an application container that support servlets.


See how maven works inside – remote debugging plugins

England Who uses maven as I do for a long time, knows that sometimes things does not go as expected. So it’s necessary to look at the ultimate documentation: the source code! In this post I’ll show how to debug a maven plugin using Eclipse’s remote debugger. I’ve chosen for this example the maven-clean-plugin.

Ok, let’s start. First create a simple sample project:

    mvn archetype:create -DgroupId=com.wordpress -DartifactId=maven-debug

In the folder maven-debug, edit the pom.xml and add the dependency of the plugin. This will serve as an “anchor” to bring all the sources needed to debug.

<dependency> 
       <groupId>org.apache.maven.plugins</groupId> 
       <artifactId>maven-clean-plugin</artifactId> 
       <version>2.1.1</version> 
       <scope>provided</scope> 
</dependency>

Note the scope “provided”, meaning that the dependency will be given to the project by someone and it should not make to the final package. It shouldn’t matter, anyway, since this is a dependency that will be removed later, it’s just for debug purposes.

It’s important to know what version of the plugin your project is using. When in doubt, run “mvn -X clean” and note lines like:

  [DEBUG] Retrieving parent-POM: org.apache.maven.plugins:maven-plugins::3 for project: 
           null:maven-clean-plugin:maven-plugin:2.1.1 from the repository.

Now generate the project in eclipse:

   mvn -Declipse.downloadSources=true eclipse:eclipse

Open eclipse, go to File -> Import -> Existing Projects into Workspace and select the root directory of the project. After that go to menu
“Run –> Open debug dialog” and under “Remote java application” create a new profile to debug our project:
debug.jpg

Using the eclipse search facilities, search for a class which name ends with “Mojo” (Maven Old Java Object) and place a breakpoint in the “execute” method. In the case of the clean plugin, this class is called CleanMojo

To debug, first enable debug on maven in the command line (linux shown here)

MAVEN_OPTS="-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"; 
export MAVEN_OPTS

and then do a “mvn clean”; the process will halt. Start the debugger in eclipse, it will hit the breakpoint.

breakpoint.jpg

Congratulations! Now you can know exactly what’s going on!