Showing posts with label tips. Show all posts
Showing posts with label tips. Show all posts

Thursday, February 16, 2012

Summary of presentation with tips on creating visually appealing Android applications that scale to various screen sizes

Interesting points from a presentation in which Eric Burke shares tips on creating visually appealing Android applications that scale to various screen sizes.
The session focuses on custom views, rounded corners, scalable drawables, gradients, shine, holograms etc.
Eric is the St. Louis Engineering Manager and Android Tech Lead with Square.

06:00 Use alpha compositing with anti-aliasing, not canvas.clipPath() because you get jagged pixelated corners

17:25 triangular shine

29:00 Check the imeOptions for EditTexts/keyboard input. E.g imeOptions="actionNext" for example. Or "actionDone". NOTE: does not work on HTC Sense UI! So "actionNext" seems safest (might work, might not, but no harm done). For "actionDone" you still need a button for it on your screen anyway.
31:00 SafeViewFlipper code because the standard one crashes your app eventually (guaranteed) on pre-HoneyComb!
35:00 Holograms! Using the accelerator it acts as if it's shiny. Cool!
39:00 demo of cards sliding in/out and hologram on emulator

Monday, October 31, 2011

Android UI Development: Tips, Tricks Techniques from Devoxx 2010 Summary

Quick summary of the most important/new tips from the Parleys talk Android UI Development: Tips, Tricks Techniques at Devoxx 2010. Now free for viewing.

13:40: tool for tracking allocations in Android via Debug.setAllocationLimit(int)

25:15: example showing the use of DDMS, Heap Analysis Tool, hprof, adb shell dumpsys meminfo

36:14: don't over-invalidate when redrawing. Only redraw what you have to. Demo shows use of traceview to detect too much being re-drawn.

44:20: (new) hierarchy viewer example. You can use Annotations to export your own values, i.e. the ones you want to check in the viewer.

48:09: you can use the ViewStub to not create your Views too early. Kindof lazy-instantiation. Uses android:inflatedId.

51:15: better way of using an ImageView (e.g icon) with a TextView inside a LinearLayout: android:drawableLeft(Top/Bottom/Right). Much more efficient because not using 3 Views.

51:45: shows use of the static analysis tool 'layoutopt'. It needs an layout XML file and it tells you what can be improved.

53:45: the one you should know by now :) Don't inflate() views in ListView.getView(), re-use. Shows a graph of performance differences if you do vs if you don't.

54:24: tip on View.setDrawingCacheEnabled(true), and explains quicky how Android uses it when you flick a ListView.

Thursday, August 18, 2011

Android project lessons learned

Quick post with some bullet points of lessons learned during my last Android project:

  • To be able to keep strings in strings.xml apart, prefix them with the name of the Activity they belong to. So listOrdersOrderNr instead of just orderNr.

  • If you inflate() a view without the parent, it won't take into account the parent's attributes like padding! Sadly apparently that trick does not seem to work in the getView() method. For that you'd have to compute the padding in dp and then set the padding of the view. E.g:

    
    
    float scale = getContext().getResources().getDisplayMetrics().density;
    float dpSize = 10 * scale; // So 10 would be the number of dp you want
    buttonRow.setPadding((int) dpSize, (int) dpSize, (int) dpSize, (int) dpSize);



  • When using a RelativeLayout, make sure you refer to other elements using "@id/someId", and not "@+id/someId". If you use the latter, Android will just create a new id named "someId" and will place the layout starting from the top of your Activity. Pretty obvious this one (the docs say the "+" creates a new id) but still an easy one to miss... So don't do:

    
    
    android:layout_below="@+id/feedbackEmailLogsSeparator"


    But use:

    
    
    android:layout_below="@id/feedbackEmailLogsSeparator"



Monday, July 4, 2011

Android pro tips for advanced expert Android developers

Short summary of the Google I/O 2011 session "Android Protips: Advanced Topics for Expert Android App Developers". Full presentation can be found here.

