pátek 28. října 2011

Using Xaml serialization to generate Design Time Data

Recently I have been working on a almost finished Silverlight project which was needed a big change in graphical interfaces, in other words I needed a designer to be able to change all the Pages and components in Blend.

The issue - well not real issues is that all the data was bounded to properties behind, and without the data, the desiner was not able to work change the UI.

Blend can help you in this case, thanks to it's ability to generate XAML data from an existing class. So you can just generate data from existing ViewModel. That is great, but the problem is that, the data generated by Blend is not always usable. When you have an account and Blend will generate the name of the account: "Rhoncus vulputate" and the string specifing currency "Ipsum hac phasellus", you are thinking, maybe I will have to do some manual changes. Well with one account it is ok, but if you have a list of accounts (like 10) and list of operations (another 10). Editing all the things manulally might not be the right approach.

Well it happens so that in our project, we are using AutoPoco for the data generation. If you do not know it, it is a great library for generating simple POCO's - it seems like the development has been stoped last year, but hopefully this project will live on.

Well basically this means that I have already a lot of meaningful data generated in the form of POCO's, later stored in the DB. I have also working application which connects to Web Services and generates the ViewModels on the client side, so I asked myself:
Why should'nt I just take these ViewModels, serialize them into XAML and givem them to Blend as Design Time Data Sources?

So my goal was to have the same data which I was using in execution time during design time. And as you can see at the two following pictures I got it to work.
Run-time

Design-time

So I set myself to try to do so:

First idea - use the XamlSerializer. The problem - this class is only presented in WPF - not in Silverlight. OK - I thought I will just use my ViewModels in WPF application.

First Problem: The ViewModels were not ready to be used in WPF. Concretely I had two issues:
  • The INotifyDataErrorInfo interface does not exists (http://connect.microsoft.com/VisualStudio/feedback/details/568212/inotifydataerrorinfo-for-wpf)
  • The Web Service proxies generated using Silverlight are not the sames as the proxies generated using WPF
Possible solulution to these issues:
  • Try to make your ViewModel completely platform independent. Reuse a DTO's (Data Transfer Objects) on the server and client side (do not let VS to generate proxies for your DTO projects).
  • Use #if SILVERLIGHT directive to specify which parts should be build for WPF and which for Silverlight.
Well this turned out to be too complicated. Maybe it is possible on a project which you start from the very beginning. But I already had several ViewModels and changing all of these would take too much time.

Second idea - I will stay in Silverlight and see if I can get the XamlSerializer working in Silverlight

The build-int XamlSerializer is available only for WPF but there is an open-source implementation done by David Poll, which is part of his Silverlight and Beyond Library.
The use of the serializer is described in the following blog post.

This saved my life, thought I had to do some changes.
  • To determine which properties to serialize, the Serializer uses the description provided on this blog. So it will leave out all generic properties which are not read-only. In my case I had a lot of ObservableCollection properties which I wanted to serialize. Actually this was the reason to try this approach, because I did not want to edit these collections for Blend by hand. To solve this issue I had to modify the VisitProperty method of the Serializer in order to force him to serialize even the writable generic properties.
  • Disable serialization of certain properties. Blend designer has problems with some special cases of XAML. Concretely I had problem with the serialized WCF proxies. When you generate a proxy, there are at least two classes generated. Service interface (ends with "Service") and Service implementation (ends with "Client"). Blend was unable to process the xaml when he was expecting property of type "Service" and I have given him "Client" it seems that it has problems regarding services and implementations. To overcome this issue I have introduced XamlSerializationVisibility attribute which can have two values (Visible and Hidden). This parameter allows me to specify to the XamlSerializer whether to seralize or not the property. Again a slight edit of the VisitProperty method was neede to check this attribute before serialization.
  • And at last, the original WPF XamlSerializer does not serialize all basic types. In my case I had several Decimal values which I wanted to serialize and they were skipped out. This was solved quite easy. XamlSerializer contains a class BuiltInTypeConverter. This class contains a list of types which can be serialized by simple conversion to String (SupportedTypes. I have added to this collection the Decimal and DateTime and it just worked.
So thats it, grab the source code, try to serialize your ViewModels or whatever you need. Really a great thanks to David for the implementation of this class.

neděle 16. října 2011

Eclipse Indigo + Maven 3 + Tomcat debugging

I had a little hard time to see how to debug a Web project with Maven structure using Eclipse and build-in Tomcat debugging.

The problem is that Maven project has different structure thant Elicpses Dynamic Web Project, so finally eclipse does not know hot to package a WAR file and deploy it to the server.

Finally I found this blog, which explains the issue. This blog describes the situation when using Maven 2 and Eclipse Helios.

I use Maven 3 and Eclipse Indigo. When using Indigo, not all the steps described in the blog are needed (no changement of .project file needed). Basically you have to perform two steps.

1) Facets - change project nature. Go to the properties of the project and Project Facets and choose Dynamic Web Model. This step will change the structure of your project - add the WebContent folder.

2) Change the org.eclipse.wst.common.component file. This file is added when you have changed the project structure.

<?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="ProjectName">
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp"/>
<wb-resource deploy-path="/WEB-INF/lib" source-path="/target/ProjecName/WEB-INF/lib"/>
<property name="context-root" value="ProjectName"/>
<property name="java-output-path" value="/ProjectName/target/classes"/>

</wb-module>
</project-modules>

What we need is to copy the everything we need into the WebContent directory. There are parts which you can copy directly from your project (jsp files, web.xml) and than parts which you have to copy from the Maven build output (libs, classes).

If you are lost, take a look at the mentioned blog.

středa 12. října 2011

Catalina - Life cycle exception

Last project: Tomcat and Java Web applicaiton.
Suddenly after just after adding some dependencies using Maven, I was not able to start the server. The exception did not give me much detaisl:
Grave: ContainerBase.addChild: start: 
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/Bank]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)

Caused by: org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 60
at org.apache.tomcat.util.bcel.classfile.Constant.readConstant(Constant.java:131)
at org.apache.tomcat.util.bcel.classfile.ConstantPool.(ConstantPool.java:60)

oct. 07, 2011 2:26:19 PM org.apache.catalina.startup.HostConfig deployDirectory

Grave: Erreur lors du déploiement du répertoire Bank de l'application web
java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/Bank]]
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:816)

So after checking the state of web.xml (no changes there) I started to take a look at te dependencies which I have added. Well actually I took a look at the JARs which were being donwloaded automatically by Maven.
I found out that when I added a dependency to Jaxen library, than several JARs war downloaded.

I started deleting one by one and redeploying, just to find out, that it was ICU4J.JAR which was causing the problem. Well I was sure that I did not need it, so I solved the problem by declaring Maven exclusion.

<dependency>
<groupid>jaxen</groupId>
  <artifactid>jaxen</artifactId>
  <version>1.1.1</version>
  <exclusions>
   <exclusion>
 <groupid>com.ibm.icu</groupId>
    <artifactid>icu4j</artifactId>
   </exclusion>
  </exclusions>
  <scope>runtime</scope>
</dependency>

Couriously enough I needed Jaxen in order to be able to use the Azure4Java tools and access to Azure table storage.


Still I do not understand the real cause of this problem, so if anyone finds similar issue, I hope this helps.