6:35: the parallel Activity pattern: based on SDK version, start another "parallel" code line. Easy check to see what your SDK version is so you can distinguish use of more advanced APIs as for example available since HoneyComb.
8:20: use interfaces for backward compatibility
9:09: get real user feedback before launching. Note you can also of course use Analytics to log an exception!
10:38: example of doing A/B testing
11:35: use Android Market for beta testing. It seems there's no special facility for this in the market, you just have to control it yourself... e.g password protect, obscure the name.
12:31: But don't forget to protect your package name before someone else takes it! So upload and save it but don't publish it.
13:43: don't assume the default (natural) orientation on a device is portrait, e.g most tablets start in landscape. The orientation sensor's default is also 90degrees "off"
15:00: Don't track devices to detect unique installations. Give your app a unique id using a UUID, store it in the prefs.
20:00: the app's data has to be always fresh if possible, the user does not have to wait. See also this recent post: http://android-developers.blogspot.com/2011/06/deep-dive-into-location.html
25:51: how to get fresher data
32:00: make your services intelligent like do everything async, let it die asap
33:13: make your app psychic: leverage what the phone already knows, remembers what the user has told it. Like the Account Manager. Preferences should follow the user across devices and resets (which means storing them offline). Give users the ability to delete their preferences, using the Backup Manager to backup shared preferences.
38:21: intercept links using an Intent Receiver
39:25: make your apps adaptive. E.g: behave as expected, optimize for different user experiences. You can specify the IME keyboard behavior by setting the appropriate action (Go, Search, Send, Done etc). Use android:imeOptions for that
43:20: handle volume and playback buttons icw requesting audio focus for playback
47:10: provide translations for your app and title description to have it better found in people's native language
48:00: make your app smooth: make everything async. Loader and CursorLoader are available since HoneyComb.

Friday, November 12, 2010

My experience with Android Market Licensing and LVL

This post describes my lessons learned with the Android Market Licensing service and the accompanying License Verification Library (LVL). I used it the first time for a completely new application, never published on the market before.


Getting started
From the description and instructions page you can read:
"Android Market offers a licensing service that lets you enforce licensing policies for paid applications that you publish through Android Market. With Android Market Licensing, your applications can query Android Market at run time to obtain their licensing status for the current user, then allow or disallow further use as appropriate".
"The License Verification Library (LVL) handles all of the licensing-related communication with the Android Market client and the licensing service. With the LVL integrated, your application can determine its licensing status for the current user by simply calling a library checker method and implementing a callback that receives the status".


That page is quite elaborate and definitely a good starting point for integrating the LVL into your project. Note that your app needs to support SDK 1.5 or higher (API level 3 and onwards).

The LVL I did not set up as a Library Project in Eclipse because it messed up my Subversion. It got totally confused, indicating stuff wasn't checked in, but I also could not check anything in even after a cleanup. Or it kept indicating there were waiting changes to check in, even though I had it checked in. Maybe it's Eclipse Ganymede, maybe it's me :). So I just imported the source code in my project. Disadvantage is I'll have to update each project that uses the LVL when an update of the LVL arrives.

Furthermore I used the ServerManagedPolicy and changed the salt for the AESObfuscator (which is used to securely obfuscate data written to and read from storage).
Note that the AESObfuscator has been reported to be quite slow.

Setting up a test response for license checks works fine for brand spanking new apps like mine was. On my emulator I used my developer account, on the real device (a Nexus One) another gmail account as test-account.

Quite essential in the documentation is the line "Signing in using a publisher account offers the advantage of letting your applications receive static test responses even before the applications are uploaded to the publisher site".
It took me (and apparently others) quite a while to figure out that that means that the test accounts only get the Test response when you've uploaded the app to the market and saved it! Otherwise they will get ERROR_NOT_MARKET_MANAGED.


To obfuscate or not to obfuscate
I decided not to obfuscate my application. Main reasons being:

  • It won't prevent hackers from decompiling your code for 100% sure.

  • It gets a real pain in the @ss to figure out the real line of the code when you get an error report or exception.

  • You should do a full regression test after obfuscation. Of course :) you have that all automated but still...

Note also that the post explaining ProGuard obfuscation requires a fix (it obfuscates too much). It might be updated now.
Here's a tip when you're using Proguard obfuscation to easily indicate which classes shouldn't be obfuscated (in short: by letting those implement a specificly named interface and put that as a keep-rule in the proguard config).

Great tip here to even more minimize the number of classes/interfaces you shouldn't obfuscate.

For another nice introduction and more in-depth details on why you could use Proguard for obfuscation, shrinking and optimization, see this post.


Server Response Extras
During testing on the emulator I found out that the Server Response Extras are never set for the test responses, so it's quite hard to test different behaviour here on different values. On the other hand, the default implementation seems to handle it quite ok. If you don't like it, you'll have to dive into the LVL code...


Testing new versions of an app
I also found out that if you increase the versionCode of your app, and try to test that app on a real phone with a test account, you'll get back ERROR_NOT_MARKET_MANAGED. Most likely this is because of the new versionCode, which the market licensing service does not recognize/accept. I have not seen the ability to just upload a new version of an app and only save it, it seems you can only publish it... And this thus means your test accounts can't test the app before release...

Looking at a reply in this post it seems you don't even want to save a newer version because the current version of your app disappears from the market! Another report of this here.

As mentioned in the first post, the only way to test a newer version seems to set the versionCode to the currently published version and use that during testing on a real device with test accounts. Sounds doable, there's not too much risk of forgetting to increase the versionCode when publishing: the market won't let you upload an app with the same versionCode as already published.

Note: Maybe the new "draft upload" feature fixes the above issues (see sixth item in this post), didn't try that out yet.

Some answers from Trevor Johns in the post clarify things re: responses a bit but not all:

"If an app is not published AND not draft, then you'll get
ERROR_NOT_MARKET_MANAGED."

Yes, but if your testing with your developer account on the emulator, you get back whatever is set in the test response. Even for an app with a newer versionCode.

"If an app is in draft (never published), then we send LICENSED for all requests for that app."
Is it? It seems it sends back to test accounts on a real device what's set in the Test response. Not tested yet for increased versionCode.
The reply on 6aug10 from Trevor Johns in this thread says it too. Unless I did something wrong, I noticed different behaviour.

"If an app is published (or has been published then unpublished), then
the response is driven by the dev console settings for the developer/testers, and purely by purchase history for everyone else."

Yes, but if you have increased the versionCode, test accounts on a real phone get back ERROR_NOT_MARKET_MANAGED.

See also other responses in this post.

I also wanted to test what happens when I publish a new version, would people with an older purchased version of the app still get verified successfully, and what does the provided LVL code do when it gets back LICENSED_OLD_KEY?
My conclusion: that combination seems only testable on the emulator for the developer account before publishing the app (note I didn't try the scenario on a real device with the developer account). After really publishing the app you can test it of course, but that's a bit late, you want to try it before publishing, before the world can download it.
For an app never published before, you can test the LICENSED_OLD_KEY as described here.


More securing your implementation
Of course I followed also most steps described in Securing Android LVL Applications. I did not add a CRC check. Tips on how to do that can also be found here and here.


Wishes
What would be great to have available in the developer console is to see how many verification requests failed, and with what error. This way you can get an indication on how many people try to illegally use your app, and get an indication whether something might be competely wrong with your app (e.g nobody gets correctly verified anymore).
You could then also see how many people haven't upgraded yet via distinct the OLD_LICENSE_KEY responses.
As long as this kind of statistics is not provided by the market, but you still want to know about these kind of failures, what you could do is report any error results to your own separate server (though of course a skilled hacker could prevent that from being sent).

Finally: don't forget to add any issues you find to the market licensing issues page.

Sunday, October 3, 2010

JBoss Application Server 5.1/ESB 4.5 GA and ActiveMQ 5.4 integration

On one of my last projects I had to integrate JBoss ESB 4.5 GA with ActiveMQ 5.4.0 and Spring 3. NOTE: the fixes below also work for integration with JBoss 5.1 Application Server.



My starting point was of course this post: Integrating Apache ActiveMQ with JBoss. (Or is this the original?)
Part of the requirements mentioned in that article (Apache ActiveMQ 4.0.1+ and JBoss 4.0.4+) got me worried that the steps described might not work for my version of JBoss and ActiveMQ. Even though there's a '+' after the version numbers :)

First I had quite a hard time finding the .rar file! Finally found it in \lib\optional in the ActiveMQ zip: activemq-rar-5.4.0.rar

After that I followed the steps including up to 'Configuring JBoss'. In there you also have to start JBoss again.
I didn't see any exceptions fly around so thought all was fine, so I started the consumer ('ant consumer').
But there I got:



[java] javax.jms.JMSException: Could not connect to broker URL:
tcp://localhost:61616. Reason: java.net.ConnectException:
Connection re[Thread-2] Caught: javax.jms.JMSException: Could not
connect to broker URL: tcp://localhost:61616. Reason:
java.net Connection Exception : connection refused


Strrrange, because I thought the server started fine. After quite some searching, I figured out the error message probably indicates that there's just nothing listening at localhost:61616.
After carefully checking the startup log (searching for 'activemq') I found out it hadn't started!



17:17:31,566 INFO [XBeanXmlBeanDefinitionReader] Loading XML bean
definitions from class path resource [broker-config.xml]
17:17:31,768 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 29 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration
of element 'beans'.


That's weird, some namespace problem?
Searching on the biggg internet I found this page (via), explaining that the namespaces are different from 5.1 onwards.

Since I'm using AMQ 5.4.0 I tried in broker-config.xml:



<beans xmlns="http://activemq.apache.org/schema/core">


But that still gave:



17:23:38,638 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 29 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration
of element 'beans'.


So now I just tried all the namespace definitions:



<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd
">
</beans>


Darn, still not ok:



17:27:42,265 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 49 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-complex-type.2.4.c: The matching wildcard
is strict, but no declaration can be found for element 'persistenceAdapter'.


But that was easy, missing the namespace prefix. So I modified:



<amq:persistenceAdapter>
<amq:journaledJDBC journalLogFiles="5" dataDirectory="activemq-data"/>
<!-- To use a different datasource, use th following syntax : -->
<!--
<journaledJDBC journalLogFiles="5" dataDirectory="../data" dataSource="#postgres-ds"/>
-->
</amq:persistenceAdapter>


But again an error:



17:29:50,840 WARN [ActiveMQResourceAdapter] Could not start up embeded
ActiveMQ Broker 'xbean:broker-config.xml': Line 50 in XML document from
class path resource [broker-config.xml] is invalid; nested exception is
org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was
found starting with element 'amq:journaledJDBC'. One of
'{"http://activemq.apache.org/schema/core":amqPersistenceAdapter,
"http://activemq.apache.org/schema/core":jdbcPersistenceAdapter,
"http://activemq.apache.org/schema/core":journalPersistenceAdapter,
"http://activemq.apache.org/schema/core":kahaDB,
"http://activemq.apache.org/schema/core":kahaPersistenceAdapter,
"http://activemq.apache.org/schema/core":memoryPersistenceAdapter,
WC[##other:"http://activemq.apache.org/schema/core"]}' is expected.


So journaledJDBC is unknown. There must be something with a wrong namespace version or something. From the XSDs from here it seemed there was an element in the wrong place. So I just removed the whole persistenceAdapter element.

I also noted that there is a persistenceFactory element in the AMQ 450 broker-config.xml, which is not present in the Integrating Apache ActiveMQ with JBoss post.
So I also changed that one's dataDirectory to be the same as in the (just removed) persistenceAdapter:



<amq:persistenceFactory>
<amq:journalPersistenceAdapterFactory journalLogFiles="5" dataDirectory="${jboss.server.data.dir}/activemq"/>
</amq:persistenceFactory>


Yes that did it! See the log part below:



17:45:01,215 INFO [XBeanXmlBeanDefinitionReader] Loading XML bean definitions
from classpath resource [broker-config.xml]
17:45:01,542 INFO [DefaultListableBeanFactory] Pre-instantiating singletons
in org.springframework.beans.factory.support.DefaultListableBeanFactory@157402b:
defining beans [org.apache.activemq.xbean.XBeanBrokerService#0]; root of
factory hierarchy
17:45:01,697 INFO [PListStore] PListStore:activemq-data\bruce.broker1\tmp_storage
started

... stuff deleted ...

17:45:02,896 INFO [BrokerService] ActiveMQ 5.4.0 JMS Message Broker
(bruce.broker1) is starting
17:45:02,911 INFO [BrokerService] For help or more information please
see: http://activemq.apache.org/
17:45:03,083 INFO [SchedulerBroker] Scheduler using directory:
activemq-data\scheduler
17:45:03,145 INFO [JournalPersistenceAdapter] Journal Recovery Started from:
Active Journal: using 5 x 20.0 Megs at: C:\jbossesb-server-4.5.GA\bin\${jboss.server.data.dir}\activemq\journal
17:45:03,176 INFO [JournalPersistenceAdapter] Journal Recovered: 0 message(s)
in transactions recovered.
17:45:03,207 INFO [TransportServerThreadSupport] Listening for connections at:
tcp://localhost:61616
17:45:03,223 INFO [TransportConnector] Connector bruce.broker1 Started
17:45:03,223 INFO [BrokerService] ActiveMQ JMS Message Broker (bruce.broker1,
ID:PC555-4930-1389432149-0:0) started


Also after that the consumer and producer worked fine.

And as a final optimization to not having to change the namespace for each new ActiveMQ version, I removed the version from the activemq-core xsd:



<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
">


To summarize, this is the final broker-config.xml (some comments deleted for space):



<?xml version="1.0" encoding="UTF-8"?>
<!-- START SNIPPET: xbean -->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core.xsd
">

<!-- shutdown hook is disabled as RAR classloader may be gone at shutdown -->
<amq:broker useJmx="true" useShutdownHook="false" brokerName="bruce.broker1">

<amq:managementContext>
<!-- use appserver provided context instead of creating one,
for jboss use: -Djboss.platform.mbeanserver -->
<amq:managementContext createConnector="false"/>
</amq:managementContext>

<amq:persistenceFactory>
<amq:journalPersistenceAdapterFactory journalLogFiles="5" dataDirectory="${jboss.server.data.dir}/activemq"/>
</amq:persistenceFactory>

<amq:transportConnectors>
<amq:transportConnector name="bruce.broker1" uri="tcp://localhost:61616" discoveryUri="multicast://default"/>
</amq:transportConnectors>

</amq:broker>
</beans>


JBoss 5.1 AS specific stuff:

I just copied over the above created .rar dir to the jboss-5.1.0.GA/server/default/deploy/activemq-ra.rar/META-INF directory and started it.

Got one error trying:



18:00:48,611 ERROR [AbstractKernelController] Error installing to Parse:
name=vfsfile:/C:/jboss-5.1.0.GA/server/default/deploy/activemq-ra.rar/
state=Not Installed mode=Manual requiredState=Parse
org.jboss.deployers.spi.DeploymentException: Error creating managed object
for vfsfile:/C:/jboss-5.1.0.GA/server/default/deploy/activemq-ra.rar/
at org.jboss.deployers.spi.DeploymentException.rethrowAsDeploymentException(
DeploymentException.java:49)
...
Caused by: org.jboss.xb.binding.JBossXBException: Failed to parse source:
cvc-complex-type.2.4.d: Invalid content was found starting with element
'config-property-value'. No child element is expected at this point. @
vfsfile:/C:/jboss-5.1.0.GA/server/default/deploy/
activemq-ra.rar/META-INF/ra.xml[100,36]


Ah for some reason I had an empty in ra.xml. Removing that made the 5.1 server start fine and the producer produce and the consumer consume!

PS: the example consumer & producer now send out 2000 messages instead of the 10 you see in the Integrating Apache ActiveMQ with JBoss post.

Hope this might help somebody some time :)

Sunday, April 11, 2010

Best of this Week Summary 5 April - 11 April 2010

Sunday, October 18, 2009

Best of this Week Summary 12 October - 18 October 2009

  • Last week was Oracle Open World 2009. Beside for Oracle DBMS and SOA specialists, these days the event is definitely also very relevant to Java professionals because of Oracle's (almost official) acquisition of Sun this year. Here's a set of relevant information from OOW:

    • The keynote on sunday which also describes the plans Oracle has with Java, MySQL and Sun hardware

    • James Gosling explaining to the developer audience what Sun was doing, what the scale is of Java activity around the world and across technology platforms.

    • The Dutch software company Amis also did quite a few presentations at OOW. Some of those presentations were already online before they were actually presented! All were interesting:

      • Good overview of what will be in the Patch Set 1 for the Oracle Fusion Middleware 11g stack, including JSF and ADF (mobile!). It's more than a patch!

      • Edition Based Redefinition (EBR): "Every database object (well, almost every database object - not tables!) can have different implementations/incarnations/versions in various editions. The object versions are all in the same schema - they only differ in the Edition they are created in."
        So no more direct table access! No big bang needed anymore. Challenges: what if a mandatory column is removed or added from the current EBR view (answer: special triggers). Including 2 suggestions for best practices.

      • XML processing/design tips.

      • This "session introduces SOA and the new Oracle SOA Suite 11g to the realm of database professionals from which it sometimes seems so far removed. What are the key SOA concepts and objectives? What is at the heart of Oracle SOA Suite 11g: composite applications, BPEL PM, and the mediator. The session shows how SOA services can be leveraged from the database, from triggers, PL/SQL units, or even SQL and how the database can publish events to the event delivery network. It covers how the SOA infrastructure can access the database, primarily using Oracle Database and Oracle Advanced Queueing adapter and how database developers can help in doing so efficiently. It ends with hints for applying SOA concepts to "normal" database development."

        Bit oriented at the database professional, but still interesting for those who want to stay up to date with Oracle's DMBS possibilities. And a bit scary too: it is possible in 11g to make webservice calls and publish them from the database(!). For example from PL/SLQ and put a webservice in front of PL/SQL... I haven't come up with a practical situation where you'd want to do this from an architectural point of view: large risk to getting tightly coupled systems and dependencies. If you have heavily invested (i.e built) in PL/SQL, it might be a valid option though.


  • There is currently a renewed focus on the role of code generation in developing enterprise Java applications. Springsource recently released Roo, Skyway Software released Skyway Builder Community Edition version 6.3 and Blu Age released M2Spring. What should the software architects and developers look for in a Code Generation framework?

  • Seven Wicket Do's and Don'ts.

Sunday, March 1, 2009

Best of this Week Summary 17 February - 01 March 2009

  • A relatively new type of mashups are so called clipping mashups. Instead of building a mashup against some official API, a clipping mashup just parses the final content in the presentation, thus including CSS, Javascript etc. Links can be rewritten, new elements injected. The presentation doesn't have to follow for example portal JSR-168 standards.
    An example use case would be that you could use it to (temporarily) fix a bug in a system (not necessarily owned by you) until the official next release comes out.

  • Eight architectural styles described. Handy overview including which one could be when appropriate.

  • Summary of migration project from JDBC to Hibernate. Valid comment at the bottom is that iBatis might be a better solution when you have to work from an existing schema.

  • Additional tips and articles for the Wicket in Action book. For example this article on how to do a partial Ajax repaint of newly created repeater items (e.g adding 1 row dynamically to a table or list with a "+" button). This in contrast to rendering all rows in a table when repainting the WebMarkupContainer as in the example described here.

  • Nice step-by-step introduction to asynchronous processing in Java 5 using Futures, ExecutorServices, CompletionService, Callback interfaces and ThreadPools. Here's part 2 where it's being used in combination with Javascript to improve the user experience.